nk.net!news-peer.sprintlink.net!news.sprintlink.net!Sprint!howland.erols.net!newshub2.home.com!newshub1.home.com!news.home.com!enews.sgi.com!news.corp.sgi.com!mew.corp.sgi.com!pablo
Subject: Sybase FAQ: 10/16 - section 9
Date: 1 Sep 1997 06:05:39 GMT
Summary: Info about SQL Server, bcp, isql and other goodies
Posting-Frequency: monthly

Archive-name: databases/sybase-faq/part10
URL: http://reality.sgi.com/pablo/Sybase_FAQ

                              Q9.1: SP_FREEDEVICE
                                       
   
     _________________________________________________________________
   

use master
go

drop proc sp_freedevice
go

create proc sp_freedevice
@devname        char(30) = null

as begin

  declare @showdev bit
  declare @alloc int
  if @devname = null
        select @devname = "%"
        , @showdev = 0
  else
        select @showdev = 1

  select @alloc = low
  from  master.dbo.spt_values
  where type = "E"
  and   number = 1

  create table #freedev
        (name   char(30),
         size   float,
         used   float)

  insert #freedev
        select dev.name,
                ((dev.high - dev.low) * @alloc + 500000) / 1048576,
                sum((usg.size * @alloc + 500000) / 1048576)
        from master.dbo.sysdevices dev, master.dbo.sysusages usg
        where dev.low <= usg.size + usg.vstart - 1
        and   dev.high >= usg.size + usg.vstart - 1
        and   dev.cntrltype = 0
        group by dev.name

  insert #freedev
        select name,
                ((high - low) * @alloc + 500000) / 1048576,
                0
        from    master.dbo.sysdevices
        where   cntrltype = 0
        and not exists (select * from #freedev
                        where name = master.dbo.sysdevices.name)

  if @showdev = 1 begin
    select
        devname = dev.name,
        size = convert(varchar(10),f.size) + " MB",
        used = convert(varchar(10),f.used) + " MB",
        free = convert(varchar(10),f.size - f.used) + " MB"
    from master.dbo.sysdevices dev, #freedev f
    where dev.name = f.name
    and   dev.name like @devname

    select
        dbase = db.name,
        size  = convert(varchar(10),((usg.size * @alloc)
        + 500000) / 1048576) + " MB",
        usage = vl.name
    from master.dbo.sysdatabases db,
        master.dbo.sysusages usg,
        master.dbo.sysdevices dev,
        master.dbo.spt_values vl
    where db.dbid = usg.dbid
    and   usg.segmap = vl.number
    and   dev.low <= usg.size + usg.vstart - 1
    and   dev.high >= usg.size + usg.vstart - 1
    and   dev.status & 2 = 2
    and   vl.type = "S"
    and   dev.name = @devname
  end
  else
    begin
        select total = convert(varchar(10), sum(size)) + " MB",
                used = convert(varchar(10), sum(used)) + " MB",
                free = convert(varchar(10), sum(size) - sum(used)) + " MB"
        from #freedev

        select devname = dev.name,
                size   = convert(varchar(10),f.size) + " MB",
                used   = convert(varchar(10),f.used) + " MB",
                free   = convert(varchar(10),f.size - f.used) + " MB"
        from    master.dbo.sysdevices dev, #freedev f
        where   dev.name = f.name
    end
  end
go

grant execute on sp_freedevice to public
go

   
     _________________________________________________________________

                                Q9.2: SP_WHODO
                                       
   
     _________________________________________________________________
   
Sybase System 10.x


use master
go

drop procedure sp_whodo
go

create procedure sp_whodo @loginame varchar(30) = NULL as

declare @low int
declare @high int
declare @spidlow int
declare @spidhigh int

select @low = 0, @high = 32767, @spidlow = 0, @spidhigh = 32767

if @loginame is not NULL
begin
        select @low = suser_id(@loginame), @high = suser_id(@loginame)
        if @low is NULL
        begin
                if @loginame like "[0-9]%"
                begin
                        select @spidlow = convert(int, @loginame),
                             @spidhigh = convert(int, @loginame),
                             @low = 0, @high = 32767
                end
                else
                begin
                        print "No login exists with the supplied name."
                        return (1)
                end
        end
end
select
   spid,
   status,
   substring(suser_name(suid),1,12)     loginame,
   hostname,
   convert(char(3),blocked)             blk,
   convert(char(7),isnull(time_blocked, 0)) blk_sec,
   convert(char(16),program_name)       program,
   convert(char(7),db_name(dbid))       dbname,
   convert(char(16),cmd)                cmd,
   convert(char(6),cpu)                 cpu,
   convert(char(7),physical_io)         io,
   convert(char(16),isnull(tran_name, ""))           tran_name
from master..sysprocesses
where suid >= @low and suid <= @high
and spid >= @spidlow and spid <= @spidhigh

return (0)
go

grant execute on sp_whodo to public
go

Sybase 4.x


use master
go

drop procedure sp_whodo
go

create procedure sp_whodo @loginame varchar(30) = NULL as

declare @low int
declare @high int
declare @spidlow int
declare @spidhigh int

select @low = 0, @high = 32767, @spidlow = 0, @spidhigh = 32767

if @loginame is not NULL
begin
        select @low = suser_id(@loginame), @high = suser_id(@loginame)
        if @low is NULL
        begin
                if @loginame like "[0-9]%"
                begin
                        select @spidlow = convert(int, @loginame),
                             @spidhigh = convert(int, @loginame),
                             @low = 0, @high = 32767
                end
                else
                begin
                        print "No login exists with the supplied name."
                        return (1)
                end
        end
end
select
   spid,
   status,
   substring(suser_name(suid),1,12)     loginame,
   hostname,
   convert(char(3),blocked)             blk,
   convert(char(16),program_name)       program,
   convert(char(7),db_name(dbid))       dbname,
   convert(char(16),cmd)                cmd,
   convert(char(6),cpu)                 cpu,
   convert(char(7),physical_io)         io
from master..sysprocesses
where suid >= @low and suid <= @high
and spid >= @spidlow and spid <= @spidhigh

return (0)
go

grant execute on sp_whodo to public
go

   
     _________________________________________________________________

                 Q9.3: GENERATING DUMP/LOAD DATABASE COMMAND.
                                       
   
     _________________________________________________________________
   

#!/bin/sh

#
# This script calls the function gen_dumpload_command to generate
# either a dump or a load command.
#
# This function works for both System 10 and Sybase 4.x
# installations.  You simply need to change your method of thinking.
# In Sybase 4.x, we only had a single stripe.  In System 10, most
# of the time we define a single stripe but in our bigger databases
# we define more stripes.
#
# Therefore, everything is a stripe.  Whether we use one stripe or
# many... cool?  Right on!
#
#
# The function gen_dumpload_command assumes that all dump devices
# adhere to the following naming convention:
#
#     stripe_NN_database
#
# NOTE:  If your shop is different search for "stripe" and replace
#        with your shop's value.
#
#


# gen_dumpload_command():
#
#        purpose:  to generate a dump/load to/from command based on
#                  what is defined in sysdevices.  The environment
#                  variable D_DEV is set.
#
#        return:   zero on success, non-zero on failure.
#
#        sets var: D_DEV is set with the actual dump/load command;
#                  stripe devices are also handled.
#
#        calls:    *none*
#
#        parms:    1 = DSQUERY
#                  2 = PASSWD
#                  3 = DB
#                  4 = CMD -> "dump" or "load"
#


gen_dumpload_command()
{
   LOCAL_DSQUERY=$1
   LOCAL_PASSWD=$2
   DB_TO_AFFECT=$3
   CMD=$4 # dump/load

   if [ "$CMD" = "dump" ] ; then
      VIA="to"
   else
      VIA="from"
   fi

   # Check for a dump device

   echo "Checking for standard $CMD device"
   D_DEV=`echo "$LOCAL_PASSWD
select name from sysdevices where name like \"stripe%_$DB_TO_AFFECT\"
go" | $SYBIN/isql -U sa -S $LOCAL_DSQUERY -w1000 | sed -n -e '/stripe/p' | \
      nawk '{ if (NR == 1) print "'$CMD' database '$DB_TO_AFFECT' '$VIA'", $0
                         else print "stripe on", $0
                         }'`

   if [ -z "$D_DEV" ] ; then # nothing defined... :(
      return 1
   fi

   return 0
}

SYBIN=$SYBASE/bin

gen_dumpload_command MAG_LOAD_2 thissux wcid "dump"

if [ $? -eq 1 ] ; then
   echo "Error..."
fi

# so what does this generate?  :-)
echo $D_DEV

# ... and it can be used as follows:

echo "$PASSWD
$D_DEV
go" | isql ....

exit 0

   
     _________________________________________________________________

                               Q9.4: SYBPERL FAQ
                                       
   _This is Michael Peppler's mpeppler@bix.com FAQ._
     _________________________________________________________________
   
  Index of Sections
     * Sybperl? What is that?
          + What is Sybperl?
          + Where can I get Sybperl?
          + Can I get Sybperl for Windows/NT?
          + Can I get Sybperl for OS foo?
          + Is there a version of Sybperl for Perl 5?
     * Building and Installing
          + Where is uperl.o?
          + make test doesn't work
          + How to get Dynamic Linking under HP-UX?
          + How do I set PERL_SRC?
          + I've moved the Sybase libraries, and Sybperl won't run...
          + Sybperl won't run as a CGI script
     * Sybperl 1.x vs Sybperl 2.x
          + Are Sybperl 1.x scripts compatible with 2.x?
          + Is Sybperl 2.x stable enough to use, or should I use 1.x for
            production code?
     * Documentation
          + Is there any good sybperl documentation?
          + Is there a sybperl FAQ?
          + Is there a sybperl mailing list?
     * Improving sybperl
          + Reporting bugs
          + Feature requests
            
   
     _________________________________________________________________
   
   
   
  Sybperl? What is that?
  
   
   
    What is Sybperl?
    
   Matthew Healy wrote this in a recent comp.databases.sybase post:
   
     Perl is an interpreted programming language discussed in
     _comp.lang.perl.*_ newsgroups; though it's interpreted, the
     interpreter has an internal compiler built-in, so medium-size
     programs still run quite efficiently.
     
     It has become very popular among people who database and/or system
     administration type work for several reasons:
     * Very powerful and flexible string-manipulation facilities;
       anything you can do with awk, grep, sed, etc. can be done in Perl
       -- and it even comes with utilities that convert scripts in those
       languages into Perl!
     * No arbitrary limits on the size of any object; you can slurp up an
       entire file into a string variable if you have enough _RAM._
     * An incredibly useful feature called associative arrays, which
       often gives you a quick and easy way of doing things that would
       otherwise require going back to your data structures textbooks.
     * Versions are available for nearly every platform you've ever heard
       of.
     * It's _free_!
       
     Sybperl is a package of extensions to Perl that basically add the
     Sybase db_library API calls to the Perl language; the combination is
     an extremely powerful scripting tool for Sybase DBA's and
     programmers because it adds the existing strengths of Perl to the
     Sybase API.
     
     So, when would you use it? Anytime the combination of shell scripts
     with isql is too limited but writing a C program is overkill.
     
     In particular, since Perl has become the language of choice for many
     WWW gurus, Sybperl has become the tool of choice for integrating
     Sybase databases with the Web. And there are now some toolkits
     written in Sybperl that make it even simpler; my favorite among
     those is WDB.
     
     Here are some URLs to check out:
     http://www.sybase.com/WWW/sybase_www_faq.html
     http://www.sybase.com/WWW/
     http://www.sybase.com/WWW/sybase_www_tools.html
     http://www.sybase.com/WWW/Sybperl/index.html
     http://arch-http.hq.eso.org/bfrasmus/wdb/wdb.html
     http://arch-http.hq.eso.org/bfrasmus/wdb/distribution/install.html
     ftp://ftp.demon.co.uk/pub/perl/db/perl4/sybperl/
     ftp://ftp.demon.co.uk/pub/perl/db/mod/Sybase/
     ftp://ftp.cis.ufl.edu/pub/perl/ ftp://ftp.perl.com/
     
   
   
    Where can I get Sybperl?
    
   Sybperl is available from CPAN (the Comprehensive Perl Archive
   Network)
   
   The CPAN master is at
   
     ftp://ftp.funet.fi/pub/languages/perl/CPAN
     
   and Sybperl is archived in
   
     ftp://ftp.funet.fi/pub/languages/perl/CPAN/authors/id/MEWP/sybperl-2
     .0.tar.gz
     
   CPAN is mirrored widely, please select the CPAN site nearest you to
   keep the networks happy. At the moment the registered CPAN sites are:
     * Africa
       
     ftp://ftp.is.co.za/programming/perl/CPAN/
     * Australasia
       
     ftp://coombs.anu.edu.au/pub/perl/
     ftp://ftp.mame.mu.oz.au/pub/perl/CPAN/
     * Europe
       
     ftp://ftp.funet.fi/pub/languages/perl/CPAN/
     ftp://ftp.sunet.se/pub/lang/perl/CPAN/
     ftp://ftp.cs.ruu.nl/pub/PERL/CPAN/
     ftp://ftp.demon.co.uk/pub/mirrors/perl/CPAN/
     ftp://ftp.pasteur.fr/pub/computing/unix/perl/CPAN/
     ftp://ftp.rz.ruhr-uni-bochum.de/pub/programming/languages/perl/CPAN
     / ftp://ftp.switch.ch/mirror/CPAN/
     ftp://orpheu.ci.uminho.pt/pub/lang/perl/
     * North America
       
     ftp://ftp.cis.ufl.edu/pub/perl/CPAN/
     ftp://uiarchive.cso.uiuc.edu/pub/lang/perl/CPAN/
     ftp://ftp.delphi.com/pub/mirrors/packages/perl/CPAN/
     ftp://ftp.sedl.org/pub/mirrors/CPAN/
     ftp://ftp.sterling.com/programming/languages/perl/
     
   _Version 2.x_ is also available on Sybase's Web page:
   
     http://www.sybase.com/WWW/Sybperl/index.html
     
   I try to make sure that the Sybase Web page is up to date, but I don't
   control it... 
   
    Can I get Sybperl for Windows/NT?
    
   Perl is available for Windows/NT from
   
     ftp://ntperl.hip.com
     
   However, certain key components that are used to add modules to Perl
   are missing in the current version, which makes things difficult for
   us Sybperl users.
   
   However, I _know_ that it is possible to get Sybperl to work under
   Windows/NT (I would venture to guess that most of the work is in
   creating appropriate Makefiles).
   
   Contact me if you are interested in attempting this. 
   
    Can I get Sybperl for OS foo?
    
   Perl is primarily a Unix tool, and Sybperl was developped under SunOS.
   However, Perl has been ported to numerous other OSes (MS-DOS,
   Windows/NT, OS/2, VMS), and Sybperl should theortically be portable to
   these OSes as well, in particular with Perl 5's better extension
   mechanism.
   
   I am always ready to provide any help I can to anyone wishing to port
   Sybperl to any particular OS/platform. 
   
    Is there a version of Sybperl for Perl 5?
    
   Yes. _Sybperl 2.x_ works only with _Perl 5_.
   
   _Sybperl 1.x_ does _not_ work with _Perl 5_, as the Perl extension
   mechanism was changed with _Perl 5_.
     _________________________________________________________________
   
   
   
  Building and Installing
  
   
   
    Where is uperl.o (1.x)?
    
   _uperl.o_ is the object file that _sybperl 1.x_ needs to be linked
   with to give it access to all the Perl routines. _uperl.o_ is normally
   created when you build _perl 4.036_, but if you have run make clean
   since building, you can recreate it by running _make uperl.o_.
   
   If you have _Perl 5.x_ you need _sybperl 2.x_ 
   
    make test doesn't work
    
   
    1. The 'interfaces' file is not visible.
       
       The Sybase libraries need access to a file that contain
       information on how to connect to the server. This file is normally
       located at the root of the Sybase installation directory, and the
       Sybase libraries and programs normally use the SYBASE environement
       variable to find this directory (ie sybperl will try to find
       $SYBASE/interfaces in order to run).
    2. The userid/password combination is invalid.
       
       Edit the PWD file to add a valid userid/password combination for
       accessing your database server.
    3. The $SYBASE environment variable is incorrectly set. The
       
       Sybase::CTlib modules needs access to its _locales_ information in
       the $SYBASE/locales directory.
       
   
   
    How to get Dynamic Linking under HP-UX?
    
   
   
   The bundled C compiler that comes with HP-UX apparently can't produce
   position independant code, which is needed to build a dynamically
   loadable library under HP-UX. The solution there is to use the add-on
   ANSI-C compiler or GCC.
   
   In addition, you can't build a dynamically loadable module of
   DBlibrary v. 4.x, because it is a statically linked library, and was
   not compiled using the position independent code flag.
   
   So the end result is: to get a dynamically loadable version you need
   the Sybase System 10 OpenClient libraries, and a C compiler that is
   capable of producing position independent code. 
   
    How do I set PERL_SRC?
    
   
   
   This problem sometimes appears when building sybperl with a copy of
   Perl that has not been installed (ie from the source tree):
   
   You've run:
   
     %perl Makefile.PL
     %make
     
   and the output looked something like this:
   
     % make
     Rebuilding CTlib/Makefile ...
     /home/mpeppler/PERL/perl5.001/miniperl
     -I//home/mpeppler/PERL/perl5.001/lib \
     -e "use ExtUtils::MakeMaker; MM->runsubdirpl(qw(CTlib))" \
     INST_LIB=/home/mpeppler/PERL/perl5.001/lib
     INST_ARCHLIB=/home/mpeppler/PERL/perl5.001/lib \
     INST_EXE=./blib LINKTYPE=dynamic LIBPERL_A=libperl.a
     Unable to locate Perl source. Try setting PERL_SRC in Makefile.PL or
     on command line.
     make: *** [CTlib/Makefile] Error 2 %
     
   
   
   To do this, you need to add a parameter to the WriteMakefile() call in
   each of the Makefile.PLs (toplevel, DBlib/Makefile.PL,
   CTlib/Makefile.PL and Sybperl/Makefile.PL). The parameter should look
   like this:
   
     WriteMakefile(DISTNAME => "sybperl",
     .... other parameters,
     PERL_SRC => "/home/mpeppler/src/perl5.001",
     ...);
     
   obviously replacing "/home/mpeppler/src/perl5.001" with the
   appropriate directory on your system. 
   
    I've moved the Sybase libraries, and Sybperl won't run...
    
    The sybperl make process hard-codes the path to the Sybase shared
   libraries (libsybdb.so and friends) into the binaries (either the
   dynamically loadable modules, or the Perl executable). This is done so
   that Perl can find the libraries it needs even if the LD_LIBRARY_PATH
   environment variable which is normally used to specify special library
   directories is not set (as when running a script from cron, for
   example).
   
   This technique obviously fails when the paths to the Sybase libraries
   are changed (through an upgrade, or when moving the binaries to
   another machine with a different configuration).
   
   The solution is to:
     * Set LD_LIBRARY_PATH to include the Sybase library directories _or_
     * Link the Sybase libraries to a standard directory (such as
       /usr/lib or /usr/local/lib) which will be searched by default.
       
   
   
    Sybperl won't run as a CGI script
    
   
   
   Typical symptoms: your script runs from the command line, but fails
   when run as a CGI script.
   
   The problem is (probably) that you normally have LD_LIBRARY_PATH set
   when running Sybperl scripts, but the HTTP deamon does not normally
   set this environment variable when executing child processes. It is
   not possible to set the LD_LIBRARY_PATH environment variable in the
   script itself (the variable needs to be set before the execution
   starts), so you may need to write a small shell wrapper that sets this
   environment variable and then execs your script.
   
   Alternatively, link the Sybase shared libraries to one of the _default_
   directories (such as /usr/lib).
     _________________________________________________________________
   
   
   
  Sybperl 1.x vs Sybperl 2.x
  
   
   
   
   
    Are sybperl 1.x scripts compatible with 2.x?
    
   
   
   Yes. With these exceptions:
   
     @var means different things to Perl and to Transact-SQL. If you use
     @var as SQL code (typically: "declare @var int exec my_proc @var
     out") you need to escape the @ (as in \@var).
     
   
   
   If you were in the habit of making calls to the _sybperl 1.x_ subs
   without parens (ie &dbnextrow; instead of &dbnextrow()) then there are
   certain situations where the dbnextrow implementation in sybperl 2.x
   may try to use an invalid DBPROCESS pointer. This problem does not
   exist if your scripts always pass explicit DBPROCESS parameters.
   
   Here at ITF I've linked /usr/local/bin/perl to /usr/local/bin/sybperl
   and all my old sybperl scripts work, provided that they had a "require
   'sybperl.pl';" at the top. 
   
    Is sybperl 2.x stable enough to use, or should I use 1.x for production
    code?
    
   
   
   _Sybperl 2.x_ is composed of three modules: Sybase::DBlib,
   Sybase::CTlib and Sybase::Sybperl. The DBlib and Sybperl modules are
   stable, and I recommend their use in production code (as I've been
   doing here for over a year now).
   
   The Sybase::CTlib module is in _beta_, and the API may be slightly
   modified in the future. In addition to that, I've noticed a
   performance problem with the CTlib module which appears to make it
   quite a bit slower than the DBlib module for equivalent code. This is
   something that I still need to investigate.
   
   The advantage (if one can call it that) of staying with _sybperl 1.x_
   is that the code is frozen - both for _sybperl_ itself and for _perl_.
   This means that any bugs that you code around will not come back to
   bite you as you upgrade from one release to the other.
     _________________________________________________________________
   
   
   
  Documentation
  
   
   
    Is there any good sybperl documentation?
    
   
   
   There is a Sybperl man page (in the current release it comes out to 13
   PostScript pages), but that is definitely _not_ sufficient in itself.
   
   You need _Perl_ documentation (there are over 370 pages of
   documentation in the standard Perl 5 release...). There are several
   good Perl books (in particular 'Programming Perl', Larry Wall & Randal
   Schwartz, O'Reilly and 'Learning Perl', Randal Schwartz, O'Reilly)
   
   And you need _Sybase_ documentation, in particular the Sybase
   OpenClient/C manual (I've got mine online via the Sybooks CD-ROM) and
   the Sybase Transact-SQL manual. 
   
    Is there a sybperl FAQ?
    
   Yes - you're reading it :-)
   
   
   But, more importantly, you should get the Perl FAQ, which is posted
   monthly in comp.lang.perl.announce, and which can be ftp'd from
   
     ftp://ftp.cis.ufl.edu/pub/perl/doc/FAQ
     
   And you need the Sybase FAQ too.
   
     http://reality.sgi.com/employees/pablo_corp/Sybase_FAQ
     
   
   
    Is there a sybperl mailing list?
    
   
   
   Yes. To subscribe to the mailing list, send a message to
   sybperl-l-REQUEST@trln.lib.unc.edu with a message body of:
   
     subscribe _Your Name_
     
     substituting your first and last name for _Your Name_.
     
   
     _________________________________________________________________
   
   
   
  Improving sybperl
  
   
   
    Reporting bugs
    
   
   
   I have a simple bug tracking database here at ITF. You can submit bugs
   for sybperl either to myself, or directly into the bug database by
   sending an e-mail message to bugtrack@itf.ch with the following
   additional fields:
   
     bug-category: sybperl
     bug-priority: high [or medium, or low]
     bug-type: bug [or feature, or change (for behavior change) or other]
     bug-summary: One line description
     
   The remainder of the message can be used to describe the bug or
   feature request in detail, with examples, etc. 
   
    Feature requests
    
   
   
   If you have any suggestions regarding how _sybperl_ can be improved,
   please send them to me - I am always happy to try to add features :-)
   
     _________________________________________________________________

                               Q9.5: DBSCHEMA.PL
                                       
   
     _________________________________________________________________
   
   In order to use this script you must have Sybperl installed -- see
   Q9.4 for more information.
   

#! /usr/local/bin/sybperl
#
#       @(#)dbschema.pl 1.11    2/22/94
#
#
# dbschema.pl   A script to extract a database structure from
#               a Sybase database
#
# Written by:   Michael Peppler (mpeppler@itf.ch)
# Last Modified:  22 Feb 1994
#
# Usage:        dbschema.pl -d database -o script.name -t pattern -s server -v
#                   where   database is self-explanatory (default: master)
#                           script.name is the output file (default: script.isq
l)
#                           pattern is the pattern of object names (in sysobjec
ts)
#                           that we will look at (default: %), and server is
#                           the server to connect to (default, the value of $EN
V{DSQUERY}).
#
#                   -v turns on a verbose switch.
#
#    Changes:   11/18/93 - bpapp - Put in interactive SA password prompt
#               11/18/93 - bpapp - Get protection information for views and
#                                  stored procedures.
#               02/22/94 - mpeppler - Merge bpapp's changes with itf version
#
#------------------------------------------------------------------------------


require 'sybperl.pl';
require 'getopts.pl';
require 'ctime.pl';

@nul = ('not null','null');

select(STDOUT); $| = 1;         # make unbuffered

do Getopts('d:t:o:s:v');

$opt_d = 'master' unless $opt_d;
$opt_o = 'script.isql' unless $opt_o;
$opt_t = '%' unless $opt_t;
$opt_s = $ENV{DSQUERY} unless $opt_s;

open(SCRIPT, "> $opt_o") || die "Can't open $opt_o: $!\n";
open(LOG, "> $opt_o.log") || die "Can't open $opt_o.log: $!\n";

#
# Log us in to Sybase as 'sa' and prompt for admin password.
#
print "\nAdministrative account password: ";
system("stty -echo");
chop($sapw =

   ); system("stty echo"); $dbproc = &dblogin("sa", $sapw, $opt_s);
   &dbuse($dbproc, $opt_d); chop($date = &ctime(time)); print
   "dbschema.pl on Database $opt_d\n"; print LOG "Error log from
   dbschema.pl on Database $opt_d on $date\n\n"; print LOG "The following
   objects cannot be reliably created from the script in $opt_o. Please
   correct the script to remove any inconsistencies.\n\n"; print SCRIPT
   "/* This Isql script was generated by dbschema.pl on $date. ** The
   indexes need to be checked: column names & index names ** might be
   truncated! */\n"; print SCRIPT "\nuse $opt_d\ngo\n"; # Change to the
   appropriate database # first, Add the appropriate user data types: #
   print "Add user-defined data types..."; print SCRIPT "/* Add
   user-defined data types: */\n\n"; &dbcmd($dbproc, "select s.length,
   s.name, st.name,\n"); &dbcmd($dbproc, " object_name(s.tdefault),\n");
   &dbcmd($dbproc, " object_name(s.domain)\n"); &dbcmd($dbproc, "from
   $opt_d.dbo.systypes s, $opt_d.dbo.systypes st\n"); &dbcmd($dbproc,
   "where st.type = s.type\n"); &dbcmd($dbproc, "and s.usertype > 100 and
   st.usertype
     _________________________________________________________________

                               Q9.6: SYBTCL FAQ
                                       
   _This is Tom Poindexter tpoindex@nyx.net FAQ._
     _________________________________________________________________
   
  Index of Sections
     * Overview
     * The enabling language platform
     * Design and commands
     * Applications
     * Information Sources
     * Availability and Support
     * About the Author
       
   
     _________________________________________________________________
   
   
   
  Overview
  
   Sybtcl is an extension to Tcl (Tool Command Language) that allows Tcl
   programs to access Sybase databases. Sybtcl adds additional Tcl
   commands to login to a Sybase server, send SQL statements, retrieve
   result sets, execute stored procedures, etc. Sybtcl simplifies Sybase
   programming by creating a high level interface on top of DB-Library.
   Sybtcl can be used to program a wide variety of applications, from
   system administration procedures to end-user applications.
     _________________________________________________________________
   
   
   
  The enabling language platform
  
   Tool Command Language, often abbreviated "Tcl" and pronounced as
   "tickle", was created by Dr. John Ousterhout at the University of
   California-Berkeley. Tcl is an interpreted script language, similar to
   Unix shell, Awk, Perl, and others. Tcl was designed to be easily
   extended, where new commands are added to the base interpreter to
   provide additional functionality. Core Tcl commands contain all of the
   usual constructs provided by most programming languages: setting and
   accessing variables, file read/write, if-then-else, do-while, function
   calls. Tcl also contains many productivity enhancing commands: list
   manipulation, associative arrays, and regular expression processing.
   
   Tcl has several features that make it a highly productive language.
   First, the language is interpreted. Interpreters allow execution
   without a compile and link step. Code can be developed with immediate
   feedback. Second, Tcl has a single data type: string. While this might
   at first glance seem to a deficiency, it avoids problems of data
   conversion and memory management. (This feature doesn't preclude Tcl
   from performing arithmetic operations.) Last, Tcl has a consistent and
   simple syntax, much the same as the Unix shell. Every Tcl statement is
   a command name, followed by arguments.
   
   Dr. Ousterhout also developed a companion Tcl extension, called Tk. Tk
   provides simplified programming of X11 applications with a Motif look
   and feel. X11 applications can be programmed with 60%-80% less code
   than equivalent Xt, Motif, or Xview programs using C or C++.
   
   Dr. Ousterhout is continuing Tcl/Tk development at Sun Microsystems.
   Current projects include porting Tcl/Tk to MS-Windows and Macintosh
   environments, creating a GUI window builder, and a byte-code compiler
   for the language.
     _________________________________________________________________
   
   
   
  Design and commands
  
   Sybtcl was designed to fill the gap between pure applications
   development tools (e.g. Apt, Powerbuilder, et.al.) and database
   administration tools, often Unix shell scripts consisting of 'isql'
   and Awk pipelines. Sybtcl extends the Tcl language with specialized
   commands for Sybase access. Sybtcl consists of a set of C language
   functions that interface DB-Library calls to the Tcl language.
   
   Instead of a simple one-to-one interface to DB-Library, Sybtcl
   provides a high-level Sybase programming interface of its own. The
   following example is a complete Sybtcl program that illustrates the
   simplified interface. It relies on the Tcl interpreter, "tclsh", that
   has been extended with Sybtcl.

  #!/usr/local/bin/tclsh
  set hand [sybconnect "mysybid" "mysybpasswd"]
  sybuse $hand pubs2
  sybsql $hand "select au_lname, au_fname from authors order by au_lname"
  sybnext $hand {
    puts [format "%s, %s" @1 @2]
  }
  sybclose $hand
  exit

   In this example, a Sybase server connection is established
   ("sybconnect"), and the "pubs" sample database is accessed ("sybuse").
   An SQL statement is sent to the server ("sybsql"), and all rows
   returned are fetched and printed ("sybnext"). Finally, the connection
   is closed ("sybclose").
   
   The same program can be made to display its output in an X11 window,
   with a few changes. The Tcl/Tk windowing shell, "wish", also extended
   with Sybtcl is used.

  #!/usr/local/bin/wish
  listbox .sql_output
  button  .exit -text exit -command exit
  pack .sql_output .exit
  set hand [sybconnect "mysybid" "mysybpasswd"]
  sybuse $hand pubs2
  sybsql $hand "select au_lname, au_fname from authors order by au_lname"
  sybnext $hand {
    .sql_output insert end  [format "%s, %s" @1 @2]
  }
  sybclose $hand

   In addition to these commands, Sybtcl includes commands to access
   return column names and datatypes ("sybcols"), return values from
   stored procedures ("sybretval"), reading and writing of "text" or
   "image" columns ("sybreadtext", "sybwritetext"), canceling pending
   results ("sybcancel"), and polling asynchronous SQL execution
   ("sybpoll").
   
   Full access to Sybase server messages is also provided. Sybtcl
   maintains a Tcl array variable which contains server messages, output
   from stored procedures ("print"), DB-Library and OS error message.
     _________________________________________________________________
   
   
   
  Applications
  
   The Sybtcl distribution includes "Wisqlite", an X11 SQL command
   processor. Wisqlite provides a typical windowing style environment to
   enter and edit SQL statements, list results of the SQL execution in a
   scrollable listbox, save or print output. In addition, menu access to
   the Sybase data dictionary is provided, listing tables in a database,
   the column names and datatypes of a table, text of stored procedures
   and triggers.
   
   Other applications included in the Sybtcl distribution include:
     * a simple graphical performance monitor
     * a version of "sp_who", with periodic refresh
     * an enhanced version of Wisqlite, "UCO/Wisql", with point/click SQL
       generation
     * an HTML template generator and CGI processor for WWW-Sybase access
       
   Sybtcl users have reported a wide variety of applications written in
   Sybtcl, ranging from end user applications to database administration
   utilities.
     _________________________________________________________________
   
   
   
  Information Sources
  
   Tcl/Tk is described in detail in "Tcl and the Tk Toolkit" by Dr. John
   Ousterhout, Addison-Wesley Publishing 1994 ISBN: 0-201-63337-X .
   Another recent publication is "Practical Programming in Tcl and Tk" by
   Brent Welch, Prentice Hall 1995 ISBN 0-13-182007-9. A forthcoming book
   on Tcl extensions, including Sybtcl, is due for publication by
   O'Reilly and Associates in early 1996.
   
   A wealth of information on Tcl/Tk is available via Internet sources:
   
     news:comp.lang.tcl
     http://www.sunlabs.com/research/tcl/
     http://www.sco.com/Technology/tcl/Tcl.html
     http://web.cs.ualberta.ca/~wade/HyperTcl/
     ftp://ftp.smli.com/pub/tcl
     ftp://ftp.aud.alcatel.com/tcl/
     
   
     _________________________________________________________________
   
   
   
  Availability and Support
  
   Tcl/Tk and Sybtcl are both released in source code form under a "BSD"
   style license. Tcl/Tk and Sybtcl may be freely used for any purpose,
   as long as copyright credit is given to the respective owners. Tcl/Tk
   can be obtained from either anonymous FTP site listed above. Sybtcl is
   located on ftp.aud.alcatel.com, which serves as the Tcl archive site
   for Internet users. Other sources are the Fall/Winter 1995 release of
   Sun's Developer CD-ROM, and the "Tcl/Tk" CD-ROM title from Walnut
   Creek.
   
   Tcl/Tk and Sybtcl can be easily configured under most modern Unix
   systems including SunOS, Solaris, HP-UX, Irix, OSF/1, AIX, SCO, et.al.
   Sybtcl requires Sybase's DB-Library, from Sybase's Open Client bundle.
   
   
   Current versions are:
     * Tcl 7.4: released July 1, 1995
     * Tk 4.0: released July 1, 1995
     * Sybtcl 2.3: released October 9, 1995
       
   The Internet newsgroup comp.lang.tcl is the focal point for support.
   The group is regularly read by developers and users alike. Authors may
   also be reached via email. Sun has committed to keeping Tcl/Tk as
   freely available software.
     _________________________________________________________________
   
   
   
  About the Author
  
   Tom Poindexter is a consultant with expertise in Unix, relational
   databases, systems and application programming. He holds a B.S. degree
   from the University of Missouri, and an M.B.A. degree from Illinois
   State University. He can be reached at tpoindex@nyx.net
     _________________________________________________________________
Q9.7: Extended Stored Procedures

----------------------------------------------------------------------------
The following stored procedures were written by Ed Barlow sqltech@tiac.net
and can be fetched from the following site:

     http://www.tiac.net/users/sqltech

Here's a pseudo-man page of what you get:

                         Modified Sybase Procedures

             Command                     Description

         sp__help        Better sp_help

         sp__helpdb      Database Information

         sp__helpdevice  Break down database devices into a nice
                         report

         sp__helpgroup   List groups in database by access level

         sp__helpindex   Shows indexes by table

         sp__helpsegment Segment Information

         sp__helpuser    Lists users in current database by group
                         (include aliases)

         sp__lock        Lock information

         sp__who         sp_who that fits on a page

                              Audit Procedures

              Command                     Description

         sp__auditsecurity Security Audit On Server

         sp__auditdb       Audit Current Database For Potential
                           Problems

                       System Administrator Procedures

            Command                     Description

         sp__block      Blocking processes.

         sp__dbspace    Summary of current database space
                        information.

         sp__dumpdevice Listing of Dump devices

         sp__helpdbdev  Show how Databases use Devices

         sp__helplogin  Show logins and remote logins to server

         sp__helpmirror Shows mirror information, discover broken
                        mirrors

         sp__segment    Segment Information

         sp__server     Server summary report (very useful)

         sp__vdevno     Who's who in the device world

                               DBA Procedures

             Command                     Description

         sp__badindex    give information about bad indexes (nulls,
                         bad statistics...)

         sp__collist     list all columns in database

         sp__indexspace  Space used by indexes in database

         sp__noindex     list of tables without indexes.

         sp__helpcolumns show columns for given table

         sp__helpdefault list defaults (part of objectlist)

         sp__helpobject  list objects

         sp__helpproc    list procs (part of objectlist)

         sp__helprule    list rules (part of objectlist)

         sp__helptable   list tables (part of objectlist)

         sp__helptrigger list triggers (part of objectlist)

         sp__helpview    list views (part of objectlist)

         sp__trigger     Useful synopsis report of current database
                         trigger schema

                             Reverse Engineering

             Command                     Description

         sp__revalias     get alias script for current db

         sp__revdb        get db creation script for server

         sp__revdevice    get device creation script

         sp__revgroup     get group script for current db

         sp__revindex     get indexes script for current db

         sp__revlogin     get logins script for server

         sp__revmirror    get mirroring script for server

         sp__revuser      get user script for current db

                              Other Procedures

             Command                    Description

         sp__bcp         Create unix script to bcp in/out database

         sp__date        Who can remember all the date styles?

         sp__quickstats  Quick dump of server summary information

----------------------------------------------------------------------------

                Q9.9: SQL TO DETERMINE SPACE USED FOR AN INDEX
                                       
   
     _________________________________________________________________
   
   OK, here's _sp_spaceused_ reduced to bare essentials:
   

  set nocount on
  declare @objname varchar(30)
  select  @objname = "your table"

  select  index_name = i.name,
          i.segment,
          rowtotal = rowcnt(i.doampg),
          reserved = (reserved_pgs(i.id, i.doampg) +
                      reserved_pgs(i.id, i.ioampg)),
          data = data_pgs(i.id, i.doampg),
          index_size =  data_pgs(i.id, i.ioampg),
          unused = ((reserved_pgs(i.id, i.doampg) +
                     reserved_pgs(i.id, i.ioampg)) -
                    (data_pgs(i.id, i.doampg) + data_pgs(i.id,
                                                         i.ioampg)))
  into    #space
  from    sysindexes i
  where   i.id = object_id(@objname)

   You can analyse this in a number of ways:
    1. This query should tally with _sp_spaceused @objname_:
       

  select  'reserved KB' = sum(reserved) * 2,
          'Data KB' = sum(data) * 2,
          'Index KB' = sum(index_size) * 2,
          'Unused KB' = sum(unused) * 2
  from    #space

    2. This one reports space allocation by segment:
       

  select  'segment name' = s.name,
          'reserved KB' = sum(reserved) * 2,
          'Data KB' = sum(data) * 2,
          'Index KB' = sum(index_size) * 2,
          'Unused KB' = sum(unused) * 2
  from    #space t,
          syssegments s
  where   t.segment = s.segment
  group by s.name

    3. This one reports allocations by index:
       

  select  t.index_name,
          s.name,
          'reserved KB' = reserved * 2,
          'Data KB' = data * 2,
          'Index KB' = index_size * 2,
          'Unused KB' = unused * 2
  from    #space t,
          syssegments s
  where   t.segment = s.segment

   If you leave out the where clause in the initial _select into_, you
   can analyse across the whole database.
   
   Hope this points you in the right direction.
     _________________________________________________________________

                               Q9.10: _XSYBMON_
                                       
   
     _________________________________________________________________
   
   _xsybmon_ provides an X interface to Sybase's _sp_monitor_ output. It
   was written by David Joyner at NCSU.
   
   This software may be anonymously ftp'd from:
   
     ftp://ftp.acs.ncsu.edu/pub/sybase/tools/xsybmon-0.7a.tar.Z [56K]
     
   
     _________________________________________________________________

                                 Q9.11: SP_DOS
                                       
   
     _________________________________________________________________
   

/*>>>>>>>>>>>>>>>>>>>>>>>>>>> sp_dos <<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
IF OBJECT_ID('dbo.sp_dos') IS NOT NULL
    DROP PROCEDURE sp_dos
GO

CREATE PROCEDURE sp_dos
    @vcObjectName varchar(30) = NULL
AS
/***********************************************************************
* sp_dos - Display Object Scope
*       This procedure graphically displays the scope of a object in
*       the database.
*
* Copyright 1996, all rights reserved.
*
* Author:  David W. Pledger, Strategic Data Systems, Inc.
*
* Parameters
*       ----------------------------------------------------------------
*       Name            In/Out  Description
*       ----------------------------------------------------------------
*       @vcObjectName    In     Mandatory - The exact name of a single
*                               database object for which the call
*                               hierarchy is to be extracted.
*
* Selected Data
*       A sample report follows:
*       ----------------------------------------------------------------
*
*               SCOPE OF EFFECT FOR OBJECT:  ti_users
*       +------------------------------------------------------------------+
*       (T) ti_users (Trigger on table 'users')
*       |
*       +--(P) pUT_GetError
*       |  |
*       |  +--(U) ui_error
*       |
*       +--(U) BGRP
*       |
*       +--(U) user_information (See Triggers: tu_user_information)
*       |
*       +--(U) users (See Triggers: ti_users, tu_users, td_users)
*       |
*       +--(P) pUT_LUDVersion
*          |
*          +--(P) pUT_GetError
*          |  |
*          |  +--(U) ui_error
*          |
*          +--(U) BGRP_LUDVersion
*
*       <End of Sample>
*
* Return Values
*       ----------------------------------------------------------------
*       Value           Description
*       ----------------------------------------------------------------
*       < -99           Unexpected error - should never occur.
*
*       -99 to -1       Sybase **reserved** return status values.
*
*       0               Execution succeeded
*
*       1               Execution of this procedure failed.
*
*       > 1             Unexpected error - should never occur.
*
***********************************************************************/
BEGIN

    /*------------------- Local Declarations -------------------------*/
    DECLARE @iObjectID    int           /* System ID of object        */
    DECLARE @cObjectType  char(1)       /* System Object Type code    */
    DECLARE @vcName       varchar(30)   /* System Object name         */
    DECLARE @vcMsg        varchar(255)  /* Error Message if needed    */
    DECLARE @iInsTrigID   int           /* Insert Trigger ID          */
    DECLARE @iUpdTrigID   int           /* Update Trigger ID          */
    DECLARE @iDelTrigID   int           /* Delete Trigger ID          */
    DECLARE @vcErrMsg     varchar(255)  /* Error Message              */

    /* Local variables to facilitate descending the parent-child
    ** object hierarchy.
    */
    DECLARE @iCurrent     int           /* Current node in the tree   */
    DECLARE @iRoot        int           /* The root node in the tree  */
    DECLARE @iLevel       int           /* The current level          */

    /* Local variables that contain the fragments of the text to
    ** be displayed while descending the hierarchy.
    */
    DECLARE @iDotIndex    int           /* Index for locating periods */
    DECLARE @cConnector   char(3)       /* '+--'                      */
    DECLARE @cSibSpacer   char(3)       /* '|  '                      */
    DECLARE @cBar         char(1)       /* '|'                        */
    DECLARE @cSpacer      char(3)       /* '   '                      */
    DECLARE @cPrntStrng1  char(255)     /* The first string to print  */
    DECLARE @cPrntStrng2  char(255)     /* The second string to print */
    DECLARE @iLoop        int           /* Temp var used for loop     */
    DECLARE @vcDepends    varchar(255)  /* Dependency String          */
    DECLARE @iDependsItem int           /* Index to a string item     */

    /* Create a temporary table to handle the hierarchical
    ** decomposition of the task parent-child relationship.  The Stack
    ** table keeps track of where we are while the leaf table keeps
    ** track of the leaf tasks which need to be performed.
    */
    CREATE TABLE #Stack
        (iItem int,
         iLevel int)

    /*------------------- Validate Input Parameters --------------------*/
    /* Make sure the table is local to the current database. */
    IF (@vcObjectName LIKE "%.%.%") AND (SUBSTRING(@vcObjectName, 1,
            CHARINDEX(".", @vcObjectName) - 1) != DB_NAME())
        GOTO ErrorNotLocal

    /* Now check to see that the object is in sysobjects. */
    IF OBJECT_ID(@vcObjectName) IS NULL
        GOTO ErrorNotFound

    /* ---------------------- Initialization -------------------------*/

    /* Do print any rowcounts while this is in progress. */
    SET NOCOUNT ON

    /* Retrieve the object ID out of sysobjects */
    SELECT @iObjectID = O.id,
           @cObjectType = O.type
    FROM   sysobjects O
    WHERE  O.name = @vcObjectName

    /* Make sure a job exists. */
    IF NOT (@@rowcount = 1 and @@error = 0 and @iObjectID > 0)
        GOTO ErrorNotFound

    /* Initialize the print string pieces. */
    SELECT @cConnector  = "+--",
           @cSibSpacer  = "|..",
           @cBar        = "|",
           @cSpacer     = "...",
           @cPrntStrng1 = "",
           @cPrntStrng2 = ""

    /* Print a separator line. */
    PRINT " "
    PRINT "** Utility by David Pledger, Strategic Data Systems, Inc.  **"
    PRINT "**         PO Box 498, Springboro, OH  45066               **"
    PRINT " "
    PRINT "         SCOPE OF EFFECT FOR OBJECT:  %1!",@vcObjectName
    PRINT "+------------------------------------------------------------------+
"

    /* -------------------- Show the Hierarchy -----------------------*/
    /* Find the root task for this job.  The root task is the only task
    ** that has a parent task ID of null.
    */
    SELECT @iRoot = @iObjectID

    /* Since there is a root task, we can assign the first
    ** stack value and assign it a level of one.
    */
    SELECT @iCurrent = @iRoot,
           @iLevel = 1

    /* Prime the stack with the root level. */
    INSERT INTO #Stack values (@iCurrent, 1)

    /* As long as there are nodes which have not been visited
    ** within the tree, the level will be > 0.  Continue until all
    ** nodes are visited.  This outer loop descends the tree through
    ** the parent-child relationship of the nodes.
    */
    WHILE (@iLevel > 0)
    BEGIN

        /* Do any nodes exist at the current level?  If yes, process them.
        ** If no, then back out to the previous level.
        */
        IF EXISTS
            (SELECT *
             FROM   #Stack S
             WHERE  S.iLevel = @iLevel)
        BEGIN

            /* Get the smallest numbered node at the current level. */
            SELECT @iCurrent = min(S.iItem)
            FROM   #Stack S
            WHERE  S.iLevel = @iLevel

            /* Get the name and type of this node.  */
            SELECT @cObjectType = O.type,
                   @vcName = O.name,
                   @iInsTrigID = ISNULL(O.instrig, 0),
                   @iUpdTrigID = ISNULL(O.updtrig, 0),
                   @iDelTrigID = ISNULL(O.deltrig, 0)
            FROM   sysobjects O
            WHERE  O.id = @iCurrent

           /*
            *    *=================================================*    *
            *    * Print out data for this node.  (Consider        *    *
            *    * making this a separate procedure.)              *    *
            *    *=================================================*    *
            */

            /* Initialize the print strings to empty (different from NULL).
            ** @cPrntStrng1 is used to 'double space' the output and
            ** contains the necessary column connectors, but no data.
            ** @cPrntStrng2 contains the actual data at the end of the
            ** string.
            */
            SELECT @cPrntStrng1 = ""
            SELECT @cPrntStrng2 = ""

            /* Level 1 is the root node level.  All Jobs have a single
            ** root task.  All other tasks are subordinate to this task.
            ** No job may have more than one root task.
            */
            IF @iLevel = 1
            BEGIN
                /* Print data for the root node. */
                SELECT @cPrntStrng1 = "",
                       @cPrntStrng2 = "(" + @cObjectType + ") " + @vcName
            END
            ELSE /* Else part of (IF @iLevel = 1) */
            BEGIN

                /* Initialize loop variable to 2 since level one has
                ** already been processed for printing.
                */
                SELECT @iLoop = 2

                /* Look at the values on the stack at each level to
                ** determine which symbol should be inserted into the
                ** print string.
                */
                WHILE @iLoop <= @iLevel
                BEGIN

                   /* While the loop variable is less than the current
                   ** level, add the appropriate spacer to line up
                   ** the printed output.
                   */
                   IF @iLoop < @iLevel
                   BEGIN

                       /* Is there a sibling (another node which exists
                       ** at the same level) on the stack?  If so, use
                       ** one type of separator; otherwise, use another
                       ** type of separator.
                       */
                       IF EXISTS(SELECT * FROM #Stack WHERE iLevel = @iLoop)
                       BEGIN
                           SELECT @cPrntStrng1 = rtrim(@cPrntStrng1) +
                                   @cSibSpacer
                           SELECT @cPrntStrng2 = rtrim(@cPrntStrng2) +
                                   @cSibSpacer
                       END
                       ELSE
                       BEGIN
                           SELECT @cPrntStrng1 = rtrim(@cPrntStrng1) + @cSpacer
                           SELECT @cPrntStrng2 = rtrim(@cPrntStrng2) + @cSpacer
                       END
                   END
                   ELSE /* Else part of (IF @iLoop < @iLevel) */
                   BEGIN
                       SELECT @cPrntStrng1 = rtrim(@cPrntStrng1) + @cBar
                       SELECT @cPrntStrng2 = rtrim(@cPrntStrng2) +
                               @cConnector + "(" + @cObjectType + ") " +
                               @vcName
                   END

                   /* Increment the loop variable */
                   SELECT @iLoop = @iLoop + 1

                END /* While @iLoop <= @iLevel */
            END /* IF @iLevel = 1 */

            /* Spaces are inserted into the string to separate the levels
            ** into columns in the printed output.  Spaces, however, caused
            ** a number of problems when attempting to concatenate the
            ** two strings together.  To perform the concatenation, the
            ** function rtrim was used to remove the end of the string.
            ** This also removed the spaces we just added.  To aleviate
            ** this problem, we used a period (.) wherever there was
            ** supposed to be a space.  Now that we are ready to print
            ** the line of text, we need to substitute real spaces
            ** wherever there is a period in the string.  To do this,
            ** we simply look for periods and substitute spaces.  This
            ** has to be done in a loop since there is no mechanism to
            ** make this substitution in the whole string at once.
            */

            /* Find the first period. */
            SELECT @iDotIndex = charindex (".", @cPrntStrng1)

            /* If a period exists, substitute a space for it and then
            ** find the next period.
            */
            WHILE @iDotIndex > 0
            BEGIN
                /* Substitute the space */
                SELECT @cPrntStrng1 = stuff(@cPrntStrng1, @iDotIndex, 1, " ")

                /* Find the next. */
                SELECT @iDotIndex = charindex (".", @cPrntStrng1)
            END

            /* Do the same thing for the second print string. */
            SELECT @iDotIndex = charindex (".", @cPrntStrng2)
            WHILE @iDotIndex > 0
            BEGIN
                SELECT @cPrntStrng2 = stuff(@cPrntStrng2, @iDotIndex, 1, " ")
                SELECT @iDotIndex = charindex (".", @cPrntStrng2)
            END

            SELECT @vcDepends = NULL

            IF @iInsTrigID > 0
                SELECT @vcDepends = OBJECT_NAME(@iInsTrigID) + " (Insert)"

            IF @iUpdTrigID > 0
                IF @vcDepends IS NULL
                    SELECT @vcDepends = OBJECT_NAME(@iUpdTrigID) + " (Update)"
                ELSE
                    SELECT @vcDepends = @vcDepends + ", " +
                           OBJECT_NAME(@iUpdTrigID) + " (Update)"

            IF @iDelTrigID > 0
                IF @vcDepends IS NULL
                    SELECT @vcDepends = OBJECT_NAME(@iDelTrigID) + " (Delete)"
                ELSE
                    SELECT @vcDepends = @vcDepends + ", " +
                           OBJECT_NAME(@iDelTrigID) + " (Delete)"

             IF @vcDepends IS NOT NULL
                 IF @cObjectType = "T"
                     SELECT @cPrntStrng2 = @cPrntStrng2 +
                               " (Trigger on table '" + @vcDepends + "')"
                 ELSE
                     SELECT @cPrntStrng2 = @cPrntStrng2 +
                                       " (See Triggers: " + @vcDepends + ")"

            /* Remove trailing blanks from the first print string.  */
            SELECT @cPrntStrng1 = rtrim(@cPrntStrng1)
            SELECT @cPrntStrng2 = rtrim(@cPrntStrng2)

            /* Print the two strings. */
            PRINT @cPrntStrng1
            PRINT @cPrntStrng2

            /* Remove the current entry from the stack (Pop) */
            DELETE #Stack
            WHERE  #Stack.iLevel = @iLevel
            AND    #Stack.iItem = @iCurrent

            /* Add (push) to the stack all the children of the current
            ** node.
            */
            INSERT INTO #Stack
            SELECT D.depid,
                   @iLevel + 1
            FROM   sysdepends D
            WHERE  D.id = @iCurrent

            /* If any were added, then we must descend another level. */
            IF @@rowcount > 0
            BEGIN
                SELECT @iLevel = @iLevel + 1
            END

        END
        ELSE
        BEGIN
            /* We have reached a leaf node.  Move back to the previous
            ** level and see what else is left to process.
            */
            SELECT @iLevel = @iLevel - 1
        END

    END /* While (@iLevel > 0) */

    PRINT " "

    RETURN (0)

/*------------------------ Error Handling --------------------------*/
ErrorNotLocal:
    /* 17460, Table must be in the current database. */
    EXEC sp_getmessage 17460, @vcErrMsg OUT
    PRINT @vcErrMsg
    RETURN (1)

ErrorNotFound:
    /* 17461, Table is not in this database. */
    EXEC sp_getmessage 17461, @vcErrMsg OUT
    PRINT @vcErrMsg
    PRINT " "

    PRINT "Local object types and objecs are:"

    SELECT "Object Type" = type,
           "Object Name" = name
    FROM   sysobjects
    WHERE  type IN ("U","TR","P","V")
    ORDER BY type, name

    RETURN (1)

END
GO

grant execute on sp_dos to public
go

   
     _________________________________________________________________

   
     _________________________________________________________________
   
                           Q9.12: SQSH, RELEASE 1.4
                                       
   _Last Modified: Oct 16, 1996 at 21:24:52 EST_
     _________________________________________________________________
   
   
   
Sybase-FAQ Notice

   You are currently reading a special Sybase-FAQified version of my home
   page. I will attempt to keep it as up-to-date as possible, however
   there is a chance that it may lag somewhat behind my personal page
   (http://www.voicenet.com/~gray/sqsh.html). Also, this version has
   been stripped of changelog and status information in order to shorten
   it up a bit for the plain-text version of the FAQ.
   
What is SQSH?

   
   
   Sqsh (pronounced skwish) is short for SQshelL (pronounced s-q-shell),
   it is intended as a replacement for the venerable 'isql' program
   supplied by Sybase. It came about due to years of frustration of
   trying to do real work with a program that was never meant to perform
   real work.
   
   Sqsh is much more than a nice prompt (a la 'dsql', from David B.
   Joyner), it is intended to provide much of the functionality provided
   by a good shell, such as variables, redirection, pipes,
   back-grounding, job control, history, command completion, and dynamic
   configuration. Also, as a by-product of the design, it is remarkably
   easy to extend and add functionality.
   
   Sqsh was designed with portability in mind and has been successfully
   compiled on most major UNIX platforms supported by Sybase, such as
   HP-UX, AIX, IRIX, SunOS, Solaris, Dynix, OSF/1, DEC Unix, SCO, NeXT,
   and CP/M (just kidding). It has also been compiled on most free
   versions of UNIX, Linux, NetBSD, and FreeBSD, using the -DNO_DB flag
   (which turns off database support). It should build relatively easily
   on most POSIX and X/OPEN compliant systems.
   
                          JOIN THE SQSH MAILING LIST
                                       
   Thanks to the tireless efforts of Pablo Sanchez (our fearless
   maintainer of the Sybase FAQ), and the excellent people at SGI, the
   sqsh mailing list is now available for use! Keep track of the latest
   developments. Offer suggestions for future additions. Offer me money
   (grin).
   
   To subscribe, send mail to external-majordomo@postofc.corp.sgi.com
   With a _body_ of:

   subscribe sqsh-users [optional e-mail address]


   Or with a _body_ of just:

   help


   for a list of commands. Once subscribed you may send mail to the
   sqsh-users mailing list by addressing your mail to:

   sqsh-users@postofc.corp.sgi.com


                               WHERE TO GET SQSH
                                       
   
   
   Sqsh may be found on the following sites:
     * http://www.voicenet.com/~gray/sqsh-1.4.tar.gz 
     * ftp://poseidon.csci.unt.edu/pub/sqsh 
     * ftp://ftp.netcom.com/pub/he/heyjude/gray 
     * ftp://davox2.davox.com/pub/sqsh 
       
   
   
   Keep in mind that sometimes the different sites become out of sync, so
   at times the latest version may be be available at one of them.
   
   If you are wondering what the funny '.gz' extension is on the end of
   some of the files, I highly recommend that you grab a copy of
   ftp://prep.ai.mit.edu/pub/gnu/gzip-1.2.4.tar or you can get a regular
   UNIX compressed version http://www.voicenet.com/~gray/sqsh-1.4.tar.Z.
   
   I also try to keep around the previous release
   http://www.voicenet.com/~gray/sqsh-1.3.tar.gz, just in case I royally
   screw up the current release (which could happen).
   
   If you have trouble reaching any of the sites above, you can send me
   e-mail at gray@voicenet.com, I am typically pretty good about
   responding.
   
                                 SQSH FEATURES
                                       
   
   
Commands

   
   
   Sqsh provides all commands provided by isql (such as go, reset,
   etc.)-- which wasn't hard, there aren't many of them--along with a
   large base of extended commands. Typically all commands in sqsh are
   prefixed with a '\' to avoid collision with the TSQL syntax. For
   example:

   1> \help
   Available commands:
   \abort       \alias       \buf-append  \buf-copy    \buf-edit
   \buf-get     \buf-load    \buf-save    \buf-show    \connect
   \done        \echo        \exit        \go          \help
   \history     \jobs        \kill        \loop        \quit
   \read        \reconnect   \redraw      \reset       \set
   \shell       \show        \sleep       \unalias     \wait
   \warranty    emacs        vi
   Use '\help command' for more details


   
   
   However, for those of you that just can't stand the '\', all commands
   may be aliased to any other name that you wish via the '\alias'
   command (see Aliasing, below). 
   
Variables

   
   
   Variables are provided in sqsh, much in the same way they are used
   within a standard shell. They may be used for storing and retrieving
   information, both within a sqsh command as well as within a SQL batch.
   
   
   For example, lets say that you have a long table name that you don't
   like to type over and over again, you can use a variable in place of
   the table name:

   1> \set t="a_really_long_table_name"
   1> SELECT "Count" = COUNT(*) FROM $t
   2> go

   Count
   -----------
          1123
   (1 row affected)


   
   
   Variables may also be used anywhere within a sqsh command, such as:

   1> \set g="go"
   1> SELECT "Count" = COUNT(*) FROM $t
   2> $g

   Count
   -----------
          1123
   (1 row affected)


   
   
   And, since virtually every aspect of sqsh is configurable through
   variables, the \set command may also be used to adjust the behavior of
   sqsh without having to exit and re-run with a different command line
   argument (like isql):

   1> \set colsep="|"
   1> SELECT id, COUNT(*) FROM syscolumns GROUP BY id
   2> go
   |id         |           |
   |-----------|-----------|
   |          1|         19|
   |          2|         23|
   ...


   
   
   This is the equivalent of exiting isql, and re-running it with the -c
   flag (which is also supported by sqsh). 
   
Redirection and Pipes

   
   
   How many times have you watched a result set disappear from your
   screen because you didn't hit ^S fast enough? Well, no more. Now, any
   command available in sqsh may be redirected to/from a file or
   pipelined to another process. For example, it is now legal to type:

   1> SELECT * FROM sysobjects
   2> go | grep test | more


   
   
   You may also redirect output to files and (if you are careful) can
   redirect input from files:

   1> select * from sysobjects
   2> go 2>/dev/null >/tmp/objects.txt


   
   
Aliasing

   
   
   As of release 1.2, sqsh supports full csh-style command aliasing.
   Aliasing provides a mechanism for supplying an alternate name for any
   given internal sqsh command, as well as a way of supplying additional
   argument to any given command. For example:

   1> \alias mo="\go !* | more"
   1> SELECT * FROM syspickles
   2> mo -h


   
   
   Is exactly the same as if you had typed:

   1> SELECT * FROM syspickles
   2> go -h | more


   
   
   The !* acts as a placeholder that indicates to sqsh that the
   parameters supplied to the alias should be inserted at this location.
   If the !* is not supplied, the parameters to the alias are appended on
   the end of the alias body.. 
   
Command Substitution

   
   
   With the 1.0 release, sqsh is slowly beginning to look more-and-more
   like a real shell with the addition of command substitution. This
   feature allows a UNIX command to substituted anywhere within a sqsh
   command or within a SQL batch simply by placing the command within
   backquotes (or ` -- this may not come out to be a backquote depending
   on which font your web browser is using). For example:

   1> SELECT COUNT(*) FROM `echo syscolumns`
   2> go | `echo more`


   
   
   Currently, sqsh allows a multi-line command within a SQL batch,
   however this is not support for command line functions as of yet. For
   example you can do:

   1> SELECT COUNT(*) FROM `echo
   2> syscolumns`
   3> go


   
   
   Whereas you _cannot_ do:

   1> SELECT COUNT(*) FROM syscolumns
   2> go | `echo
   more`


   
   
   Hopefully, in the near future I'll make sqsh smart enough to support
   line-continuations with sqsh commands. Believe it or not, it isn't
   that easy to do. 
   
Backgrounding And Job Control

   
   
   Suppose you want to run a long complex query and continue to work
   while waiting for the results. With isql, the most effective way to do
   this was to run two copies of isql. With sqsh you can now do:

   1> SELECT ... /* big nasty select */
   2> go &
   Job #1 started
   1>


   
   
   After typing 'go &', sqsh launches a child process, which reconnects
   to the database and performs the desired query. This is similar to job
   control within a standard shell except that, by default, in sqsh the
   background job's output will be deferred until the job completes. So
   when the big nasty query, above, completes you will see a message
   like:

   1> sp_helptext ....
   Job #1 completed (output pending)
   2>


   
   
   and to show the output of the job you can do:

   1> \show 1 | more


   
   
   Once again, the behavior of output deferral may be turned on and off
   via the $defer_bg variable.
   
   Sqsh also provides the commonly used job control commands available in
   such shells as csh and bash, such as \jobs (to display running jobs)
   and \kill (to terminate jobs). 
   
SQL Batch History

   
   
   Sqsh provides two methods for history control, line-by-line history
   using either vi or emacs styles (via
   ftp://prep.ai.mit.edu/pub/gnu/readline-2.0.tar.gz), it also provides
   batch history, so that entire statements may be re-run or edited:

   1> \history
   ...
   (12) SELECT name, id
          FROM syscolumns
         WHERE name LIKE "%$name%"
   (13) SELECT DISTINCT title, type
          FROM titles
         WHERE title IN
             (SELECT title
              FROM titles, titleauthor, authors
             WHERE titles.title_id = titleauthor.title_id
               AND authors.state = "CA")
     ..


   
   
   Most commands support a csh-style reference to history entries via
   '!!', or '!n'.

   1> \vi !!


   
   
Configurable Exit Status

   
   
   One of the major complaints most people have with isql is its
   inability to react to or report any sort of error condition generated
   within a SQL batch. Sqsh provides a somewhat complex but very flexible
   for configuring what is considered an error, which errors are to be
   displayed, and how to report them back to the operating system.
   
   Five internal variables are used to control sqsh's behavior to error
   conditions reported by SQL Server, $thresh_display, $thresh_fail,
   $thresh_failcount, $thresh_exit, and $exit_failcount all of which are
   configurable at run time as well as via command line flags. The
   following briefly outlines these variables and their relationship to
   each other:
     * _$thresh_display_
       This variable is used to determine at which severity level a SQL
       Server message is to be displayed. Setting this to 0 displays all
       message, and setting it to 22 suppresses all error messages.
     * _$thresh_fail_
       This variable is used by the error handler to determine which
       severity levels are considered by sqsh to be error conditions. The
       next variable will explain the importance of this value.
     * _$batch_failcount_
       Each time sqsh receives an message of a severity level that is
       considered an error (determined by $thresh_fail) this value is
       incremented to keep track of the total number of batches that have
       failed.
     * _$thresh_exit_
       This variable is used by sqsh to determine how many error
       conditions may be encountered before it will exit. In other words,
       when $batch_failcount is equal to $thresh_exit, sqsh will abort.
       Setting the variable to 0 disables this feature.
     * _$exit_failcount_
       If this variable is set to 1 (or On), then sqsh will exit with an
       operating system exit status equal to the total number of errors
       that have been encountered (the value of $batch_failcount).
       
   
   
Inter-Server BCP

   
   
   Using the \bcp command, sqsh supports the ability to transfer the
   result set from any command batch to another server (or even the same
   server) via the Sybase bcp protocol. This feature is particulary nice
   because current the standard Sybase bcp program does not support being
   able to transfer directly between server, or the ability to specify
   which rows from the source server are to be copied.

   1> SELECT customer_id, item, SUM(qty)
   2>   FROM orders
   3> GROUP BY customer_id, item
   4> \bcp -S SYB_DSS shipping.dbo.order_summary

   Starting...
   Batch successfully bulk-copied to SQL Server
   Batch successfully bulk-copied to SQL Server
   Batch successfully bulk-copied to SQL Server
   ...


   
   
   The \bcp command can deal with multiple result sets, and thus multiple
   commands in a batch or multiple results coming back from a single
   stored procedure (as long as the data types in all result sets are
   identical). 
   
Remote Procedure Calls

   
   
   With sqsh, it is possible to directly envoke a stored procedure
   without resorting to language calls (e.g. "EXEC proc_name ..."). This
   feature is of particular interest for controlling and Open Server that
   does not have language support built in. For example, to invoke the
   sp_who stored procedure, simply run:

   1> \rpc sp_who gray
   ...


   
   
   Sqsh also supports the ability to place the results of an OUTPUT
   parameter directly into a sqsh variable, for example, lets say we
   create a stored procedure that like so:

   1> CREATE PROCEDURE test_output
   2>     @x  int  OUTPUT
   3> AS
   4>     SELECT @x
   5>     SELECT @x=20
   6> go


   
   
   We may then invoke the _test_output_ procedure like this:

   1> \rpc test_output @x:my_x=10

   -----------
            10
   (0 rows affected)
   1> \echo $my_x
   20


   
   
   The \rpc command can be a little bit awkward and non-intuitive, so
   make sure you read the manual page closely before working with it. 
   
Semicolon "go"

   
   
   As of release 0.5, sqsh now supports a form of in-line go, via a ;
   placed anywhere within the current line, such as:

   1> sp_who ;


   
   
   And, anything that can follow the "go" command may also follow the
   inline ;

   1> sp_who ; | more


   
   
   Sqsh even attempts to be relatively smart, and ignores semicolons
   found within single or double quotes of a single command, although it
   currently does deal with semicolons located in comments. Note, in
   order to turn this feature on, execute:

   1> \set semicolon_hack=1


   
   
Simple Scripting

   
   
   Although sqsh does not have a full flow-of-control language (yet), it
   is possible to build simple self-executable scripts using the using #!
   notation, and sqsh's support for positional parameters. For example,
   to create a UNIX sp_who program, you simply need to create an
   executable file containing:

   #!/usr/local/bin/sqsh -i

   sp_who ${1}
   go


   
   
   The ${1} parameter to sp_who will expand to whatever argument is given
   when the script is run. Currently sqsh does not support more advanced
   positional paramters, such as $* or $@, like most shells. 
   
Multiple Display Styles

   
   
   Ever get tired of wading through isql's messy output when dealing with
   very wide result sets? Sqsh currently supports three separate display
   styles, _horizontal_ (standard isql style), _vertical_, and _bcp_,
   that are switchable at any time while running via the $style variable
   or by the -m flag to the \go command.
   
   With the vertical display style, all data is displayed as column/value
   pairs virtically down the left side. The style also nicely deals with
   performing word-wrapping on very wide text and varchar column outputs.

   1> SELECT * FROM my_table
   2> go -m vert

   int_col:     1
   varchar_col: You will notice that both varchar and text columns gracefully
                word-wrap and line up with the widest column name.
   float_col:   1.23
   text_col:    This text column would look really hideous on isql's output
                but fortunately sqsh make things look great with the vertical
                display style!

   int_col:     2
   varchar_col: Not much text here.
   float_col:   3.141592654
   text_col:

   (2 rows affected)


   
   
   And, if you want to simply generate a result set that is easily
   BCP'able into another server, the _bcp_ display style is for you. This
   style throws out all formatting and simply separates all columns by
   the value of the $colsep parameter (by default "|").

   1> SELECT * FROM my_other_table
   2> go -m bcp
   1|Scott|11/03/96 12:59:56|0|||
   1|Bob|11/19/96 12:59:56|7||32.5|


   
   
   This mode pretty much only makes sense when redirecting the output to
   a file (see Redirection and Pipes, above), 
   
Miscellaneous

   
   
   The following touches on a more of the less prominent features of
   sqsh. It is by no means a comprehensive list, for more details please
   refer to the manual page.
     * _Configurable Prompt Variable_
       The sqsh prompt is defined by the $prompt variable which is
       expanded prior to reading input from the user. Because sqsh keeps
       track of its current state in various variables, the prompt can be
       used to display such information as the current database, user,
       line number, etc.
     * _Named Buffers_
       In addition to the SQH History Buffer, sqsh also allows the
       current work buffer or any history buffer to be copied to and from
       named buffers for future use. Named buffers may also be edited and
       run.
     * _Reconnection_
       The SQL Server to which sqsh is connected may be dynamically
       changed without exiting using the \reconnect command.
     * _Configurable Keyword Completion_
       With GNU Readline support, sqsh adds keyword completion. By
       default sqsh will use its internal database of 237 TSQL keywords
       for tab-keyword completion. This completion can be configured to
       be performed in upper case, lower case, or auto-detect. If the
       file ~/.sqsh_keywords exists then the contents of this file is
       used in place of the internal keyword list.
     * _Session Locking_
       Using the \lock command, you may safely walk away from your
       current sqsh session without worrying about someone tampering with
       the database while you are away.
       
                           SQSH SUPPORTED PLATFORMS
                                       
   
   
   The following table outlines platforms that sqsh has successfully been
   compiled on. In theory each of these platforms should have been
   compiled painlessly, but in practice the odder operating systems trend
   to require a few tweaks. However, I am always working to make sqsh as
   easily portable as possible (not always an easy task).
   
   If you have any additional platforms that you would like to have added
   to this list, please send me e-mail, I always interested in hearing
   what people are doing with sqsh.

   Hardware             OS                  Compiler  Comments
   -------------------  ------------------- --------- ----------------
   Sun Sparc 1000       Solaris 2.4         gcc
   HP/9000 E35          HP-UX 10.x          gcc, cc
   HP/9000 755          HP-UX 9.01          ?         gcc -static
   SGI Indy             IRIX 5.x, 6.x       cc 3.19   See README.SGI
   NCR System 3000      SVR4                cc
   Sequent ?            Dynix/ptx 2.1.0     ?
   ?                    NeXT                ?
   150Mhz Pentium       SCO ?               ?
   DEC Alpha            OSF/1 ?             ?
   IBM RS/6000          AIX 3.2             gcc       -ltermcap, no -ltli
 * Sun IPX              SunOS 4.1.2         gcc
 * Sun Sparc 4c         SunOS 4.1.4         gcc
 * HP/300               NetBSD 1.1A         gcc
 * 486DX/50             Linux 1.3.45        gcc

 * Indicates that it has been compiled with -DNO_DB turned on, therefore
   the actual database access has not been tested, however 99% of Sqsh
   has nothing to do with database activity.


   
   
   And, for those of you that are interested in such things, sqsh is
   developed primarily on Linux 1.3.95 with the -DNO_DB flag on (I
   haven't managed to port DB-Lib to Linux yet), and tested on a Sun
   Sparc Server 1000 running Solaris 2.4.
   
                             SQSH LICENSING POLICY
                                       
   99% of the software that I use is free, therefore I like to give back
   in kind. Sqsh is held under the GNU General Public License (GPL) and
   therefore may be freely distributed under the terms of this license.
     _________________________________________________________________
   
   _Last Modified on Oct 16, 1996 at 21:24:52 EST by Scott C. Gray _

                               Q9.13: SP_GETDAYS
                                       
   
     _________________________________________________________________
   

use master
go

drop proc sp_getdays
go

create procedure getdays
 @days int OUTPUT,
 @date datetime=NULL
as
 declare @m int,
     @y int
 if (@date is NULL)
     select @date = getdate()
 select @m = datepart(mm, @date)
 if (@m = 2)
 begin
     select @y = datepart(yy, @date)
     if (@y % 4 = 0) and ((@y % 100 != 0) or (@y % 400 = 0))
         select @days = 29
     else
         select @days = 28
 end
 else
 begin
     if (@m > 7)
         select @m = @m - 7
     select @days = (30 + (@m & 1))
 end
 return (1)
go

grant execute on sp_getdays to public
go

   
     _________________________________________________________________
Q9.14: ddl_insert.pl

----------------------------------------------------------------------------
In order to use this script you must have Sybperl installed -- see Q9.4 for
more information.

#!/usr/local/bin/perl

# Author:  Vincent Yin  (umyin@mctrf.mb.ca)   Aug 1994    Last Modified: May 1996

chomp($basename = `basename $0`);

$usage = <<EOF;
USAGE
    $basename database userid passwd pattern [ pattern... ]

DESCRIPTION
    Prints isql scripts that would insert records into the
    tables whose names match any of the patterns in command line.  In
    other words, this program reverse engineers the data in a given
    table(s).  Roughly, it `select * from <table>', analyses the data
    and table structure, then prints out a bunch of
            insert <table> values ( ... )
    statements that would re-populate the table.  It's an alternative
    to `bcp'.  `bcp' has its limitations (e.g. one often needs to turn on
    'select into/bulk copy' option in the database before running bcp.)

    Table names are matched to <pattern> with Transact-SQL's LIKE clause.
    When more than one pattern is specified on command line, the LIKE
    clauses are OR'ed.  In any case, the LIKE clause(s) is logged to
    the beginning of the output as a comment, so that you'll see how this
    program interprets the command line.

    The SQL script is printed to stdout.  Since it only prints out the SQL
    but doesn't submit it to the SQL server, this procedure is safe to run.
    It doesn't modify database in any way.

EXAMPLES
    To print this usage page:
            % $basename
    To print SQL that populates the table master..sysobjects and systypes:
            % $basename master userid passwd 'sysobjects' 'systypes'
    To print SQL that populates all system tables in master db:
            % $basename master userid passwd 'sys%'

BUGS
    Embedded line breaks in strings are allowed in Sybase's isql, but not
    allowed in SQLAnywhere's isql.  So this script converts embedded line
    breaks (both DOS styled and UNIX styled) to blank characters.

EOF

$batchsize = 10;        # The number of INSERTs before a `go' is issued.
                        # This is to make the output compact.

# .................... No change needed below this line ........................

use Sybase::DBlib;

die $usage unless $#ARGV >= 3;
($db, $user, $passwd, @pattern) = @ARGV;

$likeclause = &sql_pattern_to_like_clause('name', @pattern);

print <<EOF;
-- This script is created by $0.
-- It would generate INSERT statements for tables whose names match the
-- following pattern:
/* $likeclause
*/

set nocount on
go
EOF

$dbh = new Sybase::DBlib $user, $passwd;
$dbh->{dbNullIsUndef} = 1;
$dbh->dbuse($db);

    # Get the list of tables.
$tablelist = $dbh->sql("select name from sysobjects
                   where type in ('S','U') and $likeclause
                   order by name
                  ");

foreach $tableref (@$tablelist) {
    $table = @$tableref[0];
    print "\n\n/*.............. $table ...............*/\n";
    print "-- ", `date`, "\n";
    print "declare \@d datetime\n";
    print "select \@d = getdate()\n";
    print "print        '       %1!    $table', \@d\ngo\n\n";
    print "truncate table $table  -- Lookout !!!!!!\ngo\n\n";

    $dbh->dbcmd("select * from $table");
    $dbh->dbsqlexec;
    $dbh->dbresults;

    while (@row = $dbh->dbnextrow()) {
        print "insert $table values(";
        for ($i=0; $i <= $#row; $i++) { # build the INSERT statement
                # Analyse datatype to decide if this column needs to be quoted.
            $coltype = $dbh->dbcoltype($i+1);
            if (!defined($row[$i])) {
                print 'NULL';    # Never quote NULL regardless of datatype
            }
            elsif ($coltype==35 or $coltype==39 or $coltype==47 or
                   $coltype==58 or $coltype==61 or $coltype==111 ){
                   # See systypes.type/name for explanation of $coltype.
                $row[$i] =~ s/\r|\n/ /g; # Handles both DOS and UNIX line breaks
                $row[$i] =~ s/"/""/g;    # Stuff double quotes
                print "\"" . $row[$i] . "\"";
            }
            else {
                print $row[$i];
            }
            print ", " unless $i == $#row;
        }
        print ")\n";            # wrap up the INSERT statement.
                                # print `go' at every $batchsize interval.
        print "go\n" unless $dbh->DBCURROW % $batchsize;
    }
    print "\ngo\n\n";           # print a `go' after the entire table is done.
    print "-- ### End for $table:  rowcount = ", $dbh->DBCURROW, "\n";
}

# ................................. sub ........................................
sub main'sql_pattern_to_like_clause {
    local($field_name, @pattern) = @_;
    $like_clause = "\t(  1 = 0 ";
    foreach (@pattern) {
        $like_clause .= "\n     or $field_name like '" . $_ . "' ";
    }
    $like_clause .= "\n\t) \n";
}

----------------------------------------------------------------------------

                          Q9.15: SP_DDL_CREATE_TABLE
                                       
   
     _________________________________________________________________
   

use master
go

drop proc sp_ddl_create_table
go

create proc sp_ddl_create_table
as

-- Creates the DDL for all the user tables in the
-- current database

select  right('create table ' + so1.name + '(' + '
', 255 * ( abs( sign(sc1.colid - 1) - 1 ) ) )+
        sc1.name + ' ' +
        st1.name + ' ' +
        substring( '(' + rtrim( convert( char, sc1.length ) ) + ') ', 1,
patindex('%char', st1.name ) * 10 ) +
        substring( '(' + rtrim( convert( char, sc1.prec ) ) + ', ' + rtrim(
convert( char, sc1.scale ) ) + ') ' , 1, patindex('numeric', st1.name ) *
10 ) +
        substring( 'NOT NULL', ( convert( int, convert( bit,( sc1.status &
8 ) ) ) * 4 ) + 1, 8 * abs(convert(bit, (sc1.status & 0x80)) - 1 ) ) +
        right('identity ', 9 * convert(bit, (sc1.status & 0x80)) ) +
        right(',', 5 * ( convert(int,sc2.colid) - convert(int,sc1.colid) ) ) +
        right(' )
' + 'go' + '
' + '
', 255 * abs( sign( ( convert(int,sc2.colid) - convert(int,sc1.colid) ) ) -
1 ) )
from    sysobjects so1,
        syscolumns sc1,
        syscolumns sc2,
        systypes st1
where so1.type = 'U'
and sc1.id = so1.id
and st1.usertype = sc1.usertype
and sc2.id = sc1.id
and sc2.colid = (select max(colid)
                from syscolumns
                where id = sc1.id)
order by so1.name, sc1.colid
go

grant execute on sp_ddl_create_table to public
go

   
     _________________________________________________________________

                                 Q9.16: INT.PL
                                       
   
     _________________________________________________________________
   
Background

   Please find included a copy of int.pl, the interfaces file conversion
   tool. It should work with perl 4 and 5, but some perl distributions
   don't seem to support gethostbyname which you need for the solaris,
   ncr, and vms file format.
   
   You may need to adjust the first line to the path of perl on your
   system, and may need to set the PERLLIB environment variable so that
   it finds the getopts.pl module.
   
   While it may not be 100% complete (e.g. it ignores the timeout field)
   you're free to add any functionality you may need at your site.

int.pl -h will print the usage, typical invocation is
        int.pl -f sun4-interfaces -o sol > interfaces.sol

   Note that I can't offer any kind of support, but I welcome comments,
   feedback, improvements, bug fixes, etc., just stadmin@sybase.com The
   usual disclaimers apply.
   
   Also, let me know whether it made your job easier, how often you use
   it, and how much time you save by using it.
   
Code


#!/usr/local/perl/bin/perl

# $Date: 1995/11/04 03:16:38 $ - $Author: mm $
# $Id: int.pl,v 1.4 1995/11/04 03:16:38 mm Exp mm $

# convert a sun4 interfaces file to a different format (see @modelist)
# limitations:
# - does not handle tli/spx entries (yet)
# - drivers for desktop platform hard coded
# - no sanity checks (duplicate names, incomplete entries)
# - ignores extraneous tokens silently (e.g. a 6th field)
# - don't know whether/how to convert decnet to tli format
# - ???

require "getopts.pl";

sub usage
{
        local(@token) = @_;
        if (!($token[0] eq "short" || $token[0] eq "long"))
        {
                printf STDERR "Environment variable(s) @token not defined.\n";
                exit (1);
        }
        print STDERR <<EOM;
Usage:  $progname  -f <sun4 interfaces file>
                -o { $modetext1 }
                [-V] [-v] [-h]
EOM
        if ($token[0] eq "long")
        {
                print STDERR <<EOM;
where
        -f <file> input file to process
        -o <mode> specify output mode
                  (e.g. $modetext2)
        -V        turn on verbose mode
        -v        print version string
        -h        print this message
EOM
        }
        else
        {
                print STDERR "For more details run $progname -h\n";
        }
        exit(1);
} # end of usage .


# FUNCTION NAME: parse_command_line
# DESCRIPTION: call getopts and assign command line arguments or default
#       values to global variables
# FORMAL PARAMETERS: none
# IMPLICIT INPUTS: command line arguments
# IMPLICIT OUTPUTS: $inputfile, $mode, $verbose
# RETURN VALUE: none, exits (in usage) if -h was specified (help option).
# SIDE EFFECTS: none
sub parse_command_line {
        &Getopts("f:o:hvV") || &usage("short");
        $inputfile = $opt_f;
        $mode = $opt_o;
        $verbose = $opt_V ? 1 : 0;

        print("$progname version is: $version\n"), exit 0 if $opt_v;
        &usage("long") if $opt_h;
        &usage("short") if ! $inputfile || ! $mode;
        &usage("short") if ! grep($mode eq $_, @modelist);
} # end of parse_command_line .

# FUNCTION NAME: process_file
# DESCRIPTION: parse file, try to convert it line by line.
# FORMAL PARAMETERS: $file - file to process
# IMPLICIT INPUTS: none
# IMPLICIT OUTPUTS: none
# RETURN VALUE: none
# SIDE EFFECTS: none
sub process_file {
        local($file) = @_;
        open(INPUT, "<$file") ||
                die "can't open file $file: $!\nExit.";
        local($line) = 0;
        local($type, $prot, $stuff, $host, $port, $tmp);
        print $os2_header if $mode eq "os2";
        while (<INPUT>)
        {
                $line++;
                # handle empty lines (actually lines with spaces and tabs only)
                #print("\n"), next if /^\s*$/;
                next if /^\s*$/;
                chop;
                # comments, strip leading spaces and tabs
                s/^\s*//, print("$_$lf{$mode}\n"), next if /^\s*#/;
                #s/^\s*//, next if /^\s*#/;

                # server names
                if (/^\w+/)
                {
                        if ($mode eq 'sol' || $mode eq 'ncr'
                                || $mode eq 'vms' || $mode eq 'nw386')
                        {
                                print "$_$lf{$mode}\n";
                                next;
                        }
                        elsif ($mode eq "os2")
                        {
                                $server = $_;
                                next;
                        }
                        else {
                                print "[$_]$lf{$mode}\n" if !(/SPX$/);
                                next;
                        }
                }

                if (/^\tmaster|^\tquery|\tconsole/)
                {
                        # descriptions
                        # parse first whitespace delimited word and
                        # following space(s)
                        # quietly ignore any extraerraneous characters
                        # I actually tried to catch them, but - believe
                        # it or not - perl would chop off the last digit of
                        # $port.                                   vvvv
                        # /^\t(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)(.+)$/;
                        if (!(($type, $prot, $stuff, $host, $port) =
                                /^\t(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/))
                        {
                                print STDERR "line $line: unknown format: $_";
                                next;
                        }
                        #print ("line $line: more than 5 tokens >$etc<, \n"),
                        #       next if $etc;
                        if (!($type eq "master" || $type eq "query"
                                || $type eq "console"))
                        {
                                # unknown type
                                print STDERR "line $line: unknown type $type\n"
;
                                next;
                        }
                        if ($prot eq 'tli')
                        {
                                #print STDERR "line $line: can't handle tli",
                                #       " entries (yet)\n";
                                # adjust to tli format
                                ($layer, $prot, $device, $entry) =
                                        ($prot, $stuff, $host, $port);
                                print "\t$type tli $prot $device ",
                                        "$entry$lf{$mode}\n" if $mode ne 'win3'
;
                                next;
                        }
                        if (!($prot eq "tcp" || $prot eq "decnet"))
                        {
                                # unknown protocol
                                print STDERR
                                        "line $line: unknown protocol $prot\n";
                                next;
                        }
                        if ($mode eq 'sol' || $mode eq 'ncr' || $mode eq 'nw386
')
                        {
                                $ip = &get_ip_address($host, 'hex');
                                $hexport = sprintf("%4.4x", $port);
                                print "\t$type tli $prot $device{$prot} \\x",
                                "$prefix{$mode}$hexport$ip$nulls{$mode}\n";
                                next;
                        }
                        if ($mode eq 'vms')
                        {
                                $ip = &get_ip_address($host, 'dot');
                                print "\t$type $prot $stuff $ip $port\n";
                                next;
                        }
                        if ($mode eq 'nt386')
                        {
                                $type =~ tr/a-z/A-Z/;
                                print "\t$type=$sock{$mode},$host,",
                                        "$port$lf{$mode}\n";
                                next;
                        }
                        if ($mode eq 'dos' || $mode eq 'win3')
                        {
                                next if $type ne "query";
                                print "\t${mode}_$type=$sock{$mode},",
                                        "$host,$port$lf{$mode}\n";
                                next;
                        }
                        if ($mode eq 'ntdoswin3')
                        {
                                ($tmp = $type) =~ tr/a-z/A-Z/;
                                # watch out for this local($mode) !!
                                # its scope is this BLOCK only and
                                # (within this block) overrides the
                                # other $mode!!! But we can still access
                                # the array %sock.
                                local($mode) = 'nt386';
                                print "\t$tmp=$sock{$mode},$host,$port",
                                        "$lf{$mode}\n";
                                next if $type ne "query";
                                $mode = 'dos';
                                print "\t${mode}_$type=$sock{$mode},",
                                        "$host,$port$lf{$mode}\n";
                                $mode = 'win3';
                                print "\t${mode}_$type=$sock{$mode},",
                                        "$host,$port$lf{$mode}\n";
                                next;
                        }
                        if ($mode eq 'os2')
                        {
                                print "  \"$server\" \"$type\" \"$sock{'os2'}",
                                        ",$host,$port\"\n";
                                next;
                        }
                }
                printf STDERR "line $line is ->%s<-\n", chop($_);
        }
        close(INPUT);
        print $os2_tail if $mode eq "os2";
        
} # end of process_file .

# FUNCTION NAME: print_array
# DESCRIPTION: print the array
# FORMAL PARAMETERS: *array - array to be printed, passed by reference
# IMPLICIT INPUTS: none
# IMPLICIT OUTPUTS: none
# RETURN VALUE: none
# SIDE EFFECTS: none
sub print_array {
        local(*array) = @_;
        foreach (sort keys %array)
        {
                printf STDERR "%-16s %s\n", $_, $array{$_};
        }
        
} # end of print_array .

# FUNCTION NAME: get_ip_address
# DESCRIPTION: get the ip address of a host specified by name, return it
#       as a string in the requested format, e.g.
#       requested format == 'dot' --> return 130.214.140.2
#       requested format == 'hex' --> return 82d68c02
#       In order to avoid repeated calls of gethostbyname with the same host,
#       store (formatted) results of gethostbyname in array %map.
# FORMAL PARAMETERS: name of host, requested return type: hex or dot format
# IMPLICIT INPUTS: %map
# IMPLICIT OUTPUTS: none
# RETURN VALUE: ip address
# SIDE EFFECTS: maintains %map, key is host name, value is ip address.
sub get_ip_address {
        local($host, $mode) = @_;
        if (!$map{$host})
        {
                #print "calling gethostbyname for $host";
                ($name, $aliases, $addrtype, $length, @addrs) =
                        gethostbyname($host);
                $map{$host} = join(".", unpack("C4", $addrs[0]));
                if ($mode eq 'hex')
                {
                        $map{$host} = sprintf("%2.2x%2.2x%2.2x%2.2x",
                                        split(/\./, $map{$host}));
                }
                #print " - $map{$host}\n";
        }
        return $map{$host};
} # end of get_ip_address .


$version = "\$Id: int.pl,v 1.4 1995/11/04 03:16:38 mm Exp mm \$";
$| = 1;
($progname = $0) =~ s#.*/##g;
@modelist = ('sol', 'ncr', 'vms', 'nw386', 'os2',
                 'nt386', 'win3', 'dos', 'ntdoswin3');
$modetext1 = join('|', @modelist);
$modetext2 = join(', ', @modelist);

# tli on solaris needs more zeroes
$nulls{'sol'} = "0000000000000000";
$nulls{'nw386'} = "0000000000000000";
$nulls{'ncr'} = "";
$nulls{'nt386'} = "";

# prefix for tli entries
$prefix{'sol'} = "0002";
$prefix{'nw386'} = "0200";
$prefix{'ncr'} = "0002";
$prefix{'nt386'} = "0200";

# protocol devices
$device{'tcp'} = '/dev/tcp';
$device{'spx'} = '/dev/nspx';
$device{'decnet'} = '/dev/tcp';

# socket driver names
$sock{'nt386'}="NLWNSCK";
$sock{'dos'}="NLFTPTCP";
$sock{'win3'}="WNLWNSCK";
$sock{'os2'}="nlibmtcp";

# linefeed's (^M) for the MS world
$lf{'nt386'}="";
$lf{'dos'}="";
$lf{'win3'}="";
$lf{'ntdoswin3'}="";
$lf{'os2'}="";
$lf{'vms'}="";
$lf{'sol'}="";
$lf{'ncr'}="";
$lf{'nw386'}="";

$os2_header = sprintf("STRINGTABLE\nBEGIN\n%s", "  \"\"\n" x 10);
$os2_tail = "END\n";

&parse_command_line;
&process_file($inputfile);
&print_array(*map) if $verbose;

   
     _________________________________________________________________

                 Q9.17: HOW TO ACCESS A SQL SERVER USING LINUX
                                       
   
     _________________________________________________________________
   
   Some time back, Sybase released a binary distribution of ctlib for
   Linux. This is just the header and libraries files for ctlib only, not
   dblib, not isql, not bcp, not the dataserver and not the OpenServer.
   This was done as a _skunk works_ internal project at Sybase, for the
   good of the Linux community, and not supported by Sybase in any
   official capacity. This version of ctlib identifies itself as 10.0.3.
   
   At the time, the binary format for Linux libraries was a format called
   _a.out_. Since then, the format has changed to the newer, _ELF_
   format. _ELF_ libraries and .o files cannot be linked with a.out
   libraries and .o files. Fortunately, a.out libraries and .o files can
   easily be converted to _ELF_ via the objdump(1) program.
   
   Getting a useable ctlib for Linux isn't that easy, though. Another
   compatibility problem has arisen since these old libraries were
   compiled. The byte-order for the ctype macros has changed. One can
   link to the (converted-to-ELF) ctlib, but running the resulting
   executable will result in an error message having to do with missing
   localization files. The problem is that the ctype macros in the
   compiled ctlib libraries are accessing a structure in the shared C
   library which has changed its byte order.
   
   I've converted the a.out library, as distributed by Sybase to ELF, and
   added the old tables directly to the library, so that it won't find
   the wrong ones in libc.
   
   Using this library, I can link and run programs on my Linux machines
   against Sybase databases (It also can run some programs against
   Microsoft SQL server, but that's another FAQ). However, you must be
   running Linux 2.0 or later, or else the link phase will core dump.
   
   This library is available for ftp at:
     * ftp://mudshark.sunquest.com/pub/ctlib-linux-elf/sybperl.tar.gz 
     *
       ftp://mudshark.sunquest.com/pub/ctlib-linux-elf/ctlib-linux-elf.tgz
       
       
   is a compiled version of sybperl 2.0, which is built with the above
   library. Obviously, only the ctlib module is in this distribution.
   
   In order to use this code, you will need a Sybase dataserver, a Sybase
   interfaces file (in the non-TLI format -- see Q9.16), a user named
   _sybase_ in your /etc/passwd file, whose home directory is the root of
   the distrubtion, and some application code to link to.
   
   As far as an isql replacement goes, use sqsh - Q9.12.
   
   One of the libraries in the usual Sybase distribution is a _libtcl.a_
   This conflicts with the library on Linux which implements the TCL
   scripting language, so this distribution names that library
   _libsybtcl.a_, which might cause some porting confusion.
   
     _The above conflict problem is addressed by SybPerl - Q9.4 and sqsh
     - Q9.12 _
     
   
     _________________________________________________________________
-- 
Pablo Sanchez              | Ph # (415) 933.3812        Fax # (415) 933.2821
pablo@sgi.com              | Pg # (800) 930.5635  -or-  pablo_p@pager.sgi.com
===============================================================================
I am accountable for my actions.   http://reality.sgi.com/pablo [ /Sybase_FAQ ]
