Sybtcl is an extension for Tcl that provides access to Sybase, the relational database management system from Sybase, Inc. With Sybtcl, a Tcl program can retrieve (select) data from the database server, insert new rows, update or delete existing rows. Sybtcl can also access advanced Sybase features, such as reading or writing text or image data and execute stored procedures on the database server. Sybtcl is compatible with Sybase Server version 4.2 through System 10. Sybase's DB-Lib ("Open Client") libraries are required to build Sybtcl.
While there is nothing that Sybtcl can do that can't be accomplished with C and DB-Lib, Sybtcl promotes creative uses and rapid development due to Tcl's environment. Interactive applications can be developed quickly using Tcl/Tk. Database administration procedures such as site specific backup/restore that are often cobbled together with Shell, Awk, and Isql (Sybase's command line SQL processor) can be written in a single language. Other uses of Sybtcl reported by users include: real-time server performance monitoring; interface to HTTP servers to provide World Wide Web users with access to campus telephone listings; a C++ code generation tool providing database access for a cross-platform GUI environment; ad-hoc user report generation.
Sybtcl adds several new commands to Tcl. All Sybtcl commands are prefixed with "syb" to differentiate Sybtcl commands from other Tcl commands. Sybtcl also adds a global array variable, $sybmsg, that provides the Tcl program with information about the execution of various Sybtcl commands. Various indexes in the sybmsg array are set with information about the success of failure of Sybtcl commands. The sybmsg array can be accessed anytime after a Sybtcl command.
Here is a short example of Sybtcl using the "pubs2" sample database shipped with the Sybase product. In this example, we will connect to the Sybase server, access the "pubs2" database, and selects rows from the "authors" table:
tclsh> sybconnect mysybaseuserid mypassword MYSERVER
sybtcl0
tclsh> sybuse sybtcl0 pubs2
tclsh> sybsql sybtcl0 "select au_fname, au_lname from authors"
REG_ROW
tclsh> sybnext sybtcl0
Abraham Bennet
tclsh> sybnext sybtcl0
Reginald Blotchet-Halls
tclsh> sybclose sybtcl0
Basic Sybtcl Commands
Sybase uses the client/server model exclusively. The database server accepts connections from clients wishing to access the database. Sybase client programs, such as Sybtcl, communicate using a protocol (provided by DB-Lib) to the server; the server in turn performs the database access and returns the results to the client. In order for Sybtcl to access the database, you must connect to the database server with a user id and password. Sybconnect is the command in Sybtcl to make the connection.
sybconnect userid password ?server? ?application? ?interface-file?
Sybconnect requires two parameters, the Sybase user id and password.
Three optional parameters maybe specified. Server is a specific
Sybase server to which the connection is made. If the server is not specified
on the sybconnect command, Sybtcl will attempt to connect to the
default Sybase server (environment variable "DSQUERY".) Application is
any string that is passed to the server and is used by the server for system
administration display. Sybtcl reads server location information, a hostname
and TCP port number, from a file named "interfaces" in the Sybase home
directory (environment variable $SYBASE.) An alternate server interface file
may be specified with the interface-file parameter on
sybconnect.
Upon a successful connection, sybconnect returns a connection "handle" in the form of a string. A connection handle is similar in purpose to a file descriptor or the value returned by the Tcl open command. The Sybtcl connection handle is used with all other Sybtcl commands. Several connections to the same or different Sybase servers can be open at the same time, each connection uses a separate handle. Sybtcl handles are in the format "sybtcl0", with the numeric suffix incrementing with each new connection. Normally, the handle would be saved into some Tcl variable for later access:
set handle [sybconnect $sybuserid $sybpassword]
If you plan to use the same handle for the life of your program, be sure to specify the variable as global before using the variable in a procedures, and in all other procedures where you will be using Sybtcl commands. In the case of several simultaneous connections, you would save each handle into separate variables:
global select_handle update_handle other_handle
set select_handle [sybconenct $sybuserid $sybpassword]
set update_handle [sybconnect $sybuserid $sybpassword]
set other_handle [sybconnect $sybuserid $sybpassword SERVER2]
If a connection cannot be made to the Sybase server, either because of an invalid user id/password combination, server unavailable, etc., sybconnect will cause a Tcl error to be raised. Information on why a connection cannot be made is available in $sybmsg(dberrstr). In the rest of this chapter, $handle will be used to signify a valid connection from sybconnect.
In the Sybase architecture, a single server may be responsible for managing several databases. Sybtcl provides the sybuse command to either report the database currently in use, or to cause another database to be accessed.
sybuse $handle ?database?
The result from sybuse will report the name of the database currently in use when executed without the optional database parameter. Each database user in Sybase in assigned a default database and uses that database upon a successful login to the Sybase server. Other databases may be accessed if permission has been granted by the Sybase administrator. Sybuse can be used to change to another database by specifying the optional database parameter:
puts "currently using database: [sybuse $handle]
sybuse $handle $newdatabase
puts "switched database to $newdatabase"
Sybtcl connection handles should normally be closed when the database connection is no longer needed, just as you would normally close an open file descriptor. Sybtcl provides the sybclose command to close a handle.
sybclose $handle
Executing sybclose ensures that the connection to the Sybase server is cleanly terminated. Each Sybtcl connection occupies a slot in an internal data structure. Properly closing a handle frees its corresponding slot, allowing the slot to be available for another new connection with sybconnect.
So far, we've demostrated connecting to a Sybase server, accessing databases in the server, and disconnecting. Now it's time for some real action. Sybtcl provides two commands that do most of the data manipulation: sybsql and sybnext.
Sybsql is the command that sends an SQL statement to the Sybase server for execution. Any valid Sybase SQL statement can be sent using sybsql:
* ordinary SQL such as select, insert, update, delete, create, grant.
* any of Sybase's Transact-SQL statements such as execute, begin, declare, etc.
* any Sybase database administration commands, dump, checkpoint, etc.
As with any other Transact-SQL statement, multiple SQL statements may be executed in the same sybsql command.
sybsql $handle $sql-statement ?async?
Sybsql passes the SQL statement in its entirety to the Sybase server, aside from normal Tcl variable and command substitution. It's very convenient to use Tcl's variable substitution to construct an SQL command:
puts "search for what name?"
gets stdin searchname
sybsql $handle "select * from authors where \
au_lname like '${searchname}'"
Sybsql will cause a Tcl error to be raised if the Sybase server rejects the SQL statement, either for improper syntax or if the SQL statement references tables or column names that do not exist. Again, it's prudent to enclose any sybsql command that may have user input inside of Tcl's catch command. Additionally, where limited external input is used to construct an SQL statement as in the example above, it may be necessary to pre-process the input. If the user were to enter:
O'Reilly
in response to the prompt, the resulting SQL statement would become invalid because the single quote after the "O" would specify the end of the literal string, leaving "Reilly'" as an invalid SQL statement. In such a case you might want consider the SQL quoting and wild-card characters ("%","_","[]") when building an SQL statement with string literals. To quote the quote character in SQL, you specify two quote characters. Regsub is handy to ensure that a literal string is properly quoted:
regsub -all ' $searchname '' searchname
Sybase extends the ANSI SQL wildcard characters by including "[]", matching any character or range of characters appearing between the brackets. Of course, this wildcard specification can also be interpreted by Tcl as a command execution. If your SQL statement includes "[]", be sure to either quote the brackets, either with a backslash, or enclose the entire SQL statement in brackets. For example, to select authors with last names beginning with A through M, either quoting method would suffice:
sybsql $handle "select * from authors where au_lname like '\[A-M\]%'"
sybsql $handle {select * from authors where au_lname like '[A-M]%'}
Normally, sybsql will wait until the SQL statement has finished processing on the server before returning. An optional flag async may be specified on sybsql. Async causes the sybsql command to return immediately. The sybpoll command can then check on the progress of the SQL command. Async is useful during long running SQL commands to allow the Tcl program to continue processing, perhaps giving the user feedback or allowing other processing to continue.
Assuming a valid SQL statement was sent to the server, sybsql can return different values depending on the result of the command:
* REG_ROW - at least one row of data is available from the first result set in the case of executing a "select" or a stored procedure that returned rows.
* NO_MORE_ROWS - the SQL executed properly, but no rows were returned by the first result set, or possibly "insert", "update", or "delete" was executed (again, no rows were returned.)
* PENDING - the "async" flag was specified, the sybpoll can be executed to check if the SQL command has finished on the server.
These result codes are also stored in the sybmsg global array.
In Sybase terminology, result sets are the term for each separate set of rows returned from separate "select" statements. Note that sybsql doesn't return actual data rows, just the indication that rows are available.
Sybase buffers result rows in the dataserver unitl requested. To retrieve data rows from the server as the result of a SQL statement, Sybtcl provides the sybnext command.
sybnext $handle ?tcl-statements?
Sybnext retrieves the next row available, and returns the results in the form of a Tcl list. List elements correspond to the columns selected in the same order as specified in a "select" statement. The columns returned can then easily be picked apart with standard Tcl list oriented commands, lindex, lrange, etc.
sybsql $handle "select * from titles"
set row [sybnext $handle]
puts "first column is: [lindex $row 0]"
Sybnext converts all SQL datatypes to printable strings, in keeping with the Tcl model that all data are strings. This is not normally a problem for rows returned in Sybtcl, except the Sybase datatypes binary and varbinary, in which the hexadecimal representation is returned.
Each execution of sybnext will return the next row from the current result set. In addition to returning rows, sybnext sets the variable $sybmsg(nextrow) to a status indicating what was returned. Possible values are:
* REG_ROW - the row returned was from a "select" SQL statement.
* NO_MORE_ROWS - the last row from the current result set has been previously returned. The sybnext command returns a null string (""). If another result set is expected, the next sybnext command will return the first row from the next result set.
* n - an integer compute row id, indicating that the row retrieved was from a Transact-SQL "compute" statement. Compute statements are numbered beginning with 1 and increase with each compute statement appearing in the query.
* NO_MORE_RESULTS - the final result set from the last sybsql command has been retrieved. The sybnext command returns a null string ("").
If the SQL was executed with the async option on sybsql, then sybnext will wait until
results are available before returning. Also, if the SQL statement was in error, the sybnext command will fail, since this is the first opportunity to report the error.
The following diagram represents the possible branches when SQL statements are executed with sybsql and retrieved with sybnext:
Figure Sybtcl-1. Sybsql/Sybnext processing
In the case of executing an SQL "select" statement, each row returned would be further processed in some fashion. For example, to print a list of authors' names and birthdates from the "pubs2" database:
puts [format "%-30.30s %s" "Author" "Birthdate"]
sybsql $handle "select name, birthdate from authors"
set row [sybnext $handle]
while {$sybmsg(nextrow) != "NO_MORE_ROWS"} {
puts [format "%-30.30s %s" \
[lindex $row 0] [lindex $row 1]]
set row [sybnext $handle]
}
Since it is very common programming construct to retrieve and process all the rows from a query, sybnext can accept an optional argument that is a block of Tcl statements to be executed for each row returned in a result set. Sybnext internally takes care of the while loop, retrieving rows and checking for the end-of-rows condition. The example above can be rewritten as:
puts [format "%-30.30s %s" "Author" "Birthdate"]
sybsql $handle "select name, birthdate from authors"
sybnext $handle { puts [format "%-30.30s %s" @1 @2] }
Columns from each row are specified using a special substitution notation, prefixed with "@". @1 refers to the first column, @2 refers to the second column, and so on. @0 is the entire row as a Tcl list, just as sybnext would normally return. There is no limit to the number of times a column may be specified, or the number of Tcl statement executed in the block.
There are a few extra rules to remember when using the tcl-statements option. First, don't try to reference a column beyond the number of columns that are returned. Second, columns are substituted into the tcl-statements in a way to ensure that the tcl-statements after substituting the column data are still valid Tcl commands, e.g., a proper Tcl list. Since the data returned in the column may contain Tcl quoting characters, the entire substitution maybe enclosed with Tcl quoting, with adjacent spaces inserted. If you need to format the columns for printing, use the Tcl format command as in the example above, or assign the column values into Tcl variables with the set command. Last, if any commands in the tcl-statements block fail, sybnext will raise a Tcl error.
The Tcl commands break and continue may be used in the tcl-statements block the same way they are used in while or for. Break will cause the sybnext command to return. Any further rows remaining in the query can be retrieved with a subsequent sybnext command. Continue will cause any remaining Tcl commands to be skipped and continue processing with the next row. The following example counts authors names that begin with "A" through "L", printing only the first 5:
sybsql $handle "select au_lname from authors \
order by au_lname"
set num_rows 0
sybnext $handle {
incr num_rows
if {[string compare @1 M] == 1} { break }
if {$num_rows > 5} { continue }
puts @0
}
puts "number authors with names beginning A-L: $num_rows"
Beside retrieving data from the Sybase server, Sybtcl also can retrieve information about the names of columns returned, their datatype, and length. Sybcols returns a Tcl list of the names of the columns returned by sybnext.
sybcols $handle
Sybcols can be used after a sybsql or sybnext command. The names of the columns returned are in the same order as the data values returned by sybnext. The names are the actual column names as defined in the tables from which the columns are retrieved, or any column alias name that may have been coded as part of the "select" statement. Sybase does impose two exceptions to this rule: the name returned by an aggregate function (sum, count, min, max, avg) is a null string "", and the name returned by a "compute" statement includes the function.
tclsh> sybsql $handle {
select lastname = au_lname from authors
where au_id ='756-30-7391'
select count(*) from titles
select royaltyper from titleauthor where au_ord = 3
compute min(royaltyper)
}
REG_ROW
tclsh> sybnext $handle {puts @0; puts [sybcols $handle]}
Karsen
lastname
tclsh> sybnext $handle {puts @0; puts [sybcols $handle]}
18
{}
tclsh> sybnext $handle {puts @0; puts [sybcols $handle]}
30
royaltyper
tclsh> sybnext $handle {puts @0; puts [sybcols $handle]}
30
min(royaltyper)
tclsh>
Beside getting column names, sybcols also sets the column datatypes and lengths in the
sybmsg global array. $sybmsg(coltypes) is a Tcl list of the datatypes corresponding to the columns, $sybmsg(collengths) is a Tcl list of the data lengths. Datatypes can be any of Sybase's datatype (char, text, binary, image, tinyint, smallint, int, float, real, bit. money, decimal, numeric, smallmoney, datetime, and smalldatetime). The column lengths are the internal data lengths (int = 4, real = 8, etc.); for "char" datatypes, the length returned is the maximum length defined in the table, not the column lengths of the last row retrieved.
tclsh> sybsql $handle {select from title, type, price, pubdate
from titles}
REG_ROW
tclsh> sybcols $handle
title type price pubdate
tclsh> puts $sybmsg(coltypes)
char char money datetime
tclsh> puts $sybmsg(collengths)
80 12 8 8
For SQL statements that have been executed using the async option on sybsql, the sybpoll command can be used to check on the status of the command. Sybpoll allows the Tcl program to check if executing sybnext will cause the program to block until results are available:
sybpoll $handle ?timeout? ?all?
The timeout value is an integer number of the maximum number of milliseconds sybpoll should wait before returning. The default value for timeout is 0, which mean that sybpoll will return immediately with the status of the results. A timeout value of -1 means that sybpoll will wait to return until results are avialable. All may also be specified, in which all connection handles that have pending results are checked for status. The results from sybpoll are either a null string, which means results for the handle are not yet available, or a list of handles that have results available. Once results for a handle have been read with sybnext, that handle is not considered in any additional sybpoll commands.
set handle1 [sybconnect $id $password $server]
set handle2 [sybconnect $id $passwoprd $server]
sybsql $handle1 {select long_running_query ...} async
sybsql $handle2 {select another_long_query ...} async
# wait for at least one query to finish
set available [sybpoll $handle1 -1 all]
# could be one or both handles ready
foreach ready $available {
if {[string compare $ready $handle1] == 0} {
puts "results from handle1"
}
if {[string compare $ready $handle2] == 0} {
puts "results from handle2"
}
# get results for the ready handle
sybnext $ready {puts @0}
}
puts "handle not yet processed (if any) \
[sybpoll $handle 0 all] "
Note that when the all option is used the handle (or list of handles) returned may not be the same as the handle used on the sybpoll command, since that handle may not be available yet.
Another use of sybpoll is to allow the Tcl program to continue processing while a query is being performed. The following example provides a one second clock tick as feedback while the query is executing:
sybsql $handle {select long_running_query ...} async
puts "started query"
flush stdout
set timer 0
set avail [sybpoll $handle 1000]
while {[string length $avail] == 0} {
incr timer
puts -nonewline " $timer"
flush stdout
set avail [sybpoll $handle 1000]
}
puts "\n$timer total seconds to complete"
sybnext $handle {puts @0}
Sometimes it is desirable to stop processing rows before all rows have been retrieved from a query. Sybtcl provides the sybcancel command for such a case.
sybcancel $handle
The sybcancel command informs the server that all pending rows from the last sybsql command are to be discarded. Sybsql always performs an implicit sybcancel before each execution of SQL; sybcancel may be used explicitly at any time.
Advanced Sybtcl Commands
Sybase includes several advanced features in its database server product. Stored procedures, blocks of Transact-SQL code that execute on the server, are an important part of Sybase. Stored procedures are used for transactional processing, where an entire group of SQL statements are treated as an atomic unit. Changes made to the database in a stored procedure are "commited" as a unit, or may be "rolled back", in case any one SQL statement does not execute successfully. Stored procedures can return rows from "select" statements, "print" messages, "output" variables, and also return integer values.
Sybtcl fully supports the use of stored procedures. Sybsql is used to execute a stored procedure, just as you would send a "select" or "insert" statement to the server.
sybsql $handle "execute history_proc '5023'"
As mentioned before, sybnext is used to retrieved an rows generated within the stored procedure via "select". Stored procedures may also return values from a Transact-SQL three different ways:
* "output" parameters, a form of call-by-reference, where parameters passed to a stored procedure are updated with values generated by the procedure.
* "print" statements, normally used for textual messages generated inside the procedure.
* "return" statement, to return an integer value when the procedure finishes.
Output parameter values are not rows in the regular sense; Sybtcl provides an additional command to retrieve the output value(s).
sybretval $handle
Use of sybretval is somewhat specific, due to programming constraints with the Sybase DB-Lib interface. The return values can be accessed by sybretval only after any data rows have been retrieved with sybnext. If rows are generated by the procedure, sybnext must be called until "$sybmsg(nextrow) == NO_MORE_ROWS" to access the return values. If no rows were generated, sybsql will return "NO_MORE_ROWS", and sybretval can be called. The return code from the stored procedure (from the Transact-SQL "return" statement) is made available in $sybmsg(retstatus) after sybnext has exhausted any regular rows; sybretval should be called at this time to retrieve the output values.
Messages from the stored procedure "print" statements are accumulated in $sybmsg(msgtext) as they are generated. Should the stored procedure execute more than one "print" statement, newlines are used as delimiters between the messages.
tclsh> sybsql $handle {
create proc multiply
@x int, @y int, @product int output
as
begin
print "multiplying %1! times %2!", @x, @y
select @product = @x * @y
return(99)
end
}
NO_MORE_ROWS
tclsh> sybsql $handle {
declare @result int
exec multiply 3, 5, @result output
}
NO_MORE_ROWS
tclsh> puts $sybmsg(msgtext)
multiplying 3 times 5
tclsh> puts $sybmsg(retstatus)
99
tclsh> sybretval $handle
15
Another advanced feature of Sybase supported by Sybtcl is the storage and retrieval text or image data columns. Text or image datatypes are similar, each datatype holding up to 2 gigabytes. Text datatypes usually imply printable text, whereas image datatypes are completely arbitrary. Sounds, graphics, and MPEG streams are examples of image data. Because image and text datatypes may contain binary zeros (thus unable to be represented as a Tcl string), Sybtcl provides commands to read and write text or image data to and from files.
sybwritetext $handle table.column column_number filename ?nolog?
sybreadtext $handle filename
Sybwritetext is the Sybtcl command to write a file into a text or image data column. The Sybase DB-Library imposes a specific order or processing to write data into an image column. Sybwritetext requires that a preceding "select" statement be executed before writing to the text/image column. Thus, when inserting a new row into a table, the non-text/image columns should be inserted as usual with an "insert" SQL statement, followed by an "update" that sets the image column to NULL then the text/image column can be inserted with sybwritetext. The select statement should reference the text/image column and be constrained to return a single row. The sybwritetext command also requires the table and column names of the text/image column and the relative column position from the preceeding "select":
set sybmsg(maxtext) 2147483647
sybsql $handle {insert into au_pix(au_id)
values('123-45-6789')}
sybsql $handle {update au_pix set pic = NULL
where au_id = '123-45-6789'}
sybsql $handle {select pic from au_pix
where au_id = '123-45-6789'}
sybwritetext $handle au_pix.pic 1 image.gif
Sybwritetext will return the number of bytes written to the column. Sybwritetext will normally "log" writes to the server transaction log. In cases where logging is not necessary or undesirable, the nolog option may be specified. The Sybase administrator must have previously set the database option for "select into/bulkcopy" in order for the nolog option to function.
Sybreadtext is the compliment of sybwritetext; it retrieves the text/image data from the database and writes the contents into a file. Like sybwritetext, it must be preceded by an SQL select that returns one text or image column. Text and image columns can be retrieved by sybnext, just as ordinary columns, but there are a few problems with this approach. First, image columns are converted to a printable hexadecimal notation, which may require another conversion step to reconstruct the actual data values. Second, the column would be stored as an ordinary Tcl variable, with the risk that not enough memory could be allocated to hold the value. Sybreadtext eliminates these problems by retrieving the data in smaller chunks, and writes the data directly to an operating system file.
set sybmsg(maxtext) 2147483647
sybsql $handle {select copy from blurbs
where au_id = '486-29-1786'}
sybreadtext $handle blurb.txt
Sybreadtext limits the amount of text/image data retrieved, but the upper limit can be set prior to retrieval by setting the sybmsg array element maxtext. The default is 32768, which is the total amount of data that can be written to a file.
Server Information and Feedback
Besides the Sybase specific commands that are added to Tcl by Sybtcl, a global array is maintained and used by Sybtcl for information from the server, and to control various aspects of Sybtcl. Sybmsg is the name of the array, and you should always reference it as global before accessing it in any Tcl proc. We've already touched lightly on some of the elements in $sybmsg. Now we will examine each element in detail. The first to be discussed are the elements that can be set by the Sybtcl user and affects Sybtcl processing.
$sybmsg(nullvalue)
Nullvalue controls how Sybtcl represents columns containing NULL when using sybnext. The default value for $sybmsg(nullvalue) is "default" which converts numeric datatypes to "0" and all other datatypes to a null string "". For example, you might want to set nullvalue to an unlikely string to differentiate a NULL from a column that consists of whitespace characters.
tclsh> set sybmsg(nullvalue) <null>
tclsh> sybsql $handle {select 'not null string', convert(integer,NULL)}
REG_ROW
tclsh> sybnext $handle
{not null string} <null>
$sybmsg(floatprec)
Floatprec controls how Sybtcl returns columns containing float or real datatypes when using sybnext. The default value for $sybmsg(floatprec) is a null string, which causes float values to be returned with 17 digits of precision. Floatprec can be set to an integer between 1 and 17, representing the number of digits of precision:
tclsh> sybsql $handle {select convert(float,355.0/113.0)}
REG_ROW
tclsh> sybnext $handle
3.1415920000000002
tclsh> set sybmsg(floatprec) 4
tclsh> sybsql $handle {select convert(float,355.0/113.0)}
REG_ROW
tclsh> sybnext $handle
3.142
$sybmsg(dateformat)
Dateformat alters how date values are returned by sybnext. The default value for $sybmsg(dateformat) is a null string, which causes Sybtcl to convert dates to Sybase's native date string. Dateformat can be set to any string, and within the string certain symbols are replaced with various components of the date value. While any character can appear in the dateformat, there is no quoting facility to prevent unwanted substitution.
* YYYY - four digit year, 1900-
* YY - two digit year, 00-99
* MM - two digit month, 1-12
* MONTH - name of month, "January" - "December"
* MON - month abbreviation, "Jan" - "Dec"
* DD - two digit day of the month, 1-31
* hh - two digit hour, 0-23
* mm - two digit minute, 0-59
* ss - two digit second, 0-59
* dy - three digit day of the year, 0-365
* dw - two digit day of week, 1-7 (Mon-Sun)
tclsh> sybsql $handle {
select convert(datetime,'December 25, 1995') }
REG_ROW
tclsh> sybnext $handle
{Dec 25 1995 12:00:00:000AM}
tclsh> set sybmsg(dateformat) "dw MON DD, YY"
tclsh> sybsql $handle {
select convert(datetime,'December 25, 1995') }
REG_ROW
tclsh> sybnext $handle
{02 Dec 25 95}
$sybmsg(maxtext)
Maxtext limits the amount of text or image data that is retrieved from the server for both sybnext and sybreadtext. The default limit is 32768 bytes; maxtext can be set to the maximum allowed by Sybase, 2147483647. Any change to maxtext becomes effective on the next execution of sybsql.
The remainder of the elements in $sybmsg are informational only. Sybtcl resets the informational elements on every Sybtcl command. Any element not explicitly set by any particular Sybtcl command is set to a null string.
$sybmsg(handle)
Since the sybmsg array is shared among all open connections, this element reports the
handle of the last Sybtcl command executed.
$sybmsg(nextrow)
Nextrow has been has already been mentioned for both sybsql and sybnext. Here's a quick review of the possible values:
* REG_ROW - at least one row is available after executing sybsql, or a regular row from a select was returned by sybnext.
* NO_MORE_ROWS - no rows were returned in the first result set by sybsql, or the end of the current result set was reached by sybnext.
* n (numeric integer) - last row returned by sybnext was from a "compute" statement.
* NO_MORE_RESULTS - the final set of result sets have already been returned by sybnext.
$sybmsg(retstatus)
Retstatus is the return code from a stored procedure. It's non-null presence also indicates that any output variables from a stored procedure are now available to be retrieved with the sybretval command.
$sybmsg(collengths)
Collengths is a Tcl list of column lengths that correspond to the column names that are returned by sybcols. The column lengths are either the internal size for numeric datatypes, or the maximum length that character datatypes are defined in the database.
$sybmsg(coltypes)
Coltypes is a Tcl list of column datatypes that correspond to the column names that are returned by sybcols. Both collengths and coltypes are only set after sybcols has been executed.
$sybmsg(msgno)
Msgno is a message number generated by the Sybase server. If the server generated several messages during execution of Transact-SQL statements, each number is delimited by newline characters.
$sybmsg(msgtext)
Msgtext is the message text generated by the Sybase server, and corresponds to the message numbers in msgno. As in msgno, separate messages are delimited by newlines. Messages from stored procedures that execute "print" also accumulated in msgtext.
$sybmsg(dberr)
$sybmsg(dberrstr)
$sybmsg(oserr)
$sybmsg(oserrstr)
$sybmsg(severity)
$sybmsg(svrname)
$sybmsg(procname)
$sybmsg(line)
Sybase provide serveral error feedback values, all of which are avilable in Sybtcl. Dberr and dberrstr is a error number and message generated by the DB-Lib routines that Sybtcl uses. Oserr and oserrstr are operating system errors detected by DB-Lib. Refer to Sybase documentation for interpretation. Svrname is the name of the Sybase server that generated the last error message. This is not necessarily the same as the server from the sybconnect connection, as stored procedures on one server may access tables and stored procedures on another server. Severity is the level of error severity reported by the server. Procname is the name of the stored procedure that generated an error message. Line is the line number of an SQL statement or stored procedure that generated a message.
While most errors can be easily read by examing $sybmsg(dberrstr), Sybase manuals contain the definitive reference for error codes and messages.
Handling Errors
Sybtcl commands will raise a Tcl error condition when a Sybtcl command is invalid or when the Sybase server reports an error in an SQL statement. Sybtcl was designed with this conservative error handling approach so that inadvertent operations to a database would be reported.
The Tcl interpreter normally halts script processing when a Tcl error is raised. The Tcl command catch allows errors to be trapped and error handling code to be executed. For example, the sybconnect command will normal raise a Tcl error when either a Sybase userid or password is invalid, or when the Sybase server is unavailable. Catching the error allows the script to continue, and information about the error to be extracted from the $sybmsg array:
if [catch {sybconnect $sybuserid $sybpassword $sybserver} handle] {
puts "cannot connect to server, reason: $sybmsg(dberrstr)"
exit
}
In addition to raising an error on invalid or improperly formed SQL statements, sybsql will also raise a Tcl error if the Sybase server reports a logical error. Logical errors include attempting to insert a duplicate row into a table that has unique indexes, or perhaps a stored procedure or referential integrity trigger executing a Sybase "raiserror" statement.
Wisqlite - an X11 SQL Processor
Sybtcl combined with the power of Tcl/Tk provides a basis for developing powerful database applications and tools. One common tool, usually provided by database vendors is a general SQL processor, passing SQL statements to the server and printing rows that are returned. Sybase provides 'isql', a command line SQL processor. In the process of writing and debugging Sybtcl, I wrote another SQL processor, one that uses the X Window System via the Tk extension. I originally called it Wisql (for Windowing ISQL); I've since renamed it to Wisqlite, as one of the early users of Sybtcl enhanced my original Wisql into a very comprehensive SQL processor. I still maintain Wisqlite for those who prefer a leaner tool and to provide a smaller sample of using Sybtcl. Wisqlite is also written to use commands from Extended Tcl (TclX), so its interpreter needs Tcl, Tk, TclX, and Sybtcl.
Wisqlite is similar to other X11 applications in that it provides a menu bar, scrolling edit and result panes, and various popup windows.
Figure Sybtcl-2: Wisqlite signon window
After starting wisqlite, the server signon window appears to collect the Sybase user id, password, and server to which it should connect. The server entry is pre-populated with the default (DSQUERY environment variable); pressing the "server" button will list all servers from the $SYBASE/interfaces file.
Figure Sybtcl-3: Wisqlite main window
SQL statements are entered into the top pane (a Tk edit widget); pressing the "Execute" button will send the SQL off to the server, and results will be printed in the lower pane (a Tk listbox.) Menu bar items provide additional functionality.
File
The File menu controls the SQL edit pane. "New" clears the SQL edit and Results windows for a new query. "Open" prompts for a file containing SQL statements through a file selection dialog window. "Save" and "Save as" save the SQL edit window into the current file name, or prompts for a new file name. "Quit" will disconnect from the server and exit Wisqlite.
Results
The Results menu controls the Results window. "Append results" or "Clear results" selects whether the Results window should be cleared on each new SQL execution or appended to the end of the list. "Save as" allows the Results window to be saved to a file, overwriting any previous contents. "Print" sends the Results window to the printer (via the Unix "lp" command.) "Font" changes the font size of the Results window.
Options
Currently, only one option is available in Wisqlite, and that is to set the default format for representing NULL values. "default" causes the default null value behavior as described for $sybmsg(nullvalue). Any other string can be entered.
Databases
The Databases menu allows the user to select a particular database to use. All of the databases that are available are listed.
Objects
The Objects menu provides for data dictionary access into the current database. Tables, views, stored procedures, rules, and triggers can be listed, and detailed information on each can be retrieved. For example, when displaying a list of tables, clicking "Ok" or double clicking on a table will list the columns and their datatypes in that table; for a stored procedure, rule, or trigger, the text of the procedure is listed. It's also possible to display several objects at the same time, as each object is displayed in it's own window.
Execute
Execute causes the SQL statements in the edit window to be sent to the server, and results printed. While the execution is retrieving rows, the Execute button becomes "Cancel"; pressing it will halt retrieval of additional rows. The SQL edit windows has a few keyboard bindings worth mentioning. First, "Control-Return" and "Shift-Return" are bound to the Execute/Cancel button, so a user doesn't have to move a hand from the keyboard to start the query. Also, a buffer of 10 previous commands can be accessed by holding "Control" or "Shift" and pressing either the Up or Down arrow keys. E.g., "Shift-Down" will access the most recent command. Also worth noting is the SQL text doesn't require "go" statements as 'isql' requires. The "#" character at the beginning of a line also indicates a comment, rather than the C-like delimiters "/* ... */".
Help
The last menu item is Help. It's admittedly rather sparse on information. (Since Sybtcl has been released in May of 1992, no one has complained that Wisqlite is too hard to use because of lack of documentation or help text!)
Other Sybtcl applications
The original Wisql was enhanced by an early user of Sybtcl, De Clarke (de@ucolick.org or de@lick.ucsc.edu) De is computing systems manager at the Lick Observatory, University of California, Santa Cruz. De enhanced Wisql (UCO Wisql) by adding a way for novice SQL users to build SQL queries by using Tk widgets to gather information on columns to return, constraints (i.e., SQL "where" clauses) and sorting order. Another feature of UCO Wisql is it's forms table editing mode. De's UCO Wisql is included in the Sybtcl distribution.
Figure Sybtcl-4: UCO Wisql EZreport
Using UCO Wisql's EZrpt feature provides a `point-and-click' way to build up an SQL command that selects columns from a table, arranges to sort rows in some column order, and prompts for constraint values and operators. Additonal buttons on the top of the form allow users to see the SQL that is being constructed, perform a count of rows before retrieving the rows, and then transfer the SQL command to the main Wisql editing windows to be executed.
Figure Sybtcl-5: UCO Wisql EZedit
UCO Wisql's Ezedit provides for simplified editing the data in a table. Rows can be inserted, updated, or delete in tabular or record-at-a-time format.
De Clarke also written another handy application, also included in the Sybtcl distribution. Syperf is a Sybase server performance monitor, intended for a Sybase administrator. Various Sybase operational statistics are presented in "dial" format.
Another recent development from De Clarke is "syb_html". Syb_html is a general purpose WWW HTML to Sybase interface using Sybtcl. HTML forms are generated to accept selection criteria for a table select, and to designate which columns to return. When a Web users fills out the form and presses "Submit", the results of an SQL query are returned on another page.
Building Sybtcl
Sybtcl builds "out of the box" for most environments. Sybtcl is distributed as a compressed (GNU gzip) tar file. A configure shell script built with GNU Autoconf is used to build the initial Makefile. Although not required for building Sybtcl, Extended Tcl is highly recommended. Wisqlite, UCO Wisql, and Syperf require commands from TclX. Configure will search for Tcl, Tk, and TclX (Extended Tcl) source directories in sibling directories from where Sybtcl was un-tarred.
The other requirement for the configure script is finding the Sybase home directory, since Sybtcl needs the Sybase DB-Lib to link with. Configure looks in the directory defined by the SYBASE environment variable. The resulting Makefile will build a Tcl command line interpreter and a Tcl/Tk Wish interpreter, each with or without TclX. The Makefile target "all" will compile sybtcl.c, build a library that contains the Sybtcl commands in addition to the required Sybase DB-Lib objects, then build the two interpreters, leaving the executables in the distribution directory. The target "install" will copy the executables to the installation bin directory, and the Sybtcl man page to the installation man directory. If Sybtcl was built with TclX, the target "install-wisqlite" will configure Wisqlite with the proper #!interpreter line and copy Wisqlite to the installation bin directory.
The "README" file contains notes on a particular release of Sybtcl, which Tcl/Tk versions that are required, tested configurations, and general information. The file "INSTALL" contains detailed information on building Sybtcl, including configure options recognized. Both README and INSTALL should be perused prior to building Sybtcl. The file "CHANGES" is a history of features added and bugs fixed in Sybtcl. The subdirectory "samples" contains additional sample Sybtcl scripts. UCO Wisql is located in "samples/uco".
Sybtcl is available at ftp.aud.alcatel.com in the directory /tcl/extensions/sybtcl-2.3.tar.gz. Newer versions will have the same file name, except for incrementing the major or minor version numbers. Usenet discussion of Sybtcl currently takes place in comp.lang.tcl, or comp.databases.sybase. I can be reached at tpoindex@nyx.cs.du.edu.