<--$Id: sybperl.pod,v 1.44 2003/12/31 16:47:07 mpeppler Exp $-->

=head1 NAME

sybperl - Sybase extensions to Perl

=head1 SYNOPSIS

   use Sybase::DBlib;
   use Sybase::CTlib;
   use Sybase::Sybperl;

=head1 DESCRIPTION

Sybperl implements three I<Sybase> extension modules to I<perl>
(version 5.002 or higher). I<Sybase::DBlib> adds a subset of the
I<Sybase DB-Library> API. I<Sybase::CTlib> adds a subset of the
I<Sybase CT-Library> (aka the Client Library) API. I<Sybase::Sybperl>
is a backwards compatibility module (implemented on top of
I<Sybase::DBlib>) to enable scripts written for I<sybperl 1.0xx> to
run with I<Perl 5>. 
Using both the I<Sybase::Sybperl> and I<Sybase::DBlib> modules
explicitly in a single script is not guaranteed to work correctly.

The general usage format for both I<Sybase::DBlib> and
I<Sybase::CTlib> is this:

    use Sybase::DBlib;

    # Allocate a new connection, usually referred to as a database handle
    $dbh = new Sybase::DBlib username, password;

    # Set an attribute for this dbh:
    $dbh->{UseDateTime} = TRUE;

    # Call a method with this dbh:
    $dbh->dbcmd(sql code);

The B<DBPROCESS> or B<CS_CONNECTION> that is opened with the call to
new() is automatically closed when the $dbh goes out of scope:

    sub run_a_query {
       my $dbh = new Sybase::CTlib $user, $passwd;
       my @dat = $dbh->ct_sql("select * from sysusers");

       return @dat;
    }
    # The $dbh is automatically closed when we exit the subroutine.

It should be noted that an important difference between CTlib and DBlib is
in how the SYBASE environment variable is handled.  DBlib only checks for
the SYBASE variable when it requires access to the interfaces file.  This
allows for definition of the SYBASE variable in your script.  CTlib requires
that the SYBASE variable be defined BEFORE initialization.  If the variable
is not defined then CTlib will not initialize properly and your script will
not run.


=head2 Attributes

The I<Sybase::DBlib> and I<Sybase::CTlib> modules make a use of
attributes that are either package global or associated with a
specific $dbh. These attributes control certain behavior aspects, and
are also used to store status information.

Package global attributes can be set using the B<%Att> hash table in
either modules. The B<%Att> variable is not exported, so it must be
fully qualified:

    $Sybase::DBlib::Att{UseDateTime} = TRUE;

B<NOTE:> setting an attribute via the B<%Att> variable
does B<NOT> change the status of currently allocated database handles.

In this version, the available attributes for a $dbh are set when the
$dbh is created. You can't add arbitrary attributes during the life of
the $dbh. This has been done to implement a stricter behavior and to
catch attribute errors.

It B<is> possible to add your own attributes to a $dbh at creation
time. The B<Sybase::BCP> module adds two attributes to the normal
I<Sybase::DBlib> attribute set by passing an additional attribute
variable to the I<Sybase::DBlib> new() call:

    $d = new Sybase::DBlib $user,$passwd,
                           $server,$appname, {Global => {}, Cols => {}};



=head2 DateTime, Money and Numeric data behavior

As of version 2.04, the Sybase B<DATETIME> and B<MONEY> datatypes
can be kept in their native formats in both the I<Sybase::DBlib>
and I<Sybase::CTlib> modules. In addition, B<NUMERIC> or B<DECIMAL>
values can also be kept in their native formats when using the
I<Sybase::CTlib> module. This behavior is normally turned B<off> by
default, because there is a performance penalty associated with it. It
is turned on by using package or database handle specific attributes.

Please see the discussion on
B<Special handling of DATETIME, MONEY & NUMERIC/DECIMAL values>
below for details.

=head2 Compatibility with Sybase Open Client documentation.

In general, I have tried to make the calls in this package behave the
same way as their C language equivalents. In certain cases the
parameters are different, and certain calls (dblogin() for example)
don't do the same thing in C as in Perl. This has been done to make
the life of the Perl programmer easier.

You should if possible have the Sybase Open Client documentation
available when writing Sybperl programs.


=head1 Sybase::DBlib

A generic I<perl> script using I<Sybase::DBlib> would look like this:

    use Sybase::DBlib;

    $dbh = new Sybase::DBlib 'sa', $pwd, $server, 'test_app';
    $dbh->dbcmd("select * from sysprocesses\n");
    $dbh->dbsqlexec;
    while($dbh->dbresults != NO_MORE_RESULTS) {
        while(@data = $dbh->dbnextrow)
       {
          .... do something with @data ....
       }
    }


The API calls that have been implemented use the same calling sequence
as their C equivalents, with a couple of exceptions, detailed below.

Please see also B<Common Sybase::DBlib and Sybase::CTlib routines> below.

B<List of API calls>

B<Standard Routines:>

=over 8

=item $dbh = new Sybase::DBlib [$user [, $pwd [, $server [, $appname [, {additional attributes}]]]]]

=item $dbh = Sybase::DBlib->dblogin([$user [, $pwd [, $server [, $appname, [{additional attributes}] ]]]])

Initiates a connection to a Sybase dataserver, using the supplied
user, password, server and application name information. Uses the
default values (see DBSETLUSER(), DBSETLPWD(), etc. in the Sybase
DB-Library documentation) if the parameters are omitted.

The two forms of the call behave identically.

This call can be used multiple times if connecting to multiple servers
with different username/password combinations is required, for
example.

The B<additional attributes> parameter allows you to define
application specific attributes that you wish to associate with the $dbh.

=item $dbh = Sybase::DBlib->dbopen([$server [, $appname, [{attributes}] ]])

Open an additional connection, using the current LOGINREC information.

=item $status = $dbh->dbuse($database)

Executes "use database $database" for the connection $dbh.

=item $status = $dbh->dbcmd($sql_cmd)

Appends the string $sql_cmd to the current command buffer of this connection.

=item $status = $dbh->dbsqlexec

Sends the content of the current command buffer to the dataserver for
execution. See the DB-Library documentation for a discussion of return
values.

=item $status = $dbh->dbresults

Retrieves result information from the dataserver after having executed
dbsqlexec().

=item $status = $dbh->dbsqlsend

Send the command batch to the server, but do not wait for the server to return
any results. Should be followed by calls to dbpoll() and dbsqlok(). See the 
Sybase docs for further details.

=item $status = $dbh->dbsqlok

Wait for results from the server and verify the correctness of the 
instructions the server is responding to. Mainly for use with dbmoretext() 
in Sybase::DBlib. See also the Sybase documentation for details.

=item ($dbproc, $reason) = Sybase::DBlib->dbpoll($millisecs)

=item ($dbproc, $reason) = $dbh->dbpoll($millisecs)

B<Note>: The dbpoll() syntax has been changed since sybperl 2.09_05.

Poll the server to see if any connection has results pending. Used in 
conjunction with dbsqlsend() and dbsqlok() to perform asynchronous queries.
dbpoll() will wait up to $millisecs milliseconds and poll any open DBPROCESS
for results (if called as Sybase::DBlib->dbpoll()) or poll the specified 
DBPROCESS (if called as $dbh->dbpoll()). If it finds a DBPROCESS that is
ready it returns it, along with the reason why it's ready. If dbpoll()
times out, or if an interrupt occurs, $dbproc will be undefined, and $reason 
will be either DBTIMEOUT or DBINTERRUPT. If $millisecs is 0 then dbpoll()
returns immediately. If $millisecs is -1 then it will not return until
either results are pending or a system interrupt has occurred. Please see
the Sybase documentation for further details.

Here is an example of using dbsqlsend(), dbpoll() and dbsqlok():

  $dbh->dbcmd("exec big_hairy_query_proc");
  $dbh->dbsqlsend;
  # here you can go do something else...
  # now - find out if some results are waiting
  ($dbh2, $reason) = $dbh->dbpoll(100);
  if($dbh2 && $reason == DBRESULT) {   # yes! - there's data on the pipe
     $dbh2->dbsqlok;
     while($dbh2->dbresults != NO_MORE_RESULTS) {
        while(@dat = $dbh2->dbnextrow) {
           ....
        }
     }
  }


=item $status = $dbh->dbcancel

Cancels the current command batch.

=item $status = $dbh->dbcanquery

Cancels the current query within the currently executing command batch.

=item $dbh->dbfreebuf

Free the command buffer (required only in special cases - if you don't
know what this is you probably don't need it :-)

=item $dbh->dbclose

Force the closing of a connection. Note that connections are
automatically closed when the $dbh goes out of scope.

=item $dbh->DBDEAD

Returns TRUE if the B<DBPROCESS> has been marked I<DEAD> by I<DB-Library>.

=item $status = $dbh->DBCURCMD

Returns the number of the currently executing command in the command
batch. The first command is number 1.

=item $status = $dbh->DBMORECMDS

Returns TRUE if there are additional commands to be executed in the
current command batch.

=item $status = $dbh->DBCMDROW

Returns SUCCEED if the current command can return rows.

=item $status = $dbh->DBROWS

Returns SUCCEED if the current command did return rows.

=item $status = $dbh->DBCOUNT

Returns the number of rows that the current command affected.

=item $row_num = $dbh->DBCURROW

Returns the number (counting from 1) of the currently retrieved row in
the current result set.

=item $spid = $dbh->dbspid

Returns the SPID (server process ID) of the current connection to the Sybase
server.

=item $status = $dbh->dbhasretstat

Did the last executed stored procedure return a status value?
dbhasretstats must only be called after dbresults returns
NO_MORE_RESULTS, i.e. after all the select, insert, update operations of
the stored procedure have been processed.

=item $status = $dbh->dbretstatus

Retrieve the return status of a stored procedure. As with
dbhasretstat, call this function after all the result sets of the
stored procedure have been processed.

=item $status = $dbh->dbnumcols

How many columns are in the current result set.

=item $status = $dbh->dbcoltype($colid)

What is the column type of column $colid in the current result
set. 

=item $type = $dbh->dbprtype($colid)

Returns the column type as a printable string.

=item $status = $dbh->dbcollen($colid)

What is the length (in bytes) of column $colid in the current result set.

=item $string = $dbh->dbcolname($colid)

What is the name of column $colid in the current result set.

=item @dat = $dbh->dbnextrow([$doAssoc [, $wantRef]])

Retrieve one row. dbnextrow() returns a list of scalars, one for each
column value. If $doAssoc is non-0, then dbnextrow() returns a hash (aka
associative array) with column name/value pairs. This relieves the
programmer from having to call dbbind() or dbdata(). 

If $wantRef is non-0, then dbnextrow() returns a B<reference> to
a hash or an array. This reference I<points> to a static array (or hash)
so if you wish to store the returned rows in an array, you must
B<copy> the array/hash:

  while($d = $dbh->dbnextrow(0, 1)) {
     push(@rows, [@$d]);
  }

The return value of the C version of dbnextrow() can be accessed via the 
Perl DBPROCESS attribute field, as in:

   @arr = $dbh->dbnextrow;		# read results
   if($dbh->{DBstatus} != REG_ROW) {
     take some appropriate action...
   }

When the results row is a COMPUTE row, the B<ComputeID> field of the
DBPROCESS is set:

   @arr = $dbh->dbnextrow;		# read results
   if($dbh->{ComputeID} != 0) {	# it's a 'compute by' row
     take some appropriate action...
   }

dbnextrow() can also return a hash keyed on the column name:

   $dbh->dbcmd("select Name=name, Id = id from test_table");
   $dbh->dbsqlexec; $dbh->dbresults;

   while(%arr = $dbh->dbnextrow(1)) {
      print "$arr{Name} : $arr{Id}\n";
   }


=item @dat = $dbh->dbretdata([$doAssoc])

Retrieve the value of the parameters marked as 'OUTPUT' in a stored
procedure. If $doAssoc is non-0, then retrieve the data as an
associative array with parameter name/value pairs.

=item $bylist = $dbh->dbbylist($computeID)

Returns the I<by list> for a I<compute by> clause. $bylist is a reference 
to an array of I<colids>. You can use $dbh->dbcolname() to get the column 
names.

    $dbh->dbcmd("select * from sysusers order by uid compute count(uid) by uid");
    $dbh->dbsqlexec;
    $dbh->dbresults;
    my @dat;
    while(@dat = $dbh->dbnextrow) {
        if($dbh->{ComputeID} != 0) {
            my $bylist = $dbh->dbbylist($dbh->{ComputeID});
            print "bylist = @$bylist\n";
        }
        print "@dat\n";
    }


=item %hash = $dbh->dbcomputeinfo($computeID, $column)

Returns a hash with the B<colid>, B<op>, B<len>, B<type> and B<utype>
of the I<compute by> column. You can call this subroutine to get the 
information returned by DB-Library's I<dbalt*()> calls. The $column is the 
column number in the current I<compute by> row (starting at 1) and
the $computeID is best retrieved from I<$dbh->{ComputeID}>. Please
see the documentation of the I<dbalt*()> calls in Sybase's DB-Library
manual.

=item $string = $dbh->dbstrcpy

Retrieve the contents of the command buffer.

=item $ret = $dbh->dbsetopt($opt [, $c_val [, $i_val]])

Sets option $opt with optional character parameter $c_val and optional
integer parameter $i_val. $opt is one of the option values defined in
the Sybase DB-Library manual (f.eg. DBSHOWPLAN, DBTEXTSIZE). For
example, to set SHOWPLAN on, you would use

    $dbh->dbsetopt(DBSHOWPLAN);

See also dbclropt() and dbisopt() below.

=item $ret = $dbh->dbclropt($opt [, $c_val])

Clears the option $opt, previously set using dbsetopt().

=item $ret = $dbh->dbisopt($opt [, $c_val])

Returns TRUE if the option $opt is set.

=item $string = $dbh->dbsafestr($string [,$quote_char])

Convert $string to a 'safer' version by inserting single or double
quotes where appropriate, so that it can be passed to the dataserver
without syntax errors. 

The second argument to dbsafestr() (normally B<DBSINGLE>, B<DBDOUBLE> or
B<DBBOTH>) has been replaced with a literal ' or " (meaning B<DBSINGLE> or
B<DBDOUBLE>, respectively). Omitting this argument means B<DBBOTH>.

=item $packet_size = $dbh->dbgetpacket

Returns the TDS packet size currently in use for this $dbh.

=back

=head2 TEXT/IMAGE Routines

=over 8

=item $status = $dbh->dbwritetext($colname, $dbh_2, $colnum, $text [, $log])

Insert or update data in a TEXT or IMAGE column. The usage is a bit
different from that of the C version:

The calling sequence is a little different from the C version, and
logging is B<off> by default:

B<$dbh_2> and B<$colnum> are the B<DBPROCESS> and column number of a
currently active query. Example:

   $dbh_2->dbcmd('select the_text, t_index from text_table where t_index = 5');
   $dbh_2->dbsqlexec; $dbh_2->dbresults;
   @data = $dbh_2->dbnextrow;

   $d->dbwritetext ("text_table.the_text", $dbh_2, 1,
	"This is text which was added with Sybperl", TRUE);

=item $status = $dbh->dbpreptext($colname, $dbh_2, $colnum, $size [, $log])

Prepare to insert or update text with dbmoretext().

The calling sequence is a little different from the C version, and
logging is B<off> by default:

B<$dbh_2> and B<$colnum> are the B<DBPROCESS> and column number of a
currently active query. Example:

   $dbh_2->dbcmd('select the_text, t_index from text_table where t_index = 5');
   $dbh_2->dbsqlexec; $dbh_2->dbresults;
   @data = $dbh_2->dbnextrow;

   $size = length($data1) + length($data2);
   $d->dbpreptext ("text_table.the_text", $dbh_2, 1, $size, TRUE);
   $dbh->dbsqlok;
   $dbh->dbresults;
   $dbh->dbmoretext(length($data1), $data1);
   $dbh->dbmoretext(length($data2), $data2);

   $dbh->dbsqlok;
   $dbh->dbresults;

=item $status = $dbh->dbmoretext($size, $data)

Sends a chunk of TEXT/IMAGE data to the server. See the example above.

=item $status = $dbh->dbreadtext($buf, $size)

Read a TEXT/IMAGE data item in $size chunks.

Example:

    $dbh->dbcmd("select data from text_test where id=1");
    $dbh->dbsqlexec;
    while($dbh->dbresults != NO_MORE_RESULTS) {
        my $bytes;
        my $buf = '';
        while(($bytes = $dbh->dbreadtext($buf, 512)) != NO_MORE_ROWS) {
	    if($bytes == -1) {
	        die "Error!";
            } elsif ($bytes == 0) {
                print "End of row\n";
            } else {
                print "$buf";
            }
        }
    }

=back

=head2 BCP Routines

See also the B<Sybase::BCP> module.

=over 8

=item BCP_SETL($state)

This is an exported routine (i.e., it can be called without a $dbh
handle) which sets the BCP IN flag to TRUE/FALSE.

It is necessary to call BCP_SETL(TRUE) before opening the
connection with which one wants to run a BCP IN operation.

=item $state = bcp_getl

Retrieve the current BCP flag status.

=item $status = $dbh->bcp_init($table, $hfile, $errfile, $direction)

Initialize BCP library. $direction can be B<DB_OUT> or B<DB_IN>

=item $status = $dbh->bcp_meminit($numcols)

This is a utility function that does not exist in the normal BCP
API. Its use is to initialize some internal variables before starting
a BCP operation from program variables into a table. This call avoids
setting up translation 
information for each of the columns of the table being updated,
obviating the use of the bcp_colfmt call.

See EXAMPLES, below.

=item $status = $dbh->bcp_sendrow(LIST)

=item $status = $dbh->bcp_sendrow(ARRAY_REF)

Sends the data in LIST to the server. The LIST is assumed to contain
one element for each column being updated. To send a NULL value set
the appropriate element to the Perl B<undef> value.

In the second form you pass an array reference instead of passing the
LIST, which makes processing a little bit faster on wide tables.

=item $rows = $dbh->bcp_batch

Commit rows to the database. You usually use it like this:

       while(<IN>) {
           chop;
	   @data = split(/\|/);
	   $d->bcp_sendrow(\@data);    # Pass the array reference

	   # Commit data every 100 rows.
	   if((++$count % 100) == 0) {
		   $d->bcp_batch;
	   }
	}


=item $status = $dbh->bcp_done

=item $status = $dbh->bcp_control($field, $value)

=item $status = $dbh->bcp_columns($colcount)

=item $status = $dbh->bcp_colfmt($host_col, $host_type, $host_prefixlen, $host_collen, $host_term, $host_termlen, $table_col [, $precision, $scale])

If you have DB-Library for System 10 or higher, then you can pass the
additional $precision and $scale parameters, and have sybperl call
bcp_colfmt_ps() instead of bcp_colfmt().

=item $status = $dbh->bcp_collen($varlen, $table_column)

=item $status = $dbh->bcp_exec

=item $status = $dbh->bcp_readfmt($filename)

=item $status = $dbh->bcp_writefmt($filename)

Please see the DB-Library documentation for these calls.

=back

=head2 DBMONEY Routines

B<NOTE:> In this version it is possible to avoid calling the routines
below and still get B<DBMONEY> calculations done with the correct
precision. See the B<Sybase::DBlib::Money> discussion below.

=over 8

=item ($status, $sum) = $dbh->dbmny4add($m1, $m2)

=item  $status = $dbh->dbmny4cmp($m1, $m2)

=item ($status, $quotient) = $dbh->dbmny4divide($m1, $m2)

=item ($status, $dest) = $dbh->dbmny4minus($source)

=item ($status, $product) = $dbh->dbmny4mul($m1, $m2)

=item ($status, $difference) = $dbh->dbmny4sub($m1, $m2)

=item ($status, $ret) = $dbh->dbmny4zero

=item ($status, $sum) = $dbh->dbmnyadd($m1, $m2)

=item $status = $dbh->dbmnycmp($m1, $m2)

=item ($status, $ret) = $dbh->dbmnydec($m1)

=item ($status, $quotient) = $dbh->dbmnydivide($m1, $m2)

=item ($status, $ret, $remainder) = $dbh->dbmnydown($m1, $divisor)

=item ($status, $ret) = $dbh->dbmnyinc($m1)

=item ($status, $ret, $remain) = $dbh->dbmnyinit($m1, $trim)

=item ($status, $ret) = $dbh->dbmnymaxneg

=item ($status, $ret) = $dbh->dbmnymaxpos

=item ($status, $dest) = $dbh->dbmnyminus($source)

=item ($status, $product) = $dbh->dbmnymul($m1, $m2)

=item ($status, $m1, $digits, $remain) = $dbh->dbmnyndigit($m1)

=item ($status, $ret) = $dbh->dbmnyscale($m1, $multiplier, $addend)

=item ($status, $difference) = $dbh->dbmnysub($m1, $m2)

=item ($status, $ret) = $dbh->dbmnyzero

All of these routines correspond to their DB-Library counterpart, with
the following exception:

The routines which in the C version take pointers to arguments
(in order to return values) return these values in a list instead:

   status = dbmnyadd(dbproc, m1, m2, &result) becomes
   ($status, $result) = $dbproc->dbmnyadd($m1, $m2)

=back

=head2 RPC Routines

B<NOTE:> Check out eg/rpc-example.pl for an example on how to use
these calls.

=over 8

=item $dbh->dbrpcinit($rpcname, $option)

Initialize an RPC call to the remote procedure $rpcname. See the
DB-Library manual for valid values for $option.

=item $dbh->dbrpcparam($parname, $status, $type, $maxlen, $datalen, $value)

Add a parameter to an RPC call initiated with dbrpcinit(). Please see
the DB-Library manual page for details & values for the parameters.

B<NOTE:> All floating point types (MONEY, FLOAT, REAL, DECIMAL, etc.)
are converted to FLOAT before being sent to the RPC.

=item $dbh->dbrpcsend([$no_ok])

Execute an RPC initiated with dbrpcinit().

By default this routine calls the C library dbrpcsend() and dbsqlok(), so 
that you can directly call $dbh->dbresults directly after a call to 
$dbh->dbrpcsend. If you need more control you can pass a non-0 value for 
the $no_ok parameter, and it will then be your responsibility to call
$dbh->dbsqlok(). Please read the Sybase OpenClient DB-Library manual
pages on dbrpcsend() and dbsqlok() for further details.

=item dbrpwset($srvname, $pwd)

Set the password for connecting to a remote server.

=item dbrpwclr

Clear all remote server passwords.

=back

=head2 Registered procedure execution

=over 8

=item $status = $dbh->dbreginit($proc_name)

=item $status = $dbh->dbreglist

=item $status = $dbh->dbreglist($parname, $type, $datalen, $value)

=item $status = $dbh->dbregexec($opt)

These routines are used to execute an OpenServer registered procedure.
Please see the Sybase DB-Library manual for a description of what these
routines do, and how to call them.

=back

=head2 Two Phase Commit Routines

=over 8

=item $dbh = Sybase::DBlib->open_commit($user, $pwd, $server, $appname)

=item $id = $dbh->start_xact($app_name, $xact_name, $site_count)

=item $status = $dbh->stat_xact($id)

=item $status = $dbh->scan_xact($id)

=item $status = $dbh->commit_xact($id)

=item $status = $dbh->abort_xact($id)

=item $dbh->close_commit

=item $string = Sybase::DBlib::build_xact_string($xact_name, $service_name, $id)

=item $status = $dbh->remove_xact($id, $site_count)

Please see the Sybase documentation for this.

B<NOTE:> These routines have not been thoroughly tested!

=back

=head2 Exported Routines

=over 8

=item $old_handler = dberrhandle($err_handle)

=item $old_handler = dbmsghandle($msg_handle)

Register an error (or message) handler for DB-Library to use. Handler
examples can be found in B<sybutil.pl> in the Sybperl
distribution. Returns a reference to the previously defined handler
(or undef if none were defined). Passing undef as the argument clears
the handler.

=item dbsetifile($filename)

Set the name of the 'interfaces' file. This file is normally found by
DB-Library in the directory pointed to by the $SYBASE environment variable.

=item dbrecftos($filename)

Start recording all SQL sent to the server in file $filename.

=item dbversion

Returns a string identifying the version of DB-Library that this copy
of Sybperl was built with.

=item DBSETLCHARSET($charset)

=item DBSETLNATLANG($language)

=item DBSETLPACKET($packet_size)

=item DBSETLENCRYPT($flag)

=item $time = DBGETTIME

=item $time = dbsettime($seconds)

=item $time = dbsetlogintime($seconds)

These utility routines are probably very seldom used. See the
DB-Library manual for an explanation of their use.

=item dbexit

Tell DB-Library that we're done. Once this call has been made, no
further activity requiring DB-Library can be performed in the current
program.

=back

=head2 High Level Wrapper Functions (sql() and nsql())

These routines are not part of the DB-Library API, but have been added
because they can make our life as programmers easier, and exploit
certain strengths of Perl.

=over 8

=item $ret|@ret = $dbh->sql($cmd [, \&rowcallback [, $flag]])

Runs the sql command and returns the result as a reference to an array
of the rows. In a LIST context, return the array itself (instead of a
reference to the array).  Each row is a reference to a list of scalars.

If you provide a second parameter it is taken as a procedure to call
for each row.  The callback is called with the values of the row as
parameters.

If you provide a third parameter, this is used in the call to
dbnextrow() to retrieve associative arrays rather than 'normal' arrays
for each row, and store them in the returned array. To pass the third
parameter without passing the &rowcallback value you should pass the
special value I<undef> as second parameter:

	@rows = $dbh->sql("select * from sysusers", undef, TRUE);
	foreach $row_ref (@rows) {
	    if($$row_ref{'uid'} == 10) {
	        ....
	    }
	}

See also eg/sql.pl for an example.

Contributed by Gisle Aas.

B<NOTE:> This routine loads all the data into memory. It should not be
run with a query that returns a large number of rows. To avoid the
risk of overflowing memory, you can limit the number of rows that the
query returns by setting the 'MaxRows' field of the $dbh attribute
field:

	$dbh->{'MaxRows'} = 100;

This value is B<not> set by default.

=item @ret = $dbh->nsql($sql [, "ARRAY" | "HASH" ] [, \&subroutine ] );

An enhanced version of the B<sql> routine, B<nsql>, is also available.
nsql() provides better error checking (using its companion error and
message handlers), optional deadlock retry logic, and several options
for the format of the return values.  In addition, the data can either
be returned to the caller in bulk, or processes line by line via a
callback subroutine passed as an argument (this functionality is
similar to the r_sql() method).

The arguments are an SQL command to be executed, the B<$type> of the
data to be returned, and the callback subroutine.

if a callback subroutine is not given, then the data from the query is
returned as an array.  The array returned by nsql is one of the
following:

    Array of Hash References (if type eq HASH)
    Array of Array References (if type eq ARRAY)
    Simple Array (if type eq ARRAY, and a single column is queried)
    Boolean True/False value (if type ne ARRAY or HASH)

Optionally, instead of the words "HASH" or "ARRAY" a reference of the
same type can be passed as well.  That is, both of the following are
equivalent:

    $dbh->nsql("select col1,col2 from table","HASH");
    $dbh->nsql("select col1,col2 from table",{});

For example, the following code will return an array of hash
references:

    @ret = $dbh->nsql("select col1,col2 from table","HASH");
    foreach $ret ( @ret ) {
      print "col1 = ", $ret->{'col1'}, ", col2 = ", $ret->{'col2'}, "\n";
    }

The following code will return an array of array references:

    @ret = $dbh->nsql("select col1,col2 from table","ARRAY");
    foreach $ret ( @ret ) {
      print "col1 = ", $ret->[0], ", col2 = ", $ret->[1], "\n";
    }

The following code will return a simple array, since the select
statement queries for only one column in the table:

    @ret = $dbh->nsql("select col1 from table","ARRAY");
    foreach $ret ( @ret ) {
      print "col1 = $ret\n";
    }

Success or failure of an nsql() call cannot necessarily be judged
from the value of the return code, as an empty array may be a
perfectly valid result for certain sql code.
  
The nsql() routine will maintain the success or failure state in a
variable $DB_ERROR, accessed by the method of the same name, and a
pair of Sybase message/error handler routines are also provided which
will use $DB_ERROR for the Sybase messages and errors as well.
However, these must be installed by the client application:

    dbmsghandle("Sybase::DBlib::nsql_message_handler");
    dberrhandle("Sybase::DBlib::nsql_error_handler");

Success of failure of an nsql() call cannot necessarily be judged
from the value of the return code, as an empty array may be a
perfectly valid result for certain sql code.

The following code is the proper method for handling errors with use
of nsql.

    @ret = $dbh->nsql("select stuff from table where stuff = 'nothing'","ARRAY");
    if ( $DB_ERROR ) {
      # error handling code goes here, perhaps:
      die "Unable to get stuff from table: $DB_ERROR\n";
    }
  
The behavior of nsql() can be customized in several ways.  If the
variable:

    $Sybase::DBlib::nsql_strip_whitespace

is true, then nsql() will strip the trailing white spaces from all of
the scalar values in the results.

When using a callback subroutine, the subroutine is passed to nsql()
as a CODE reference.  For example:

    sub parse_hash {
      my %data = @_;
      # Do something with %data 
    }

    $dbh->nsql("select * from really_huge_table","HASH",\&parse_hash);
    if ( $DB_ERROR ) {
      # error handling code goes here, perhaps:
      die "Unable to get stuff from really_huge_table: $DB_ERROR\n";
    }

In this case, the data is passed to the callback (&parse_hash) as a
HASH, since that was the format specified as the second argument.  If
the second argument specifies an ARRAY, then the data is passed as an
array.  For example:

    sub parse_array {
      my @data = @_;
      # Do something with @data 
    }

    $dbh->nsql("select * from really_huge_table","HASH",\&parse_array);
    if ( $DB_ERROR ) {
      # error handling code goes here, perhaps:
      die "Unable to get stuff from really_huge_table: $DB_ERROR\n";
    }

The primary advantage of using the callback is that the rows are
processed one at a time, rather than returned in a huge
array.  For very large tables, this can result in very significant
memory consumption, and on resource-constrained machines, some large
queries may simply fail.  Processing rows individually will use much
less memory.

IMPORTANT NOTE: The callback subroutine must return a true value if it
has successfully handled the data.  If a false value is returned, then
the query is canceled via dbcancel(), and nsql() will abort further
processing.

WARNING: Using the following deadlock retry logic together with a
callback routine is dangerous.  If a deadlock is encountered after
some rows have already been processed by the callback, then the data
will be processed a second time (or more, if the deadlock is retried
multiple times).

The nsql() method also supports automated retries of deadlock errors
(1205).  This is disabled by default, and enabled only if the
variable

    $Sybase::DBlib::nsql_deadlock_retrycount

is non-zero.  This variable is the number of times to resubmit a given
SQL query, and the variable

    $Sybase::DBlib::nsql_deadlock_retrysleep

is the delay, in seconds, between retries (default is 60).  Normally,
the retries happen silently, but if you want nsql() to carp() about
it, then set

    $Sybase::DBlib::nsql_deadlock_verbose

to a true value, and nsql() will whine about the failure.  If all of
the retries fail, then nsql() will return an error, as it normally
does.  If you want the code to try forever, then set the retry count
to -1.

=back

=head2 Constants

Most of the #defines from sybdb.h can be accessed as
B<Sybase::DBlib::NAME> (e.g., B<Sybase::DBlib::STDEXIT>). Additional constants are:

=over 8

=item $Sybase::DBlib::Version

The Sybperl version. Can be interpreted as a string or as a number.

=item DBLIBVS

The version of I<DB-Library> that sybperl was built against.

=back

=head2 Attributes

The behavior of certain aspects of the Sybase::DBlib module can be
controlled via global or connection specific attributes. The global
attributes are stored in the %Sybase::DBlib::Att variable, and the
connection specific attributes are stored in the $dbh. To set a global
attribute, you would code

     $Sybase::DBlib::Att{'AttributeName'} = value;

and to set a connection specific attribute you would code

     $dbh->{"AttributeName'} = value;

B<NOTE!!!> Global attribute setting changes do not affect existing
connections, and changing an attribute inside a ct_fetch() does B<not>
change the behavior of the data retrieval during that ct_fetch()
loop.

The following attributes are currently defined:

=over 8

=item dbNullIsUndef

If set, NULL results are returned as the Perl 'undef' value, otherwise
as the string "NULL". B<Default:> set.

=item dbKeepNumeric

If set, numeric results are not converted to strings before returning
the data to Perl. B<Default:> set.

=item dbBin0x

If set, BINARY results are preceded by '0x' in the result. B<Default:> unset.

=item useDateTime

Turn the special handling of B<DATETIME> values on. B<Default:>
unset. See the section on special datatype handling below.

=item useMoney

Turn the special handling of B<MONEY> values on. B<Default:>
unset. See the section on special datatype handling below.

=back

=head2 Status Variables

These status variables are set by I<Sybase::DBlib> internal routines,
and can be accessed using the $dbh->{'variable'} syntax.

=over 8

=item DBstatus

The return status of the last call to I<dbnextrow>.

=item ComputeID

The compute id of the current returned row. Is 0 if no I<compute by>
clause is currently being processed.

=back

=head2 Examples

=over 4

=item BCP from program variables

See also B<Sybase::BCP> for a simplified bulk copy API.


   &BCP_SETL(TRUE);
   $dbh = new Sybase::DBlib $User, $Password;
   $dbh->bcp_init("test.dbo.t2", '', '', DB_IN);
   $dbh->bcp_meminit(3);   # we wish to copy three columns into
			   # the 't2' table
   while(<>)
   {
	chop;
	@dat = split(' ', $_);
	$dbh->bcp_sendrow(@dat);
   }
   $ret = $dbh->bcp_done;

=item Using the sql() routine

   $dbh = new Sybase::DBlib;
   $ret = $dbh->sql("select * from sysprocesses");
   foreach (@$ret)   # Loop through each row
   {
       @row = @$_;
       # do something with the data row...
   }

   $ret = $dbh->sql("select * from sysusers", sub { print "@_"; });
   # This will select all the info from sysusers, and print it

=item Getting SHOWPLAN and STATISTICS information within a script

You can get B<SHOWPLAN> and B<STATISTICS> information when you run a
B<sybperl> script. To do so, you must first turn on the respective
options, using dbsetopt(), and then you need a special message handler
that will filter the B<SHOWPLAN> and/or B<STATISTICS> messages sent
from the server.

The following message handler differentiates the B<SHOWPLAN> or
B<STATICSTICS> messages from other messages:

    # Message number 3612-3615 are statistics time / statistics io
    # message. Showplan messages are numbered 6201-6225.
    # (I hope I haven't forgotten any...)
    @sh_msgs = (3612 .. 3615, 6201 .. 6225);
    @showplan_msg{@sh_msgs} = (1) x scalar(@sh_msgs);

    sub showplan_handler {
	my ($db, $message, $state, $severity, $text,
	    $server, $procedure, $line)	= @_;
    
        # Don't display 'informational' messages:
	if ($severity > 10) {
	    print STDERR ("Sybase message ", $message, ",
	       Severity ", $severity, ", state ", $state);
	    print STDERR ("\nServer `", $server, "'") if defined ($server);
	    print STDERR ("\nProcedure `", $procedure, "'")
		  if defined ($procedure);
	    print STDERR ("\nLine ", $line) if defined ($line);
	    print STDERR ("\n    ", $text, "\n\n");
        }
        elsif($showplan_msg{$message}) {
	# This is a HOWPLAN or STATISTICS message, so print it out:
	    print STDERR ($text, "\n");
        }
        elsif ($message == 0) {
	    print STDERR ($text, "\n");
        }
    
        0;
    }

This could then be used like this:

    use Sybase::DBlib;
    dbmsghandle(\&showplan_handler);

    $dbh = new Sybase::DBlib  'mpeppler', $password, 'TROLL';

    $dbh->dbsetopt(DBSHOWPLAN);
    $dbh->dbsetopt(DBSTAT, "IO");
    $dbh->dbsetopt(DBSTAT, "TIME");

    $dbh->dbcmd("select * from xrate where date = '951001'");
    $dbh->dbsqlexec;
    while($dbh->dbresults != NO_MORE_RESULTS) {
	while(@dat = $dbh->dbnextrow) {
	    print "@dat\n";
        }
    }

Et voilą!

=back

=head1 Sybase::Sybperl

The Sybase::Sybperl package is designed for backwards compatibility
with sybperl 1.0xx (for Perl 4.x). Its main purpose is to allow
sybperl 1.0xx scripts to work unchanged with Perl 5 & sybperl 2. Using
this API for new scripts is not recommended, unless portability with
older versions of sybperl is essential.

The sybperl 1.0xx man page is included in this package in pod/sybperl-1.0xx.man.

Sybase::Sybperl is layered on top of the Sybase::DBlib package, and could
therefore suffer a small performance penalty.

=head1 Sybase::CTlib

The CT-Library module has been written in collaboration with Sybase.

=head2 DESCRIPTION

=over 8

=item $dbh = new Sybase::CTlib $user [, $passwd [, $server [, $appname[, {attributes}]]]]

=item $dbh = Sybase::CTlib->ct_connect($user [, $passwd [, $server [,$appname, [{attributes}]]]])

Establishes a connection to the database engine. Initializes and
allocates resources for the connection, and registers the user name,
password, target server, and application name.

The B<attributes> hash reference can be used to add private attributes
to the connection handle that you can later use, and can also be used to 
set certain connection properties.

To set the connection properties you pass a special hash in the
B<attributes> parameter:

	$dbh = new Sybase::CTlib 'user', 'pwd', 'SYBASE', undef, 
	           { CON_PROPS => { CS_HOSTNAME => 'kiruna',
                                    CS_PACKETSIZE => 1024,
				    CS_SEC_CHALLENGE => CS_TRUE }
                   };

The following connection properties are currently recognized:

=over 4

=item CS_HOSTNAME

=item CS_ANSI_BINDS

=item CS_PACKETSIZE

=item CS_SEC_APPDEFINED

=item CS_SEC_CHALLENGE

=item CS_SEC_ENCRYPTION

=item CS_SEC_NEGOTIATE

=item CS_HAFAILOVER

=back

See the Sybase documentation on how and when to use these connection
properties.

Please see the Sybase manuals for B<CS_HAFAILOVER> usage.

In addition, you can set the B<CS_SYB_LANG> and B<CS_SYB_CHARSET> properties
in the same manner. However - you should be aware that these settings affect
all open connections, not just the one that you are opening with this call
to ct_connect(). This behavior will likely change in the future.


=item $status = $dbh->ct_execute($sql)

Send the SQL commands $sql to the server. Multiple commands are
allowed. However, you must call ct_results() until it returns
CS_END_RESULTS or CS_FAIL, or call ct_cancel() before submitting a new
set of SQL commands to the server.

Return values: CS_SUCCEED, CS_FAIL or CS_CANCELED (the operation was
canceled).

B<NOTE:> ct_execute() is equivalent to calling ct_command() followed by
ct_send(). 

=item $status = $dbh->ct_command(type, buffer, len, option)

Append a command to the current SQL command buffer. Please check the
OpenClient documentation for exact usage.

B<NOTE:> You should only need to call ct_command()/ct_send() directly
if you want to do RPCs or cursor operations. For straight queries you
should use ct_execute() or ct_sql() instead.

=item $status = $dbh->ct_send

Send the current command buffer to the server for execution.

B<NOTE:> You only need to call ct_send() directly if you've used
ct_command() to set up your SQL query.

=item $status = $dbh->ct_results($res_type [, $textBind])

This routine returns a results type to indicate the status of returned
data. "Command Done:" result type is returned if one result set has
been processed. "Row result" token is returned if regular rows are
returned. This output is stored in $res_type.

If the optional $textBind parameter is B<FALSE> then I<TEXT> or
I<IMAGE> columns are B<not> bound, and will not be subsequently
returned by ct_fetch(). Using this feature is a little tricky - please
see the discussion on raw B<TEXT> and B<IMAGE> handling elsewhere in this
document.

The commonly used values for $res_type are CS_ROW_RESULT, CS_CMD_DONE,
CS_CMD_SUCCEED, CS_COMPUTE_RESULT, CS_CMD_FAIL. The full list of
values is on page 3-203 of the OpenClient reference manual.

See also the description of ct_fetchable() below.

The $status value takes the following values: CS_SUCCEED,
CS_END_RESULTS, CS_FAIL, CS_CANCELED.

=item @names = $dbh->ct_col_names

Retrieve the column names of the current query. If the current query
is not a select statement, then an empty array is returned.

=item @types = $dbh->ct_col_types([$doAssoc])

Retrieve the column types of the currently executing query. If
$doAssoc is non-0, then a hash (aka associative array) is returned
with column names/column type pairs.

=item @data = $dbh->ct_describe([$doAssoc])

Retrieves the description of each of the output columns of the current
result set. Each element of the returned array is a reference to a
hash that describes the column. The following fields are set: B<NAME>,
B<TYPE>, B<SYBTYPE>, B<MAXLENGTH>, B<SCALE>, B<PRECISION>, B<STATUS>.

You could use it like this:

    $dbh->ct_execute("select name, uid from sysusers");
    while(($rc = $dbh->ct_results($restype)) == CS_SUCCEED) {
        next unless $dbh->ct_fetchable($restype);

	@desc = $dbh->ct_describe;
	print "$desc[0]->{NAME}\n";         # prints 'name'
	print "$desc[0]->{MAXLENGTH}\n";    # prints 30

	....
    }

The B<STATUS> field is a bitmask which can be tested for the following
values: CS_CANBENULL, CS_HIDDEN, CS_IDENTITY, CS_KEY, CS_VERSION_KEY,
CS_TIMESTAMP and CS_UPDATEABLE. See table 3-46 of the Open Client
Client Library Reference Manual for a description of each of these values.

The B<TYPE> field is the data type that Sybase::CTlib converts the column 
I<to> when retrieving the data, so a DATETIME column will be returned as a 
CS_CHAR_TYPE column, unless the B<UseDateTime> attribute described elsewhere 
in this document is turned on.

The B<SYBTYPE> field is the real Sybase data type for this column.


=item @data = $dbh->ct_fetch([$doAssoc [, $wantRef]])

Retrieve one row of data. If $doAssoc is non-0, a hash is returned
with column name/value pairs.

If $wantRef is non-0, then a B<reference> to an array (or hash)
is returned. This reference I<points> to a static array (or hash), so
to store the returned rows in an array you must copy the array (or hash):

   while($d = $dbh->ct_fetch(1, 1)) {
      push(@rows, {%$d});
   }

An empty array is returned if there is no data to fetch.

=item $dbh->ct_cancel($type)

Issue an attention signal to the server about the current
transaction. If $type == CS_CANCEL_ALL, then cancels the current
command immediately. If $type == CS_CANCEL_ATTN, then discard all
results when next time the application reads from the server.

=item $status = $dbh->DBDEAD

Calls ct_con_props(CS_CON_STATUS) on the connection and returns TRUE if 
the connection status CS_CONSTAT_DEAD bit is set. If this call returns
TRUE (i.e. non-0) the connection has been marked DEAD and you need to 
reconnect to the server.

=item $old_cb = ct_callback($type, $cb_func)

Install a callback routine. Valid callback types are CS_CLIENTMSG_CB
and CS_SERVERMSG_CB. Returns a reference to the previously installed
callback of the specified type, or I<undef> if no callback of that type
exists. Passing undef as $cb_func unsets the callback for that type.

=item $res_info = $dbh->ct_res_info($info_type)

Retrieves information on the current result set. The type of
information returned depends on $info_type. Currently supported values
are: CS_NUM_COMPUTES, CS_NUMDATA, CS_NUMORDERCOLS, CS_ROW_COUNT.

=item ($status, $param) = $dbh->ct_options($action, $option, $param, $type)

This routine will set, retrieve, or clear the values of server
query-processing options.

Values for $action: CS_SET, CS_GET, CS_CLEAR

Values for $option: see p. 3-170 of the OpenClient reference manual

Values for $param: When setting an option, $param can be a integer or
a string. When retrieving an option, $param is set and returned. When
clearing an option, $param is ignored.

Value for $type: CS_INT_TYPE if $param is of integer type,
CS_CHAR_TYPE if $param is a string

=item $ret = $dbh->ct_cursor($type, $name, $text, $option)

Initiate a cursor command. Usage is similar to the CT-Library
ct_cursor() call, except that when in C you would pass NULL as the
value for $name or $text you pass the special Perl value I<undef>
instead.

See eg/ct_cursor.pl for an example.

=item $ret = $dbh->ct_param(\%datafmt)

Define a command parameter. The %datafmt hash is used to pass the
appropriate parameters to the call. The following fields are defined:
name (parameter name), datatype, status, indicator, and value. These
fields correspond to the equivalent fields in the CS_DATAFMT structure
which is used in the CT-Library ct_param call, and includes the two
additional parameters 'value' and 'indicator'.

The hash should be used like this:

  %param = (name => '@acc', datatype => CS_CHAR_TYPE,
            status => CS_INPUTVALUE, value => 'CIS 98941',
	    indicator => CS_UNUSED);

  $dbh->ct_param(\%param);

Note that ct_param() converts all parameter types to either
CS_CHAR_TYPE, CS_FLOAT_TYPE, CS_DATETIME_TYPE, CS_MONEY_TYPE or
CS_INT_TYPE.

See eg/ct_param.pl for an example.

=item $dbh2 = $dbh->ct_cmd_alloc

Allocate a new I<CS_COMMAND> structure. The new $dbh2 shares the
I<CS_CONNECTION> with the original $dbh, so this is really only
useful for interleaving cursor operations (see ct_cursor() above, and
the section on cursors in Chapter 2 of the
I<Open Client Client-Library/C Reference Manual>).

The two handles also share attributes, so setting $dbh->{UseDataTime}
(for example) will also set $dbh2->{UseDateTime}.

=item $rc = $dbh->ct_cmd_realloc

Drops the current I<CS_COMMAND> structure, and reallocs a new
one. Returns CS_SUCCEED on successful completion.

=item $ret = ct_config($action, $property, $value, $type)

Calls ct_config() to change some basic parameter, like the
B<interfaces> file location.

$action can be B<CS_SET> or B<CS_GET>.

$property is one of the properties that is settable via ct_config()
(see your OpenClient man page on ct_config() for a complete list).

$value is the input value if $action is B<CS_SET>, and the output
value if $action is B<CS_GET>.

$type is the data type of the property that is being set or
retrieved. It defaults to B<CS_CHAR_TYPE>, but should be set to
B<CS_INT_TYPE> if an integer value (such as B<CS_NETIO>) is being set or
retrieved.

$ret is the return status of the ct_config() call.

Example:

	$ret = ct_config(CS_SET, CS_IFILE, "/home/mpeppler/foo", CS_CHAR_TYPE);
	print "$ret\n";

	$ret = ct_config(CS_GET, CS_IFILE, $out, CS_CHAR_TYPE);
	print "$ret - $out\n";  #prints 1 - /home/mpeppler/foo

=item $ret = $dbh->ct_dyn_prepare($sql)

Prepare a I<Dynamic SQL> statement. A Dynamic SQL statement is a normal
insert/update/delete SQL statement that includes '?' placeholders.
The placeholders are replaced by the values passed to ct_dyn_execute().
The prepared statement can be called multiple times with different variables,
making it quite efficient.

Sybase creates a temporary stored procedure for the dynamic SQL statement,
and then executes this procedure each time ct_dyn_execute() is called. Only
one active prepared statement can exist per connection.

Returns CS_SUCCEED on success, CS_FAIL in case of errors.

Example:

   $ret = $dbh->ct_dyn_prepare("update my_table set string=? where key=?");
   $dbh->ct_dyn_execute(["testing", 123]);
   while($dbh->ct_results($restype) == CS_SUCCEED) {
       next unless $dbh->ct_fetchable($restype);
       # shoudn't have any fetchable results in this example!
   }
   $dbh->ct_dyn_execute(["another test", 456]);
   while($dbh->ct_results($restype) == CS_SUCCEED) {
       next unless $dbh->ct_fetchable($restype);
       # shoudn't have any fetchable results in this example!
   }
   $dbh->ct_dyn_dealloc();    # free the temporary proc.

=item $ret = $dbh->ct_dyn_execute($arrayref);

Execute a prepared statement with the specified parameters. $arrayref is
a reference to an array with one value for each '?' that appears in the 
SQL statement passed to ct_dyn_prepare().

=item $ret = $dbh->ct_dyn_dealloc();

Free the resources associated with the prepared statement created by
ct_dyn_prepare(). This must be called before you can prepare a new 
statement. 

=item $ret = cs_dt_info($action, $type, $item, $buf)

cs_dt_info() allows you to set the default conversion modes for
I<DATETIME> values, and lets you query the locale database for
names for dateparts.

To set the default conversion you call cs_dt_info() with
a $type parameter of CS_DT_CONVFMT, and pass the conversion style
you want as the last parameter:

	cs_dt_info(CS_SET, CS_DT_CONVFMT, CS_UNUSED, CS_DATES_LONG);

See Table 2-26 in the Open Client and Open Server Common Libraries 
Reference Manual for details of other formats that are available.

You can query a datepart name by doing something like:

	cs_dt_info(CS_GET, CS_MONTH, 3, $buf);
	print "$buf\n";    # Prints 'April' in the default locale

Again see the entry for cs_dt_info() in Chapter 2 of the Open Client and
Open Server Common Libraries Reference Manual for details.

=item ($ret, $data) = $dbh->ct_get_data($colnum [, $maxsize])

Retrieve the raw TEXT or IMAGE data from column $colnum in the
current result set. The optional $maxsize parameter can be used
to limit the size of the retrieved buffer. If $maxsize is used then
ct_get_data() should be called in a loop until $ret == CS_END_DATA
(all the data for this column has been retrieved, and this is the
last column in the row) or $ret == CS_END_ITEM (all the data for this
column has been retrieved, but there are more columns to be processed).

The TEXT columns B<must> appear I<after> all the normal data columns
in the select list for this to work, and ct_results() must be called 
with $textBind set to 0.

See the discussion on raw TEXT/IMAGE processing elsewhere in this 
document.

=item $ret = $dbh->ct_send_data($data, $size)

Send $size bytes of data in $data to the server, based on information 
previously determined via a call to ct_data_info().

If the data item is large it can be stored in chunks by calling
ct_send_data() multiple times.

Please see the discussion on raw TEXT/IMAGE handling elsewhere in this
document for details.


=item $ret = $dbh->ct_data_info($action, $colnum [, \%attr [, $dbh_2]])

When $action is CS_GET ct_data_info() retrieves a CS_IODESC
struct for column $colnum. The CS_IODESC struct is stored internally
in the $dbh, and stores the text pointer, the total length and whether
logging should be turned on for updates.

The %attr hash can be used to set the B<total_txtlen> and
B<log_on_update> fields of the CS_IODESC by setting the 
corresponding fields in the %attr param:

	%attr = ( total_txtlen => 1024, log_on_update => 1 );
        $dbh->ct_data_info(CS_SET, $colnum, \%attr);

If $dbh_2 is passed and $action is CS_SET then the CS_IODESC struct
from $dbh_2 is copied to $dbh. This is useful if you need to update
TEXT columns in multiple rows by selecting the rows in one
connection and doing the update in a different connection.

The CS_IODESC struct is referenced when calling ct_send_data() to
tell OpenClient where to store the data that is being sent to the server,
as well as what the total size of the column is supposed to be.

The CS_IODESC is typically set during a select query to retrieve a valid
text pointer to the column that you wish to update. Sybase::CTlib is
limited to a single CS_IODESC struct per connection, so you can only
update a single TEXT or IMAGE column at a time.

For details on CS_IODESC please see the Sybase OpenClient 
Client Library Reference Manual.

For examples on the usage of ct_data_info() please see the discussion on 
raw TEXT processing elsewhere in this document.


=item $ret|@ret = $dbh->ct_sql($cmd [, \&rowcallback [, $doAssoc]])

Runs the sql command and returns the result as a reference to an array
of the rows.  Each row is a reference to an array of scalars. In a
LIST context, ct_sql returns an array of references to each row.

If the $doAssoc parameter is B<CS_TRUE>, then each row is a reference
to an associative array (keyed on the column names) rather than a
normal array (see ct_fetch(), above).

If you provide a second parameter it is taken as a procedure to call
for each row.  The callback is called with the values of the row as
parameters.

This routine is very useful to send SQL commands to the server that
do not return rows, such as:

    $dbh->ct_sql("use BugTrack");

Examples can be found in eg/ct_sql.pl.

B<NOTE:> This routine loads all the data into memory. Memory
consumption can therefore become quite important for a query that
returns a large number of rows, unless the B<MaxRows> attribute has
been set.

Two additional attributes are set after calling ct_sql(): B<ROW_COUNT>
holds the number of rows affected by the command, and B<RC> holds the
return code of the last call to ct_execute().

=item @ret = $dbh->nsql($sql [, "ARRAY" | "HASH" ] [, \&subroutine ] );

See the entry in the Sybase::DBlib section, above.

=item $ret = $dbh->ct_fetchable($restype)

Returns TRUE if the current result set has fetchable rows.
Use like this:

    $dbh->ct_execute("select * from sysprocesses");
    while($dbh->ct_results($restype) == CS_SUCCEED) {
        next if(!$dbh->ct_fetchable($restype));

	while(@dat = $dbh->ct_fetch) {
	    print "@dat\n";
	}
    }

=back

=head2 Bulk-Copy Routines (BLK API)

The following routines allow you to use a subset of the blk_*()
routines to bulk-copy data from Perl variables to Sybase tables.
The blk_*() routines are faster than normal inserts as they run with 
minimal logging.

You enable BLK operations on a Sybase::CTlib handle by passing the 
CS_BULK_LOGIN property to the new() or ct_connect() call:

   my $dbh = new Sybase::CTlib $user, $pwd, $server, $appname, 
                  {CON_PROPS => {CS_BULK_LOGIN => CS_TRUE }};

You then initialise the bulk-copy operation by calling blk_init(), send 
rows with blk_rowxfer(), commit rows with blk_done(), and end with a 
call to blk_drop() to clean up resources held during the bulk-copy operation.


Routines:

=over 4

=item $ret = $dbh->blk_init($table, $num_cols, $has_identity, $id_column)

This initializes a bulk-copy operation for the table $table. $num_cols
should be set to the number of columns of the target table. $has_identity
should be set if the target table has an identity column B<and> you provide
the identity values as part of your blk operation (i.e. if $has_identity is 
set, then the I<identity insert> option is set on the table). If you 
want to let the server set the identity values during the insert then leave
$has_identity I<off>, set $id_column to the column number of the identity 
column (remember that the first column is column 1, the second is 2, etc), 
and pass I<undef> as the values for that column.

See the CTlib/t/xblk.t test script for an example of both letting the
server set the identity values and specifying them directly.

Returns CS_SUCCEED or CS_FAIL.

=item $ret = $dbh->blk_rowxfer($data)

Send one row to the server. $data is an array reference, where each element
is the data for a column in the row.

Returns CS_SUCCEED or CS_FAIL.

=item $ret = $dbh->blk_done($type, $rows)

Commit a batch of rows, or all rows sent with blk_rowxfer(). $type should 
be one of CS_BLK_ALL, CS_BLK_BATCH or CS_BLK_CANCEL. As a side effect sets
$rows to the number of rows affected.

Returns CS_SUCCEED or CS_FAIL.

=item $dbh->blk_drop()

Free internal resources after a Bulk-copy operation has been performed.

=back

=head2 EXPERIMENTAL Asynchronous Routines

The following routines allow you to make asynchronous calls to the
server. The implementation is experimental, and might change in the
future, so please use with care.

Before attempting to use this feature you should read the "Asynchronous
Programming" chapter in the Sybase OpenClient - Client Library manuals 
to understand the concepts. Sybase::CTlib only provides a thin layer
over the Sybase API.

The Sybase API knows about I<synchronous> (the default), I<deferred IO> and 
I<async IO> modes. In the synchronous mode database requests block until
a response is available. In deferred IO mode database requests return 
immediately (with a return code of CS_PENDING), and you check for
completion with the ct_poll() call. In async IO mode the API uses
a I<completion callback> to notify the application of pending data
for async operations.

A connection can be set to async or deferred IO modes when it is idle,
via the ct_con_props() call:

	$dbh->ct_con_props(CS_SET, CS_NETIO, CS_DEFER_IO, CS_INT_TYPE);

Operations will now run in deferred mode, and the perl code will need
to be adapted somewhat to handle the async behaviour. Specifically, 
ct_results() won't set $restype correctly - this value will only get set
when ct_results() completes, which means that it has to be retrieved with
an alternative method. The same is true for ct_fetch(), which can't be used
in async or deferred modes.

Here is a simple (well, as simple as I can make it!) example of using the 
deferred IO mode:

   my $ret = $dbh->ct_con_props(CS_SET, CS_NETIO, CS_DEFER_IO, CS_INT_TYPE);

   $ret = $dbh->ct_execute("select * from large_table where val = 100");
   my $compid;
   my $compstatus;
   my $foo;

   $ret = $dbh->ct_poll(CS_NO_LIMIT, $foo, $compid, $compstatus);
   my $restype;
   while(($ret = $dbh->ct_results($restype)) == CS_SUCCEED || $ret == CS_PENDING) {
      if($ret == CS_PENDING) {
	 $ret = $dbh->ct_poll(CS_NO_LIMIT, $foo, $compid, $compstatus);
	 last if $compstatus == CS_END_RESULTS;
	 $restype = $dbh->{LastRestype};
      }
      next unless $dbh->ct_fetchable($restype);
      $ret = $dbh->as_describe($restype);
      while(($ret = $dbh->as_fetch) == CS_SUCCEED || $ret == CS_PENDING) {
	if($ret == CS_PENDING) {
	    $ret = $dbh->ct_poll(CS_NO_LIMIT, $foo, $compid, $compstatus);
	}
	my $d = $dbh->as_fetchrow;
	print "data: @$d\n";

	last if $compstatus == CS_END_DATA;
      }
   }

As you can see this snippet is quite a bit more complicated than the
corresponding synchronous code would be. In this case we've used the 
CS_NO_LIMIT parameter to ct_poll(), meaning that ct_poll() will block 
until data is available (which of course defeats the purpose of running
an async query, but serves to illustrate the API).

Note the use of $dbh->{LastRestype} to fetch the $restype value set
by ct_results() after that call completes.

Also, note the use of $dbh->as_describe($restype). This is a call that is 
normally done internally in ct_results(), but which needs to be called
separately in this case, and which allocates appropriate data structures
internally for the columns being returned.

The ct_fetch() call is replaced by as_fetch(), moving the actual data 
fetch to as_fetchrow().

If you define a completion callback then this will also be called - with 
the $dbh, $func (set to a symbolic value for the operation that completed, 
and $status (the return code for the operation).

For example, when ct_results() completes $func will be set to CT_RESULTS
and $status will be set to CS_SUCCEED, or CS_END_RESULTS if there are no more
results to be processed.

Most Sybase::CTlib calls can be called from within the completion callback,
if necessary.

To enable fully async mode, you need to have a completion callback 
registered, and you need to also enable async notifications, as well as
setting up a timeout for requests, via ct_config(CS_TIMEOUT).

There are two very simple scripts in the eg/ directory (ctpoll.pl and
ctasync.pl) which should illustrate the API somewhat.

=over 4

=item Completion Callbacks

A I<completion callback> is a perl subroutine that takes three arguments
(a database handle, and two integers ($func, $status). The callback
is enabled via the ct_callback() call:

    ct_callback(CS_COMPLETION_CB, \&my_completion_cb);

It is either invoked when ct_poll() is notified of a request completion,
or by the OpenClient API directly in full async mode.

It should return CS_SUCCEED, or CS_PENDING if you've initiated a new
async request from within the callback

=item $ret = $dbh->ct_con_props($action, $property, $value, $type)

You can set or retrieve connection properties with this call. In
particular you set async mode like this:

	$dbh->ct_con_props(CS_SET, CS_NETIO, CS_ASYNC_IO, CS_INT_TYPE);


=item $ret = $dbh->ct_poll($milliseconds, $conn, $compid, $compstatus)

Check for completion of an operation. $milliseconds determines how long
ct_poll should wait before timing out, where 0 means return immediately, 
and CS_NO_LIMIT means don't return until a pending operation has completed.

If the call is invoked as Sybase::CTlib->ct_poll() it will return the
first connection that has a completed operation in the $conn variable. 
The $compid value is a symbolic value identifying the type of operation 
that has completed, and $compstatus is the return value of that operation.

=item $ret = $dbh->as_describe($restype)

This needs to be called before fetching rows on an async or deferred IO
connection. This allocates the appropriate internal buffers for the
data columns returned in the query.

=item $ret = $dbh->as_fetch

Performs an async fetch - does not actually return any row data.

=item $ret = $dbh->as_fetchrow([$doAssoc])

Returns a row of data after as_fetch() is marked as completed. This call
returns a static array (or a static hash, if $doAssoc is set). So each call
to this routine B<overwrites> the data that you just fetched. If you need
to save the data you must make a copy of the array.

=back

=head2 EXAMPLES

    #!/usr/local/bin/perl

    use Sybase::CTlib;

    ct_callback(CS_CLIENTMSG_CB, \&msg_cb);
    ct_callback(CS_SERVERMSG_CB, "srv_cb");
    $uid = 'mpeppler'; $pwd = 'my-secret-password'; $srv = 'TROLL';

    $X = Sybase::CTlib->ct_connect($uid, $pwd, $srv);

    $X->ct_execute("select * from sysusers");

    while(($rc = $X->ct_results($restype)) == CS_SUCCEED) {
	next if($restype == CS_CMD_DONE || $restype == CS_CMD_FAIL ||
	        $restype == CS_CMD_SUCCEED);
	if(@names = $X->ct_col_names()) {
	     print "@names\n";
	}
	if(@types = $X->ct_col_types()) {
	     print "@types\n";
	}
	while(@dat = $X->ct_fetch) {
	     print "@dat\n";
	}
    }

    print "End of Result Set\n" if($rc == CS_END_RESULTS);
    print "Error!\n" if($rc == CS_FAIL);

    sub msg_cb {
        my($layer, $origin, $severity, $number, $msg, $osmsg, $dbh) = @_;

	printf STDERR "\nOpen Client Message: (In msg_cb)\n";
	printf STDERR "Message number: LAYER = (%ld) ORIGIN = (%ld) ",
    	       $layer, $origin;
	printf STDERR "SEVERITY = (%ld) NUMBER = (%ld)\n",
	       $severity, $number;
	printf STDERR "Message String: %s\n", $msg;
	if (defined($osmsg)) {
	    printf STDERR "Operating System Error: %s\n", $osmsg;
	}
	CS_SUCCEED;
    }

    sub srv_cb {
        my($dbh, $number, $severity, $state, $line, $server,
	   $proc, $msg) = @_;

    # If $dbh is defined, then you can set or check attributes
    # in the callback, which can be tested in the main body
    # of the code.

	printf STDERR "\nServer message: (In srv_cb)\n";
	printf STDERR "Message number: %ld, Severity %ld, ",
	       $number, $severity;
    	printf STDERR "State %ld, Line %ld\n", $state, $line;

	if (defined($server)) {
	    printf STDERR "Server '%s'\n", $server;
	}

	if (defined($proc)) {
	    printf STDERR " Procedure '%s'\n", $proc;
	}

	printf STDERR "Message String: %s\n", $msg;  CS_SUCCEED;
    }

=head2 ATTRIBUTES

The behavior of certain aspects of the Sybase::CTlib module can be
controlled via global or connection specific attributes. The global
attributes are stored in the %Sybase::CTlib::Att variable, and the
connection specific attributes are stored in the $dbh. To set a global
attribute, you would code

     $Sybase::CTlib::Att{'AttributeName'} = value;

and to set a connection specific attribute you would code

     $dbh->{"AttributeName'} = value;

B<NOTE!!!> Global attribute setting changes do not affect existing
connections, and changing an attribute inside a ct_fetch() does B<not>
change the behavior of the data retrieval during that ct_fetch()
loop.

The following attributes are currently defined:

=over 8

=item UseDateTime

If TRUE, then keep B<DATETIME> data retrieved via ct_fetch() in native
format instead of converting the data to a character string. Default:
FALSE.

=item UseMoney

If TRUE, keep B<MONEY> data retrieved via ct_fetch() in native format
instead of converting the data to character string. Default: FALSE.

=item UseNumeric

If TRUE, keep B<NUMERIC> or B<DECIMAL> data retrieved via ct_fetch()
in native format, instead of converting to a character string. Default: FALSE.

=item UseChar

This is a no-op. As of sybperl 2.16 B<NUMERIC>, B<DECIMAL> and B<MONEY> 
data retrieved via ct_fetch() is stored as character strings by default
to ensure no loss of precision.

=item MaxRows

If non-0, limit the number of data rows that can be retrieve via
ct_sql(). Default: 0.

=back

=head1 Using ct_get_data() and ct_send_data() to do raw TEXT processing

As of release 2.09_06 of B<sybperl> Sybase::CTlib includes the ability
to process B<TEXT> and B<IMAGE> datatypes using perl versions of 
ct_get_data() and ct_send_data(). Using these functions is a little
tricky, however.

B<NOTE:> This discussion applies equally to B<TEXT> and B<IMAGE> datatypes,
even if only one or the other is mentioned in the text.

=head2 Retrieving TEXT columns using ct_get_data()

First let's see how ct_get_data() is implemented to retrieve B<TEXT>
or B<IMAGE> data types in raw format, and (possibly) in retrieve large 
data items in smaller, more manageable pieces.

First, it is essential that the B<TEXT> columns appear
I<last> in the select statement (there can be several TEXT columns
in the statement, but they must appear I<after> any regular columns).

For example:

	select userID, userName, msgText
	from   messageTable

(where I<msgText> is a TEXT column) would work fine.

You issue the query in the normal way:

	$dbh->ct_execute("select userID, userName, msgText
			from messageTable where userID = 5");

You call ct_results() in the normal way, with the exception that you
pass the $textBind param as B<FALSE> to prevent ct_fetch() from 
returning the TEXT column.

If there are fetchable results, you call ct_fetch() to retrieve the 
normal data columns, and for each row you then call ct_get_data()
to retrieve the TEXT column(s).

For example:

	$dbh->ct_execute("select userID, userName, msgText
			from messageTable where userID = 5");
	while($dbh->ct_results($restype, 0) == CS_SUCCEED) {
	    next unless $dbh->ct_fetchable($restype);
	    while(@row = $dbh->ct_fetch) {
		($ret, $msg) = $dbh->ct_get_data(3);
	    }

=head2 Updating TEXT columns using ct_send_data()

This operation is a little more complicated. Essentially, you must
first select the column that you wish to update to obtain a valid 
I<text pointer> (via a call to ct_data_info(CS_GET)), then you 
initiate a CS_SEND_DATA_CMD command using ct_command(), you
set the new total length of the column via ct_data_info(CS_SET), 
send the data to the server via ct_send_data(), commit the operation
with ct_send(), and then process the results in the normal way
with ct_results() and ct_fetch().

For example, assuming the following table:

	create table blobtext(id numeric(5,0) identity,
			      data image)

We would update the I<data> column of a particular row like this:

	$dbh->ct_execute("select id, data from testdb..blobtest where id = 5");
	my $restype;
	while($dbh->ct_results($restype) == CS_SUCCEED) {
	    next unless($dbh->ct_fetchable($restype));
	    my @dat;
	    while(@dat = $dbh->ct_fetch) {
		$dbh->ct_data_info(CS_GET, 2);
	    }
	}

	my $data = "This is the new content that we want to place in
	            the 'data' column for the row";
	my @dat;
	$dbh->ct_command(CS_SEND_DATA_CMD, '', CS_UNUSED, CS_COLUMN_DATA);
	$dbh->ct_data_info(CS_SET, 2, {total_txtlen => length($data)});
	$dbh->ct_send_data($data, length($data));
	$dbh->ct_send;
	while($dbh->ct_results($restype) == CS_SUCCEED) {
	    next unless $dbh->ct_fetchable($restype);

	    while(@dat = $dbh->ct_fetch) {
		print "@dat\n";
	    }
	}

The last ct_fetch() will return one column - the new text pointer. At the
moment there is no way to make use of this text pointer directly.

You can also update TEXT fields on a set of rows by using a second
connection and performing the ct_send_data() in a nested loop:

	$dbh->ct_execute("select id, data from testdb..blobtest");
	my $restype;
	while($dbh->ct_results($restype) == CS_SUCCEED) {
	    next unless($dbh->ct_fetchable($restype));
	    my @dat;
	    while(@dat = $dbh->ct_fetch) {
		$dbh->ct_data_info(CS_GET, 2);

		# get the data to be updated, based on the 'id' column
		# presumably the get_data() function knows what to do :-)
		my $data = get_data($dat[0]);
		$dbh2->ct_command(CS_SEND_DATA_CMD, '', CS_UNUSED, CS_COLUMN_DATA);
		# copy the CS_IODESC struct from $dbh to $dbh2, and
		# set 'total_txtlen' to the correct value.
		$dbh2->ct_data_info(CS_SET, 2, {total_txtlen => length($data)}, $dbh);
		$dbh2->ct_send_data($data, length($data));
		$dbh2->ct_send;
		while($dbh2->ct_results($restype) == CS_SUCCEED) {
		    next unless $dbh2->ct_fetchable($restype);

		    while(@dat = $dbh2->ct_fetch) {
			print "@dat\n";
		    }
		}		
	    }
	}


=head1 Common Sybase::DBlib and Sybase::CTlib routines
        
=item $module_name::debug($bitmask)

Turns the debugging trace on or off. The $module_name should be one of
I<Sybase::DBlib> or I<Sybase::CTlib>. The value of $bitmask determines
which features are going to be traced. The following trace bits are
currently recognized:

=over 4

=item * TRACE_CREATE

Trace all CTlib and/or DBlib object creations.

=item * TRACE_DESTROY

Trace all calls to DESTROY.

=item * TRACE_SQL

Traces all SQL language commands - (i.e. calls to dbcmd(), ct_execute() or
ct_command()). 

=item * TRACE_RESULTS

Traces calls to dbresults()/ct_results().

=item * TRACE_FETCH

Traces calls to dbnextrow()/ct_fetch(), and traces the values that are
pushed on the stack.

=item * TRACE_CUSROR

Trace calls to ct_cursor() (not available in Sybase::DBlib).

=item * TRACE_PARAMS

Trace calls to ct_param() (not implemented in Sybase::DBlib).

=item * TRACE_OVERLOAD

Trace all overloaded operations involving DateTime, Money or Numeric
datatypes.

=back

Two special trace flags are B<TRACE_NONE>, which turns off debug
tracing, and B<TRACE_ALL> which (you guessed it!) turns everything on.

The traces are pretty obscure, but they can be useful when trying to
find out what is I<really> going on inside the program.

For the B<TRACE_*> flags to be available in your scripts, you must
load the Sybase::??lib module with the following syntax:

     use Sybase::CTlib qw(:DEFAULT /TRACE/);

This tells the autoloading mechanism to import all the I<default>
symbols, plus all the I<trace> symbols.


=head1 Special handling of DATETIME, MONEY & NUMERIC/DECIMAL values

B<NOTE:> This feature is turned off by default for performance
reasons.  You can turn it on per datatype and B<dbh>, or via the
module attribute hash (%Sybase::DBlib::Att and %Sybase::CTlib::Att).

The Sybase::CTlib and Sybase::DBlib modules include special features
to handle B<DATETIME>, B<MONEY>, and B<NUMERIC/DECIMAL> (I<CTlib>
only) values in their native formats correctly. What this means is
that when you retrieve a date using ct_fetch() or dbnextrow() it is
not converted to a string, but kept in the internal format used by the
Sybase libraries. You can then manipulate this date as you see fit,
and in particular 'crack' the date into its components.

The same is true for B<MONEY> (and for I<CTlib> B<NUMERIC> values),
which otherwise are converted to floating point values, and hence are 
subject to loss of precision in certain situations. Here they are
stored as B<MONEY> values, and by using operator overloading we can
give you intuitive access to the cs_calc()/dbmnyxxx() routines.

This feature has been implemented by creating new classes in both
Sybase::DBlib and Sybase::CTlib:
B<Sybase::DBlib::DateTime>, B<Sybase::DBlib::Money>,
B<Sybase::CTlib::DateTime>, B<Sybase::CTlib::Money> and
B<Sybase::CTlib::Numeric> (hereafter referred to as B<DateTime>,
B<Money> and B<Numeric>). All the examples below use the I<CTlib>
module. The syntax is identical for the I<DBlib> module, except that
the B<Numeric> class does not exist.
 
To create data items of these types you call:

   $dbh = new Sybase::CTlib user, password;
   ...  # code deleted
   # Create a new DateTime object, and initialize to Jan 1, 1995:
   $date = $dbh->newdate('Jan 1 1995');

   # Create a new Money object
   $mny = $dbh->newmoney;	# Default value is 0

   # Create a new Numeric object
   $num = $dbh->newnumeric(11.111);

The B<DateTime> class defines the following methods:

=over 8

=item $date->str

Convert to string (calls cs_convert()/dbconvert()).

=item @arr = $date->crack

'Crack' the date into its components.

=item $date->cmp($date2)

Compare $date with $date2.

=item $date2 = $date->calc($days, $msecs)

Add or substract $days and $msecs from $date, and returns the new
date.

B<NOTE:> The minimal interval that Sybase understands is 1/300th of second,
so amounts of less than 3 $msecs will NOT be visible.

=item ($days, $msecs) = $date->diff($date2)

Compute the difference, in $days and $msecs between $date and $date2.

=item $val = $date->info($datepart)

Calls cs_dt_info to return the string representation for a
datepart. Valid dateparts are CS_MONTH, CS_SHORTMONTH and CS_DAYNAME.

B<NOTE:> Not implemented in I<DBlib>.

=item $time = $date->mktime

=item $time = $date->timelocal

=item $time = $date->timegm

Converts a Sybase B<DATETIME> value to a Unix B<time_t> value. The
B<mktime> and B<timelocal> methods assumes the date is stored in local
time, B<timegm> assumes GMT. The B<mktime> method uses the POSIX
module (note that unavailability of the POSIX module is not a fatal error).

=back
    
Both the B<str> and the B<cmp> methods will be called transparently
when they are needed, so that

    print "$date"

will print the date string correctly, and

    $date1 cmp $date2

will do a comparison of the two dates, not the two strings.

B<crack> executes cs_dt_crack()/dbdatecrack() on the date value, and
returns the following list:

    ($year, $month, $month_day, $year_day, $week_day, $hour,
	$minute, $second, $millisecond, $time_zone) = $date->crack;

Compare this with the value returned by the standard Perl function
localtime():

    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
                                                   localtime(time);

In addition, the values returned for the week_day can change depending
on the locale that has been set.

Please see the discussion on cs_dt_crack() or dbdatecrack() in the
I<Open Client / Open Server Common Libraries Reference Manual>, chap. 2.

The B<Money> and B<Numeric> classes define these methods:

=over 8

=item $mny->str

Convert to string (calls cs_convert()/dbconvert()).

=item $mny->num

Convert to a floating point number (calls cs_convert()/dbconvert()).

=item $mny->cmp($mny2)

Compare two Money or Numeric values.

=item $mny->set($number)

Set the value of $mny to $number.

=item $mny->calc($mny2, $op)

Perform the calculation specified by $op on $mny and $mny2. $op is one
of '+', '-', '*' or '/'.

=back

As with the B<DateTime> class, the B<str> and B<cmp> methods will be
called automatically for you when required. In addition, you can
perform normal arithmetic on B<Money> or B<Numeric> datatypes without
calling the B<calc> method explicitly.

B<CAVEAT!> You must call the B<set> method to assign a value to a
B<Money/Numeric> data item. If you use

      $mny = 4.05

then $mny will lose its special B<Money> or B<Numeric> behavior and
become a normal Perl data item.

When a new B<Numeric> data item is created, the I<SCALE> and
I<PRECISION> values are determined by the initialization. If the data
item is created as part of a I<SELECT> statement, then the I<SCALE>
and I<PRECISION> values will be those of the retrieved item. If the
item is created via the B<newnumeric> method (either explicitly or
implicitly) the I<SCALE> and I<PRECISION> are deduced from the
initializing value. For example, $num = $dbh->newnumeric(11.111) will
produce an item with a I<SCALE> of 3 and a I<PRECISION> of 5. This is
totally transparent to the user.

=head1 BUGS

The Sybase::DBlib 2PC calls have not been well tested.

There is a (approximately) 300 byte memory leak in the newdbh() function
in Sybase/DBlib.xs and Sybase/CTlib.xs. This function is called when a 
new connection is created.
I have not been able to locate the real cause of the leak so far. Patches
that appear to solve the problem are welcome!

I have a simple bug tracking system at http://www.peppler.org/cgi-bin/bug.cgi .
You can use it to check for known bugs, or to submit
new ones.

You can also look for new versions/patches for sybperl in 
http://www.peppler.org/downloads.

=head1 ACKNOWLEDGMENTS

Larry Wall - for Perl :-)

Tim Bunce & Andreas Koenig - for all the work on MakeMaker

=head1 AUTHORS

Michael Peppler E<lt>F<mpeppler@peppler.org>E<gt>

Dave Bowen & Amy Lin for help with Sybase::CTlib.

Jeffrey Wong for the Sybase::DBlib DBMONEY routines.

W. Phillip Moore E<lt>F<Phil.Moore@msdw.com>E<gt> for the nsql() method.

Numerous folks have contributed ideas and bug fixes for which they
have my undying thanks :-) 

The sybperl mailing list E<lt>F<sybperl-l@peppler.org>E<gt> is the
best place to ask questions.

=cut
