0% found this document useful (0 votes)
4 views

TD Connecting SQLWindows Objects to Databases

Uploaded by

gibasam
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

TD Connecting SQLWindows Objects to Databases

Uploaded by

gibasam
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 145

Team Developer
Connecting SQLWindows Objects to Databases

Product Version 6.3.1


The software described in this book is furnished under a license agreement and may be used only in accordance with the
terms of the agreement.

Last updated: September 2015

Legal Notice

Copyright © 2014-2015 Gupta Technologies, Inc. All rights reserved.

Gupta, Gupta Technologies, the Gupta logo, Gupta Powered, the Gupta Powered logo, ACCELL, Centura, Centura
Ranger, the Centura logo, Centura Web Developer, Component Development Kit, Connectivity Administrator,
DataServer, DBIntegrator, Development Kit, eWave, Fast Facts, NXJ, Object Nationalizer, Quest, Quest/Web,
QuickObjects, RDM, Report Builder, RPT Report Writer, RPT/Web, SQL/API, SQLBase, SQLBase Exchange, SQLBase
Resource Manager, SQLConsole, SQLGateway, SQLHost, SQLNetwork, SQLRouter, SQLTalk, Team Developer, Team
Object Manager, TD Mobile, Velocis, VISION, Web Developer and WebNow! are trademarks of Gupta Technologies and
may be registered in the United States of America and/or other countries.

SQLWindows is a registered trademark, and TeamWindows, ReportWindows and EditWindows are trademarks
exclusively used and licensed by Gupta Technologies.

The product described in this document is distributed under licenses restricting its use, copying, distribution, and
decompilation/reverse engineering. No part of this document may be reproduced in any form by any means without prior
written authorization of Gupta Technologies Corporation and its licensors, if any.

THE DOCUMENTATION IS PROVIDED “AS IS” AND ALL EXPRESS OR IMPLIED CONDITIONS,
REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE
EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. GUPTA TECHNOLOGIES, INC.
SHALL NOT BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES IN CONNECTION WITH THE
FURNISHING, PERFORMANCE, OR USE OF THIS DOCUMENTATION. THE INFORMATION CONTAINED IN
THIS DOCUMENTATION IS SUBJECT TO CHANGE WITHOUT NOTICE.

This document may describe features and/or functionality not present in your software or your service agreement. Contact
your account representative to learn more about what is available with this Gupta Technologies® product.

Gupta Technologies, Inc.


1420 Rocky Ridge Drive, Suite 380
Roseville, CA 95661

www.guptatechnologies.com

2
Table of Contents

Chapter 1 –Overview ................................................................................................................................................. 11


Client/Server Architecture ................................................................................................................................................ 11
Autocommit.............................................................................................................................................................................. 12
Bind Variables ......................................................................................................................................................................... 13
Concurrency ............................................................................................................................................................................. 14
Connectivity Parameters .................................................................................................................................................... 14
Connect Search Order .......................................................................................................................................................... 14
Cursor Context Preservation ............................................................................................................................................. 15
Front End Result Sets ........................................................................................................................................................... 16
Handles, Contexts, and Connections .............................................................................................................................. 17
IMAGE Column Types........................................................................................................................................................... 18
Writing Data to an IMAGE Column ......................................................................................................................... 18
Isolation Levels ....................................................................................................................................................................... 19
Isolation Level Terms ................................................................................................................................................... 19
Isolation Reads by Server ........................................................................................................................................... 20
Lock Time-Out......................................................................................................................................................................... 21
Nulls, Empty Strings, and Spaces .................................................................................................................................... 21
Using NULL ....................................................................................................................................................................... 22
Positioned Updates ............................................................................................................................................................... 23
Coding a Positioned Update....................................................................................................................................... 23
Result Set Mode ...................................................................................................................................................................... 24
Turning the Mode On and Off.................................................................................................................................... 24
Initial setting ............................................................................................................................................................ 24
Effects of Setting the Mode ........................................................................................................................................ 25
Stored Procedures ................................................................................................................................................................. 26
Transactions............................................................................................................................................................................. 26
Chapter 2 – Initializing and Testing Your Connection ................................................................................... 27
Synopsis of What to Do........................................................................................................................................................ 27
Before Connecting ................................................................................................................................................................. 27
Configuring an ODBC Data Source .................................................................................................................................. 28
Adding and Setting Up an ODBC Data Source .................................................................................................... 28
Initializing SQLWindows Applications.......................................................................................................................... 28
Search Path for SQL.INI ............................................................................................................................................... 29
How SQL.INI is Structured ......................................................................................................................................... 29

3
Connection Strings ........................................................................................................................................................ 30
How to Edit SQL.INI ...................................................................................................................................................... 30
SQL.INI Keywords to Use ............................................................................................................................................ 31
Testing the Connection ........................................................................................................................................................ 31
Testing the Connection with SQLTalk .................................................................................................................... 31
Connecting to Sybase ........................................................................................................................................................... 32
Connecting to Oracle ............................................................................................................................................................ 32
Modifying the Registry ................................................................................................................................................ 32
Creating Views ................................................................................................................................................................ 32
Accessing tables and stored procedures on Oracle 8 and 9 ................................................................. 32
Running a view script........................................................................................................................................... 33
Specifying the Database in Your Application.............................................................................................................. 33
Troubleshooting Problems ................................................................................................................................................ 33
Problems Connecting ................................................................................................................................................... 33
Truncation of LONG Data ............................................................................................................................................ 34
Logging ODBC API Calls............................................................................................................................................... 34
Checking the Release Notes ....................................................................................................................................... 34
Contacting Technical Support ................................................................................................................................... 34
Selected Errors and Ways to Fix Them ................................................................................................................. 35
Sample Applications ............................................................................................................................................................. 36
Microsoft SQL Server .................................................................................................................................................... 36
Oracle .................................................................................................................................................................................. 37
Sybase System 11.x........................................................................................................................................................ 37
Chapter 3 – Connecting to Informix ..................................................................................................................... 38
Chapter Contents ................................................................................................................................................................... 38
Arithmetic Functions............................................................................................................................................................ 38
Autocommit.............................................................................................................................................................................. 39
Cursor Context Preservation ............................................................................................................................................. 39
Cursors ....................................................................................................................................................................................... 39
Data Types ................................................................................................................................................................................ 39
DDL Statement Support ...................................................................................................................................................... 40
Empty Strings .......................................................................................................................................................................... 40
GET DIAGNOSTICS Statement .......................................................................................................................................... 40
Isolation Levels ....................................................................................................................................................................... 41
Lock Time-Out......................................................................................................................................................................... 41
Miscellaneous Features ....................................................................................................................................................... 41

4
Positioned Updates ............................................................................................................................................................... 41
SET Statement Support ....................................................................................................................................................... 42
SQL.INI Keywords .................................................................................................................................................................. 42
buffrow............................................................................................................................................................................... 42
comdll ................................................................................................................................................................................. 43
defconnect......................................................................................................................................................................... 43
log ......................................................................................................................................................................................... 43
longbuffer .......................................................................................................................................................................... 44
remotedbname................................................................................................................................................................ 44
yieldonservercall............................................................................................................................................................ 45
Stored Procedures ................................................................................................................................................................. 45
Creating Stored Procedures ....................................................................................................................................... 46
Executing Stored Procedures .................................................................................................................................... 46
Dropping Stored Procedures..................................................................................................................................... 46
More information on stored procedures ...................................................................................................... 47
Storing TEXT and BYTE Data ............................................................................................................................................ 47
Unicode ...................................................................................................................................................................................... 47
Supported Data types........................................................................................................................................................... 48
Chapter 4 – Connecting to Microsoft SQL Server ............................................................................................. 49
Autocommit.............................................................................................................................................................................. 49
Cursor Context Preservation ............................................................................................................................................. 49
Data Types Supported.......................................................................................................................................................... 49
Identity Column Type................................................................................................................................................... 51
Empty Strings .......................................................................................................................................................................... 51
Isolation Levels ....................................................................................................................................................................... 51
Keywords as Table and Column Names ........................................................................................................................ 52
Lock Time-Out......................................................................................................................................................................... 52
Native Connectivity ............................................................................................................................................................... 52
Positioned Updates ............................................................................................................................................................... 52
Result Set Mode ...................................................................................................................................................................... 53
SET Statement ......................................................................................................................................................................... 53
SqlDirectoryByName Semantics ...................................................................................................................................... 53
SQL.INI Keywords .................................................................................................................................................................. 53
buffrow............................................................................................................................................................................... 54
comdll ................................................................................................................................................................................. 54
log ......................................................................................................................................................................................... 54

5
longbuffer .......................................................................................................................................................................... 55
odbctrace ........................................................................................................................................................................... 56
odbctracefile .................................................................................................................................................................... 56
remotedbname................................................................................................................................................................ 56
Stored Procedures ................................................................................................................................................................. 57
Creating Stored Procedures ....................................................................................................................................... 57
Deleting Stored Procedures ....................................................................................................................................... 57
Executing Stored Procedures .................................................................................................................................... 58
Example Code .................................................................................................................................................................. 58
Error Codes for Stored Procedures ........................................................................................................................ 59
SAL Extensions................................................................................................................................................................ 59
OdrExecuteProc .............................................................................................................................................................. 59
OdrGetNextResults ........................................................................................................................................................ 60
OdrGetReturnStatus ..................................................................................................................................................... 61
OdrPrepareProc.............................................................................................................................................................. 61
OdrPrepareNextResults .............................................................................................................................................. 62
Restrictions on Stored Procedures ......................................................................................................................... 63
Disable result set mode ....................................................................................................................................... 63
No scrollable cursors ............................................................................................................................................ 63
No positioned updates ......................................................................................................................................... 63
No output values .................................................................................................................................................... 64
Retrieving Output Parameters from Stored Procedures................................................................................ 64
Chapter 5 – Connecting to Oracle .......................................................................................................................... 66
Autocommit.............................................................................................................................................................................. 66
Cursor Context Preservation ............................................................................................................................................. 66
Data Types ................................................................................................................................................................................ 67
DBA Authority ......................................................................................................................................................................... 67
OS Authentication .................................................................................................................................................................. 67
Dynamic PL/SQL .................................................................................................................................................................... 68
Empty Strings .......................................................................................................................................................................... 68
Isolation Levels ....................................................................................................................................................................... 68
Lock Time-Out......................................................................................................................................................................... 69
Optimizing Message Traffic ............................................................................................................................................... 69
Positioned Updates ............................................................................................................................................................... 69
Result Set Mode ...................................................................................................................................................................... 69
SQL.INI Keywords .................................................................................................................................................................. 69

6
comdll ................................................................................................................................................................................. 70
fetchrow ............................................................................................................................................................................. 70
log ......................................................................................................................................................................................... 70
longbuffer .......................................................................................................................................................................... 71
nodefparse ........................................................................................................................................................................ 72
remotedbname................................................................................................................................................................ 72
substitute .......................................................................................................................................................................... 73
uselob.................................................................................................................................................................................. 73
Stored Procedures ................................................................................................................................................................. 74
Overloading Stored Procedures ............................................................................................................................... 75
SqlPLSQLCommand ...................................................................................................................................................... 75
Example PL/SQL stored procedure ................................................................................................................ 75
Executing the Sample Stored Procedure .............................................................................................................. 76
Dynamic Array Support for PL/SQL Stored Procedures ................................................................................ 76
Dynamic Arrays as INPUT arguments ........................................................................................................... 77
Uninitialized values ............................................................................................................................................... 77
Dynamic Arrays as OUTPUT arguments ....................................................................................................... 77
Uninitialized values ............................................................................................................................................... 77
Dynamic Arrays as INPUT/OUTPUT arguments ....................................................................................... 77
Transactions, Disconnects, and Exits............................................................................................................................. 77
Writing RAW Data ................................................................................................................................................................. 78
Chapter 6 – Connecting to Sybase .......................................................................................................................... 79
Autocommit and Chained Transactions ....................................................................................................................... 79
Bind Variables ......................................................................................................................................................................... 79
COMPUTE Clause ................................................................................................................................................................... 80
Cursor Context Preservation ............................................................................................................................................. 80
Data Types ................................................................................................................................................................................ 81
Using the Timestamp Data Type.............................................................................................................................. 81
Empty Strings .......................................................................................................................................................................... 82
Error Processing .................................................................................................................................................................... 82
Error Precedence ........................................................................................................................................................... 82
RAISERROR ...................................................................................................................................................................... 83
Retrieving Error and Info Messages ....................................................................................................................... 83
SybGetClientMsgCount ................................................................................................................................................ 83
SybGetClientMsg ............................................................................................................................................................ 84
SybGetNextClientMsg ................................................................................................................................................... 84

7
SybGetServerMsgCount ............................................................................................................................................... 85
SybGetServerMsg ........................................................................................................................................................... 85
SybGetNextServerMsg.................................................................................................................................................. 86
Examples ................................................................................................................................................................... 86
Getting Multiple Connections ........................................................................................................................................... 87
Handles and Connections ................................................................................................................................................... 87
Isolation Levels ....................................................................................................................................................................... 87
Positioned Updates ............................................................................................................................................................... 88
Positioned Updates and fetchrow ........................................................................................................................... 89
Releasing Locks ...................................................................................................................................................................... 89
Reserved Strings .................................................................................................................................................................... 89
SQL.INI Keywords .................................................................................................................................................................. 90
checkexists........................................................................................................................................................................ 90
closecursorateof ............................................................................................................................................................. 90
comdll ................................................................................................................................................................................. 91
enablemultipleconnections ....................................................................................................................................... 91
fetchrow ............................................................................................................................................................................. 92
locktimeout ...................................................................................................................................................................... 92
log ......................................................................................................................................................................................... 93
longbuffer .......................................................................................................................................................................... 94
remotedbname................................................................................................................................................................ 94
substitute .......................................................................................................................................................................... 95
sybapplicationname...................................................................................................................................................... 95
sybautocommit ............................................................................................................................................................... 96
sybmaxmessages............................................................................................................................................................ 96
sybtracefile ....................................................................................................................................................................... 97
sybworkstationname.................................................................................................................................................... 97
yieldonservercall............................................................................................................................................................ 97
Stored Procedures ................................................................................................................................................................. 99
Using Cursors with Stored Procedures ................................................................................................................. 99
SybExecuteProc ........................................................................................................................................................... 100
SybExecuteProcEx ...................................................................................................................................................... 101
SybGetNextResults ..................................................................................................................................................... 102
SybGetReturnStatus ................................................................................................................................................... 102
SybPrepareProc ........................................................................................................................................................... 103
SybPrepareNextResults ............................................................................................................................................ 104

8
Retrieving Output Parameter Values .................................................................................................................. 104
Transactions, Disconnects, and Exits.......................................................................................................................... 106
Writing and Retrieving IMAGE and TEXT Data ...................................................................................................... 106
Writing Data .................................................................................................................................................................. 106
Retrieving Data and Buffer Size ............................................................................................................................ 107
SybWriteText ................................................................................................................................................................ 107
Chapter 7 – Connecting to Databases Using ODBC ....................................................................................... 110
Connecting to Specific Data Sources ........................................................................................................................... 110
Connecting to Access ................................................................................................................................................. 110
Connecting to DB2/400 ........................................................................................................................................... 111
About the ODBC Driver for SQLBase ................................................................................................................... 111
Brand Information.............................................................................................................................................................. 111
Cursor Context Preservation .......................................................................................................................................... 112
Data Types ............................................................................................................................................................................. 112
Error Processing ................................................................................................................................................................. 112
Error Code Mapping .......................................................................................................................................................... 113
Lock Time-Out...................................................................................................................................................................... 117
SQL.INI Keywords ............................................................................................................................................................... 117
buffrow............................................................................................................................................................................ 117
comdll .............................................................................................................................................................................. 118
enablemultipleconnections .................................................................................................................................... 118
log ...................................................................................................................................................................................... 119
longbuffer ....................................................................................................................................................................... 119
odbctrace ........................................................................................................................................................................ 120
odbctracefile ................................................................................................................................................................. 120
remotedbname............................................................................................................................................................. 120
Stored Procedures .............................................................................................................................................................. 121
Transactions, Disconnects, and Exits.......................................................................................................................... 121
Chapter 8 – Connecting to Multiple Databases Concurrently .................................................................. 123
Overview ................................................................................................................................................................................ 123
Two Approaches.................................................................................................................................................................. 123
About the Application ............................................................................................................................................... 123
Schema and Location of Tables ............................................................................................................................. 124
Running the Application .................................................................................................................................................. 124
Preparing to Run the Application......................................................................................................................... 124
Configuring the Application ................................................................................................................................... 125

9
Connecting to the Servers ....................................................................................................................................... 125
Starting the sample application .................................................................................................................... 125
Logging in to the database servers .............................................................................................................. 125
Populating the Tables ................................................................................................................................................ 126
Deleting the Tables ..................................................................................................................................................... 126
Displaying Table Data................................................................................................................................................ 126
About the Details Form............................................................................................................................................. 127
Modifying Autocommit and Isolation Level Settings ................................................................................... 127
Disconnecting and Exiting....................................................................................................................................... 128
Design Issues ........................................................................................................................................................................ 128
Connecting to the Databases .................................................................................................................................. 128
Setting autocommit .................................................................................................................................................... 129
Concurrency and Consistency ............................................................................................................................... 130
Locking .................................................................................................................................................................... 130
Isolation levels ..................................................................................................................................................... 131
Replicating Table Schemas and Data .................................................................................................................. 132
Displaying Table Data................................................................................................................................................ 135
Key Columns ................................................................................................................................................................. 136
Transaction Processing ............................................................................................................................................ 136
Processing the INVOICE Transaction .................................................................................................................. 138
Dynamic Binding ......................................................................................................................................................... 138
Implementation Details.................................................................................................................................................... 139
Updating Table Data .................................................................................................................................................. 140
Dynamic Table Windows Management ............................................................................................................. 140
Microsoft SQL Server and SQLBase ............................................................................................................. 140
Oracle ....................................................................................................................................................................... 140
Dynamic Data Type Binding ................................................................................................................................... 141
Oracle ....................................................................................................................................................................... 141
Microsoft SQL Server ......................................................................................................................................... 141
Isolation Levels and Oracle ..................................................................................................................................... 141
Autocommit ................................................................................................................................................................... 142
Getting More Information ............................................................................................................................................... 143
Glossary ....................................................................................................................................................................... 144

10
Chapter 1 –Overview
This chapter explains how to connect SQLWindows applications to database servers. It describes some of the
features supported by one or more of the databases to which you can connect a SQLWindows application.
Read this chapter to get a general description of each feature, and then read the chapter that covers the
database to which you are connecting to find out if (and how) those features are supported by that database.
If the chapter that discusses a particular database does not mention a particular feature, that feature is not
supported (either because the database does not support it, or because SQLWindows does not support it for
that database).

Client/Server Architecture
A SQLWindows client/server application consists at runtime of the following components:
• SQLWindows application—This is a program that performs services for the user, and that communicates
with a database using a SQLRouter. The application can be written using QuickObjects®, the object-
oriented Scalable Application Language (SAL), or a combination of both.
• SQLRouter—A software library supplied with Team Developer that takes as input data and actions
specified by a Gupta SQLWindows application; SQLRouter maps the input into appropriate function calls
and data types from a database vendor’s API (such as Oracle’s OCI or Microsoft’s ODBC) to implement the
specified actions on the given data.
SQLRouter also accepts data and actions from a database, and converts them into actions and data that a
SQLWindows application can understand.
You can use the following SQLRouters to connect to the following databases:

SQL Router Target Databases

SQLRouter/Informix (no Unicode support) Informix 7.2, 7.3, and 10.

SQLRouter/ODBC DB2/400, Microsoft SQL Server 2000 & 2005,


Microsoft Access, Oracle (using the ODBC API)
SQLRouter/Oracle Oracle 9.2 and later (using the native API)

SQLRouter/Sybase (Sybase 15 client Sybase ASE 12.x and 15


recommended)

You can also use SQLRouter/ODBC to connect to any database for which you can obtain an ODBC driver
that is certified to work with SQLWindows. For more information about connecting using ODBC drivers,
see Chapter 7 – Connecting to Databases Using ODBC.
• Database vendor API —The call-level interface supplied by a database vendor to communicate with that
vendor’s database. Examples include Oracle OCI, Informix SQLI, Sybase CT-LIB, CA-OpenIngres Embedded
SQL, and Microsoft ODBC.
• Network access layer—The software library supplied by a database vendor to accept the actions and data
specified by the database vendor API and send them out over the network. This library also accepts

11
actions and data provided by the database over the network and passes them up to the database vendor
API layer.
• Network software—Software that sends and receives messages over network hardware. Examples
include TCP/IP, IPX/SPX, NetBEUI, NetBIOS, and Named Pipes.

Autocommit
Commit relates to the grouping of server actions (typically changes to a database) so all of the actions are
made permanent in the database if and only if they are all successful. The set of such actions constitutes a
transaction (see also Transactions on page 26).
The default setting for autocommit is:
• Oracle—OFF
• Informix—OFF
• Microsoft SQL Server—ON
• System 11—OFF
• CA-OpenIngres—OFF
• ODBC—In general, it depends on the driver and data source.
If autocommit is on, each SQL statement executed becomes its own transaction. Unless an error occurs, the
result of the SQL statement is committed to the database. Also, you release locks and other transaction
resources as soon as possible, without needing to execute an explicit COMMIT statement.
If autocommit is off and you have a pending transaction, what happens if you try to execute a DDL statement
depends on the database server:
• Microsoft SQL Server and Sybase—DDL statements are not allowed while a transaction is pending.
• Oracle—If you try to execute a DDL statement, the database server first commits the pending transaction
before executing the DDL statement.
• Informix—If you are using the ANSI or Transaction database server mode, DDL statements are considered
part of the current transaction. Once you commit (or roll back) the transaction, the DDL is committed (or
rolled back) along with all the other statements that make up the transaction.
• CA-OpenIngres—DDL statements are considered part of the current transaction. Once you commit (or roll
back) the transaction, the DDL is committed (or rolled back) along with all the other statements that
make up the transaction.
If autocommit is off, you start a transaction, then you try to turn autocommit on, what happens next depends
on the database server:
• Microsoft SQL Server, CA-OpenIngres, and Sybase—You get an error. You must first commit or roll back
the pending transaction before you can turn autocommit on.
• Oracle and Informix—You can execute the statement to turn autocommit on while a transaction is
pending, but the new setting for autocommit does not immediately take effect; you must first finish the
transaction by explicitly committing it or rolling it back. Once you finish the pending transaction,
autocommit takes effect.If autocommit is on and cursor context preservation (CCP) is off, performing an
SQL statement that modifies a row in a result set (such as UPDATE or DELETE) not only commits the

12
change to the database, it also destroys the result set and its associated cursor. To perform multiple
operations on the same result set, either disable autocommit or enable CCP (or both).
• If autocommit is off, you are responsible for:
• Executing the COMMIT statement (or calling SqlCommit) often enough to reduce unnecessary locking
that could restrict access to data for other users (because of either read or update locks).
• Grouping your changes to the database in such a way that only the appropriate changes are cancelled if
you need to execute a ROLLBACK.
An explicit commit (either manual or automatic) affects all SQL handles associated with the same connection
to the database.
To find out if the database you are connecting to supports autocommit, how it supports it, and whether
autocommit is on or off by default, read the chapter in this book devoted to that database.
To turn autocommit off for all SQLWindows applications that connect to a given database, set the appropriate
SQL.INI keyword (if one is defined for that database) to “off”.

Note: You can turn off autocommit only for some of the databases you can connect to. For more
information, see the Autocommit section of the chapters that discuss the databases your application
connects to.

You turn autocommit on or off by calling SqlSetParameter. Even though you specify a specific handle (hSql) as
one of the arguments, the change in the setting for autocommit affects all handles associated with the same
connection as hSql.
To turn autocommit off for a given application, include the following line in your Gupta SQLWindows
application:
Call SqlSetParameter(hSql, DBP_AUTOCOMMIT, FALSE, strNull)
If autocommit is off, changes you make to a database do not become permanent until you execute a COMMIT
statement (or call SqlCommit). Further, if you disconnect from the database or exit the application (either
normally or abnormally) without executing COMMIT, the pending transaction may or may not be committed
for you. To find out what does happen, read the chapter that covers the database you are connecting to.
For additional information, read the documentation on SqlSetParameter and DBP_AUTOCOMMIT in the
SQLWindows Function Reference.

Bind Variables
Bind variables, also called program variables, host variables, or parameter markers, refer to data values
associated with an SQL statement. Bind variables associate (bind) a syntactic location in an SQL statement
with a data value in that statement; the binding actually occurs at runtime.
A bind variable in an SQL statement indicates that data from a variable in the application is bound to it each
time the statement executes. This feature allows an SQL statement to be compiled once and executed
repeatedly with a new set of values in the bind variables each time.
Read the chapter that covers the database to which you are connecting to find out if (and how) bind variables
are supported by that database.

13
Concurrency
A single Gupta SQLWindows application can access multiple tables on a given database, as well as connect to
multiple databases simultaneously. However, execution of all Scalable Application Language (SAL) functions is
single-threaded— the function must complete (and the value of bOk returned to the application) before the
next statement or function in the application can be executed.
Concurrency can also be affected by how different applications obtain and release locks. For related
information, see “Isolation Levels” on page 19 and “Lock Time-Out” on page 21.
To learn how to write applications that connect to multiple databases concurrently, see Chapter 8 –
Connecting to Multiple Databases Concurrently.

Connectivity Parameters
In your Gupta SQLWindows application you can get and set a number of parameters that affect transaction
processing and other activities or attributes related to connectivity.
The following parameters can be retrieved with SqlGetParameter and set with SqlSetParameter:
• DBP_AUTOCOMMIT—Whether autocommit is on or off.
• DBP_BRAND—The vendor of the database you are connected to.
• DBP_LOCKWAITTIMEOUT—Time limit on waiting for a lock to be released.
• DBP_PRESERVE—Whether cursor context preservation is on or off.
• DBP_VERSION—The version of the database to which you are connected.
• DBP_ROLLBACKONTIMEOUT—Whether the most recent transaction is rolled back if a time-out expires.
• DBP_TIMEOUT—Specifies Sql command timeout. Works only in .NET applications
The library file SQLNWKCN.APL (in the Gupta installation directory) contains a large number of user constants
that you can retrieve with SqlGetParameterAll and set with SqlSetParameterAll. To use these constants,
include this file in your Gupta SQLWindows application. Open this file with File, Open from the Gupta
SQLWindows menu bar and expand the user constants section of the outline to see the constants defined.
For example, to set the size of the buffer used to hold LONG data to 2000 bytes, include the following line in
your application:
SqlSetParameterAll(hSql,DBP_LONGBUFFER,2000,’’,TRUE)

Connect Search Order


The name of the database your SQLWindows application tries to connect to (the target database) must be
assigned to the global variable SqlDatabase before the application calls SqlConnect. Before the application
actually tries to connect to the database, it searches each router-specific section of the SQL.INI file for a
remotedbname statement that lists the target database name.
The order in which you list the different SQLRouters (using the comdll keyword) in the SQL.INI file determines
the order in which the router-specific sections are searched. For example, if the DLL for SQLRouter/Oracle is
listed first in the comdll statements, the Oracle-specific section of the file, namely [oragtwy], is searched first.

14
For multiple database connections to work, the order of connections in the [win32client.dll] section of the
sql.ini must follow this convention:
• If not using SQLBase or ODBC, these entries may appear in any order.
• If you're using ODBC and specifying multiple routers, sqlodb32 should appear in the last position.
• If you're using SQLBase and specifying multiple routers, sqlws32 should be the last position.
• If you're using SQLBase & ODBC and specifying multiple routers, sqlodbc32 should be in the 2nd last
position and sqlws32 in the last position.
The following is an example:
[win32client.dll]
comdll=sqlsyb32 ! Sybase native router.
comdll=sqlora32 ! Oracle native router.
comdll=sqlodb32 ! ODBC is in the 2nd last position.
comdll=sqlws32 ! SQLBase is in the last position.
If a remotedbname statement is found that lists a name matching the name of the target database, the
application tries to connect. If the attempt to connect fails, the application gets an error.
If no remotedbname statement is found that lists a name matching the name of the target database, the
router-specific section for the next SQLRouter in the sequence of comdll statements is searched. This
continues until a matching database name is found, or until the sequence of comdll statements is exhausted.
A slight variation applies to SQLRouter/ODBC and SQLRouter/Microsoft SQL Server. Instead of searching the
[odbcrtr] section of SQL.INI for a remotedbname statement, the application tries to find an ODBC data source
name in the ODBC.INI file that matches the target database name. Otherwise, the processing is the same as
for all the other SQLRouters.
For more information about the SQL.INI file, see “Initializing SQLWindows Applications” on page 28. For more
information about the remotedbname statement, read the SQL.INI keywords section for each of the database
servers (Oracle, Informix, and so on) you will connect to.

Cursor Context Preservation


Cursor context preservation (CCP) preserves any result sets (including FERS, see “Front End Result Sets” on
page 16) that you retrieved during a transaction, as well as the cursors associated with those result sets, even
after you do a COMMIT. You also maintain the locks you obtained during the transaction.
Not all databases support CCP. CA-OpenIngres does not. If the database you are connecting to does not
support CCP, or it supports it but you have turned it off, you lose all result sets and their associated cursors
when you do a COMMIT; you also release all locks obtained during the transaction.
SQLWindows turns CCP off by default.
If CCP is off and you commit the current transaction (either explicitly or because autocommit is on), you lose
all open result sets and all prepared statements are invalidated. If CCP is on, the result sets remain open even
after the commit.
If you roll back the transaction, you lose all open result sets even if CCP is on.
If autocommit is off and you change the setting of CCP during the course of a transaction, the new setting for
CCP does not take effect until you commit or roll back the current transaction.
To turn CCP on, include the following statement:

15
Call SqlSetParameter(hSql, DBP_PRESERVE, TRUE, strNull)
in your SQLWindows application. Call this function for each SQL handle associated with the result sets you
want to maintain context for (see “Handles, Contexts, and Connections” on page 17).
If your application performs a ROLLBACK, all result sets (whether backend result sets or FERS) and their
cursors are discarded even if CCP is enabled.
If you are using front end result sets (FERS) and you have enabled CCP, the rows in the result set remain
available to your application even after you execute a COMMIT. However, the data in the FERS file is not
updated to keep it in sync with any changes made to the corresponding rows on the database server. If other
clients make changes to the table on the server, the FERS data will be out of date. To learn more about FERS,
see “Front End Result Sets”, which follows.
To find out if the database you are connecting to supports CCP, read the chapter in this book that covers that
database.

Front End Result Sets


If you turn on result set mode (read Result set mode on page 1-19) in your SQLWindows application, the user
of your application can scroll both backwards and forwards through a result set returned from the database;
the user then has a “scrollable cursor”. If you turn off result set mode, the user of your application can scroll
only forwards through the result set, not backwards.

Note: In SQLWindows, result set mode is off by default.

If you turn on result set mode, and your application is connected to a database that has native support for
scrollable cursors (sometimes called backend result sets or backend cursors), SQLWindows uses that support
to allow the user to scroll in both directions through a result set. If the database does not have native support
for scrollable cursors, SQLWindows implements this capability using front end result sets (FERS).
Even if the target database supports (backend) scrollable cursors, this feature must first be enabled on the
database for your application to use it.
To implement FERS, the SQLRouter software takes a snapshot of the result set returned by a SELECT
statement and stores it in a temporary file on the client. Each file is named FRSn (with no extension), where n
is a positive integer. The first file is named FRS1, the second is named FRS2, and so on.
SQLRouter needs two file handles for every FERS file it creates. If SQLRouter cannot obtain the file handles, it
returns an error to the application.
These FERS files are deleted when your SQLWindows application attempts to prepare another SQL statement
or disconnects from the database server. If the application ends abnormally and leaves these files, you must
delete them manually. Look for them in the standard directory where temporary files are stored.

Note: If your application is using FERS, you can release all shared locks on the result set returned by your
SELECT statement, call SqlGetResultSetCount. To learn more about this function, see the SQLWindows
Function Reference.

You cannot SELECT...FOR UPDATE (use positioned cursors) if the named cursor is associated with a front end
result set. In other words, you cannot lock on the server the rows that are being displayed on the client. The

16
data the user sees on the client may not match the data on the server if another application makes changes
to those same rows.
For information on result set mode and how to enable it, see “Result Set Mode” on page 24. For information
on FERS and cursor context preservation (CCP), see “Cursor Context Preservation” on page 15.

Handles, Contexts, and Connections


For a SQLRouter, a SQLWindows SQL handle identifies a context for the separate preparation (compilation)
and execution of SQL statements, and (for SELECT statements) the accessing of rows from a result set. The
handle also identifies a separate context for communication between the application and the server. If
allowed by the target database server, a SQLWindows application can have multiple connections to a
database, and multiple open handles on any given connection.
A SQLWindows application opens a connection to a database when it makes its first call to SqlConnect; at the
same time, the application obtains its first handle. This allows the application to prepare and execute SQL
statements. Associated with this connection are a database name, user ID, and password. When the
application makes additional calls to SqlConnect, SQLRouter uses the same connection to the database, but
establishes a new context. If you pass a new SQL handle variable as the argument to SqlConnect (the normal
case), you continue to have access to all the other contexts you created with earlier calls to SqlConnect.
This architecture allows you, for example, to obtain a result set, position the result set cursor to the desired
row, then update the row without losing the result set or the execution plan for the SELECT statement that
obtained the result set. For more information, see “Positioned Updates” on page 23.

Note: Some Gupta documentation refers to the SQL handle as a “cursor.” This is very different from the ANSI
use of the term cursor, which is a pointer to a given row in a result set.

In general, a SQLWindows application that uses multiple handles does one of the following:
• Connects to the same table for two different activities—the first handle is used to prepare and execute a
SELECT statement to obtain a result set, then fetch the desired row from that result set; the second
handle is used to prepare and execute the SQL statement that modifies the result set (UPDATE, INSERT, or
DELETE).
• Connects to two different tables—the handle is used to prepare and execute the SQL statement that
updates a column in one table based on a value from another table obtained by preparing and executing
a different SQL statement.
• Connects to two different databases—each handle identifies a separate context for preparing and
executing SQL statements.
In general, different SQL handles, while they identify separate contexts for preparing and executing SQL
statements, are all part of the same transaction scope. Therefore, if you execute a COMMIT or ROLLBACK
statement on any handle, the database server commits (or rolls back) the results of all of the uncommitted
SQL statements executed on all of the open handles connected to a given database.

Note: When it comes to committing statements, some databases distinguish between Data Manipulation
Language (DML) statements and Data Definition Langauge (DDL) statements. With these databases,
DDL statements are always committed, whereas DML statements are committed only if the COMMIT
statement (or the SqlCommit function) is executed. In other databases, neither DML nor DDL

17
statements are committed until COMMIT is executed (or SqlCommit is called). This whole discussion
assumes, of course, that autocommit is off.

The maximum number of handles and connections your application can have depends on your client
operating system resources and any limits imposed by the target database server— SQLWindows itself
imposes no limitations.
For related information, see “Transactions” on page 26.

IMAGE Column Types


A SQLWindows application can read, write, and update LONG BINARY data (IMAGE data) using regular SQL
statements (SELECT and UPDATE). This is true even in versions prior to TD 5.2, when SQLWindows did not
have a native LONG BINARY data type (SQLWindows converts LONG BINARY data to LONG STRING data when
reading, and LONG STRING data to LONG BINARY data when writing).
In TD 5.2, a new binary data type has been added (see “Binary” on page 2).
When writing LONG STRING data to IMAGE columns, the bind variable that contains the data must be bound
to the appropriate data type by calling SqlSetLongBindDatatype.

Writing Data to an IMAGE Column


1. Assign the data to a SQLWindows LONG STRING variable. The data must use the correct syntax for the
column where it will be stored.
2. Prepare the SQL statement that will update the appropriate value in the table.
3. Set the binding of the LONG STRING variable to type LONG BINARY (numeric value of 23) by calling
SqlSetLongBindDatatype with the correct parameters.
4. Execute the prepared SQL statement.
The syntax of SqlSetLongBindDatatype is:
bOk = SqlSetLongBindDatatype(nBindVarNumber,
nDatatype)
where both arguments are of type Number.
The first parameter identifies the bind variable to be set by its ordinal position in the prepared SQL
statement. Use 1 to denote the first bind variable in the SQL statement, 2 to denote the second bind variable,
and so forth. The second parameter indicates the data type to which the bind variable should be set (LONG
BINARY).

Note: The numeric value to use as the second parameter is 23 (for LONG BINARY). To make your code self-
documenting, you should create a symbolic constant with that value, and then use the constant as
the parameter.

For example:
Constants
System
User

18
Number: LONGBINARY = 23
Local Variables
String: strName
Long String: strPhoto
...
Actions
...
SqlConnect(hSql)
...
! Assume we have read a name into strName and a .BMP file
! into strPhoto. We want to update a particular employee’s
! photo in the database.
Call SqlPrepare(hSql, ’UPDATE emp ’ ||
’SET emp_name = :strName,image_col = :strPhoto’ )

! Because the bind variable :strPhoto is the second bind


! variable in the SQL statement we just prepared, we set
! the nBindVarNumber parameter of SqlSetLongBindDatatype to 2. Call
SqlSetLongBindDatatype(2, LONGBINARY)
Call SqlExecute(hSql)

SqlSetLongBindDatatype should be called immediately after the call to SqlPrepare. The binding established
with SqlSetLongBindDatatype lasts until the next call to SqlPrepare on the same SQL handle.
SQLWindows automatically converts between LONG STRING and TEXT data. You do not need to call
SqlSetLongBindDatatype when reading and writing TEXT data.

Isolation Levels
Isolation levels control how access to the tables in a database by one user affects other users accessing the
same tables in that database. These isolation levels are implemented by locks and apply to all SQL statements
executed as part of a single transaction.
The isolation level you choose depends on the application’s requirement for consistency and concurrency.
Consistency means that during a transaction, the data read remains stable. This implies that the data being
read by one transaction does not change because of updates, deletes, and inserts performed by other
transactions.
Concurrency is a function of the number of users that can access data at the same time. High concurrency
allows multiple users to execute database transactions simultaneously without interfering with each other,
but at the possible expense of consistency.

Isolation Level Terms


Isolation levels are often defined in terms of the following phenomena:
• dirty read—Transaction A changes one or more column values in a row. Transaction B reads the same
row, after which transaction A rolls back its changes. Transaction B has now read values that (from a
transaction-oriented point of view) never existed.
• non-repeatable read — Transaction A reads a row. Transaction B either updates or deletes the row.
Transaction A cannot now repeat the read of that row and get the same results; either the values are
different or the row no longer exists.

19
• phantom row — Transaction A performs a SELECT on a table. Based on the selection criteria in the
SELECT statement, a set of rows is returned to A. Transaction B inserts one or more new rows into the
same table with values that happen to match the selection criteria of the SELECT statement used by A. If
transaction A re-executes the same SELECT statement, it obtains a different set of rows than it did the
first time.

Note: This discussion applies to Gupta applications. If using SQLTalk, the default isolation level, irrespective
of the database to which you connect, is always RR (repeatable reads).

Choose the appropriate isolation level for your applications depending on the balance you need to achieve
between data access concurrency and data consistency.

Isolation Reads by Server


SQLWindows provides four different two-letter codes you can specify when you set an isolation level. The
table below shows you the isolation level you get depending on the code you specify and the database server
you are connected to.

Oracle Informix Microsoft SQL Sybase System CA-OpenIngres


Server/ODBC 11
Default* not applicable committed reads read_committed Level 1 repeatable reads

RO not supported dirty reads read uncommitted Level 0 not applicable

RL not supported committed reads read committed not supported not applicable

CS not supported cursor stability repeatable reads Level 1 not applicable

RR not supported repeatable reads serializable Level 3 not applicable

Default is not an actual code. This row indicates the isolation level you get when you first connect to the given
database server.
Microsoft SQL Server supports the isolation levels listed in the column Microsoft SQL Server/ODBC. For ODBC
data sources on other than Microsoft SQL Server, the SQLWindows ODBC router attempts to set the very
same mapping of two-letter codes to isolation levels; however, which isolation level (if any) is actually
implemented for a given code depends on the ODBC driver and the database server you are using to access
your data source. Your ODBC driver and database server documentation should provide the information you
need.
CA-OpenIngres servers do not support isolation levels. Use the SET LOCKMODE statement to set different
types and levels of locks. The default lock is Shared for read and Exclusive for write.
If you try to change isolation levels while a transaction is pending, what happens depends on the database
server:
• Oracle—The attempt to change isolation levels is ignored.
• Informix and Microsoft SQL Server—The pending transaction is first committed, then the isolation level is
changed as requested.
• Sybase System 11—The request is rejected and an error is raised.

20
To set the isolation level in your application, call SqlSetIsolationLevel. For example, to set the isolation level to
SQL_TXN_SERIALIZABLE on Microsoft SQL Server 6.0, put the following statement in your SQLWindows
application:
Call SqlSetIsolationLevel(hSql,’RR’)
See the SQLWindows Function Reference for more information about this function.
To find out the kinds of isolation levels supported on the databases you are connecting to and the
appropriate arguments to use when calling SqlSetIsolationLevel, see the chapter for those databases.

Lock Time-Out
Lock time-out is the amount of time spent waiting for a resource to become available. If an action cannot be
completed within the set limit, the transaction is aborted and any changes are rolled back.
To enable lock time-out, call SqlSetLockTimeout, or call SqlSetParameter with the DBP_LOCKWAITTIMEOUT
parameter. (For more information about these functions, refer to the SQLWindows Function Reference and
the on-line help.)
Read the chapter that covers the database to which you are connecting to find what kind of lock time-outs (if
any) are supported on that database.

Nulls, Empty Strings, and Spaces


Given a column that is of some character string data type, you can in general insert (or update) the data in
that column using any one of three different values that you may think are similar to each other, but are
actually quite distinct:
• NULL—This value denotes “undefined value”. Use NULL to indicate that there is no information available
for that column. You can put NULL into a column only if you defined the column to allow NULLs when you
created the table that has that column.
• Empty string—This is a string of length zero. It is an actual string value. If a character string column has
the empty string, the value of the column is defined. (This is unlike NULL, which means that the value of
the column is undefined.)
To actually store the empty string in a column, you must be able to define the column to be a varying-
length character type, where the length varies from zero to some positive number. If you define the
column to be a fixed-length character type, the value you insert (or update) is padded with spaces if it is
shorter than the fixed length defined for that column. The empty string has a length of zero, so it is
always padded with spaces (it is always shorter than the fixed length defined for the column). Thus, in the
case of a column of a fixed-length string data type, the database always stores a string of spaces in place
of the empty string. (If you concatenate the empty string with any string X, the result is the same string
X.)
• Space—This is a string of length one. It is no different from any other string of length one as far as the
database is concerned. The only thing special about a space is that it is harder for you to “see” a space
than most other strings of length one.
All databases will store a space in a character string column. If you allow NULLs in a character string column
when you create the table with that column, the database will also store a NULL. However, different

21
databases store different values in a (varying length) character string column when you try to store the empty
string—some do store the empty string, some store NULL, and others store a space.
When inserting or updating a string column in a database table in such a way as to indicate that there is no
data in the column, you typically enter a NULL (if the table allows this), the empty string (also called the zero-
length string), or a space. Which of these you values you use depends on whether the table allows NULLs in
the column and how your application is written.
The value that is actually stored in the database, depending on the database server and the value supplied by
the client, is the following:

Oracle Informix Microsoft SQL Sybase System 11 CA-OpenIngres


Server
empty string NULL space space space (constant) space (constant)
(“”) NULL (bind variable) NULL (bind variable)
NULL NULL NULL NULL NULL NULL

space space space space space space

STRING_Null NULL NULL NULL NULL NULL

To use STRING_Null, you must assign it to a String variable, then use that variable as a bind variable in your
SQL INSERT or UPDATE statement.
The behavior of an ODBC data source depends on the driver and the database server you connect to.

Using NULL
To insert NULL into any of the string columns of your database tables, or to update such columns with NULL
(assuming the columns allow NULLs), you must do the following:
1. Set a String variable to the predefined constant STRING_Null.
2. Prepare and execute a SQL INSERT or UPDATE statement using the variable in step 1 as a bind variable;
for example:
Set strVar = STRING_Null
. . .
SqlPrepareAndExecute(hSql, `UPDATE my_table SET this_column = :strVar`)
You must explicitly set the String variable to STRING_Null because SQLWindows initializes all String variables
to the empty (zero-length) string. Only by using a bind variable set to STRING_Null (rather than the empty
string) can you store NULL in the string columns of your database tables.
To test whether a string column value you fetch from one of the tables in your database is NULL, you must
compare the variable into which you fetched the value to the constant STRING_Null; for example:
SqlPrepareAndExecute(hSql, `SELECT string_col FROM my_table INTO
:strColValue`)
IF strColValue = STRING_Null
. . .
You can also hard-code the NULL in a SQL statement, if appropriate.

22
Positioned Updates
SQLWindows applications can perform positioned updates (WHERE CURRENT OF cursor updates) when
connected to certain databases. For positioned updates to be possible:
• You must create a unique index on one of the table columns (Microsoft SQL Server only).
• You must turn on result set mode (scrollable cursors), except that this is not required for Oracle.

Coding a Positioned Update


To code a positioned udpate, execute the following steps:
1. Create a unique index on one of the table columns (Microsoft SQL Server only).
2. Connect twice to obtain two distinct SQL handles.
3. Enable result set mode (scrollable cursors), specifying one of the SQL handles obtained in the previous
step. (Not required for Oracle.)
4. Using the same SQL handle as in the previous step, execute the SELECT... FOR UPDATE statement to
retrieve the key column value, and any other columns of interest from the table.
5. Using the same SQL handle as in the previous step, create a named cursor by calling SqlOpen.
6. Position the cursor to the appropriate row (using, for example, the SqlFetchNext, SqlFetchPrevious, or
SqlFetchRow functions).
7. Using the other SQL handle, prepare and execute an UPDATE statement with the WHERE CURRENT OF
cursor clause. Specify the cursor that was named with SqlOpen.
The following code fragment illustrates the highlights. In addition to providing an example of the steps listed
above, this fragment shows the creation of the table to be accessed and the creation of the unique index.
Replace numeric_data_type with an appropriate numeric data type for your database.

Note: You need to execute the CREATE UNIQUE INDEX statement (included in the code below) only when
connected to Microsoft SQL Server version 6.x. For other databases, omit this statement.

Call SqlConnect(hSqlA)
Call SqlConnect(hSqlB)
Set strCreateTable = ’CREATE TABLE REALTY ’
Set strCreateTable = strCreateTable || ’(PROPNUM INT,’
Set strCreateTable = strCreateTable || ’CITY VARCHAR(30),’
Set strCreateTable = strCreateTable || ’PRICE numeric_data_type)’
Call SqlPrepareAndExecute(hSqlA, strCreateTable)
Set strCreateIndex = ’CREATE UNIQUE INDEX REALTY_IDX ’
Set strCreateIndex = strCreateIndex || ’ON REALTY(PROPNUM)’ Call
SqlPrepareAndExecute(hSqlA, strCreateIndex)
Call SqlSetResultSet(hSqlA, TRUE)
Call SqlPrepareAndExecute(hSqlA, ’SELECT PROPNUM, CITY, PRICE
FROM REALTY
FOR UPDATE
INTO :COL1,:COL2,:COL3’)
If NOT SqlOpen(hSqlA, ’CUR1’)

If NOT SqlFetchNext(hSqlA, nRetVal)

If NOT SqlPrepare(hSqlB, ’UPDATE REALTY

23
SET PRICE =:COL3
WHERE CURRENT OF CUR1’)

! If no problem preparing the UPDATE statement, do it.
Else Call SqlExecute (hSqlB)
To find out if the database you are connecting to supports positioned updates, see the chapter for that
database.

Result Set Mode


You turn on result set mode in your SQLWindows application to allow a user of your application to scroll both
forward and backward through a result set returned by the database.
Result set mode is off by default when you connect to any of the databases covered in this document. When
result set mode is off, there are several consequences for your application:
• You can retrieve only one result set at a time.
• The cursor can move forward only.
• You must fetch all rows from the result set before retrieving another result set or executing another SQL
statement (for example, CREATE TABLE), even on another handle. This means that, as long as a result set
retrieved on a given SQL handle is pending (you have not yet fetched all the rows), your application
cannot perform any operations on any other SQL handle in the application.
None of these restrictions apply if you turn on result set mode.

Turning the Mode On and Off


To turn on result set mode, include the following statement in your SQLWindows application (before calling
SqlConnect):
Set SqlResultSet=TRUE
You can also include the following statement:
Call SqlSetResultSet(hSql,TRUE)
Make this call before you prepare and execute the SELECT statement that retrieves the result set for which
you want the cursor to be scrollable.
To turn off result set mode, specify FALSE (instead of TRUE) in the Set and Call statements.
If you turn on result set mode for a database server that does not support scrollable cursors (sometimes
called backend cursors), the SQLRouter software makes use of front end result sets (FERS) to get the same
effect. For more information, see “Front End Result Sets” on page 16.
To find out if the database you are connecting to supports scrollable (backend) cursors, see the chapter in this
book that covers that database.

Initial setting
When a SQLWindows application first starts up, the value of the global system variable SqlResultSet is
undefined (null). As long as the variable's value remains undefined, result set mode is initially on for all your
connections to SQLBase and initially off for all your connections to all other database servers.

24
If you set SqlResultSet to TRUE, result set mode is initially on for any subsequent connection you make to
any database server, including SQLBase. If you set SqlResultSet to FALSE, result set mode is initially off for
any subsequent connection you make to any database server, including SQLBase. In either case, if you then
set SqlResultSet to NUMBER_Null, result set mode is initially on for all your subsequent connections to
SQLBase and initially off for all subsequent connections to all other database servers.
Changing the value of the global variable SqlResultSet does not affect the setting of result set mode for any
existing connection. To change the setting of an existing connection, you must call the function
SqlSetResultSet.
With Microsoft SQL Server 6.x or any ODBC data source, even though you specify a specific SQL handle as an
argument to SqlSetResultSet, the new setting for result set mode affects all SQL handles on the same
connection, not just the SQL handle you pass as an argument. If you connect natively to Oracle, Informix, or
Sybase System 11, however, the new setting for result set mode affects only the handle you specify as an
argument to SqlSetResultSet.

Effects of Setting the Mode


The setting of result set mode can affect whether or not you can have multiple open result sets (concurrent
cursors). With CA-OpenIngres, using multiple concurrent result sets, you should set SqlResultSet prior to any
SqlConnects. Otherwise, you have to use the function call after each connect.
The setting of result set mode can affect stored procedures that return result sets:
• Microsoft SQL Server —You must always turn off result set mode before calling a stored procedure if it
returns one or more result sets. Once you return from the stored procedure, you can turn result set mode
back on.
• ODBC data source—Any special requirements, restrictions, or side effects depend on the ODBC driver
you are using and the data source you are accessing (see your driver and database server
documentation).
• Oracle—When you call SqlPLSQLCommand, SQLWindows automatically turns off result set mode for the
SQL handle you supply as an argument. Once you return from the call to SqlPLSQLCommand, you can
turn result set mode back on.

Attribute Oracle/Informix Microsoft SQL Server CA-OpenIngres


Sybase System 11
default OFF OFF OFF
(correct if the global variable
SqlResultSet is either null or FALSE)
scrollable cursors must be ON must be ON must be ON

concurrent result sets can be ON/OFF must be ON must be ON (make sure


autocommit is OFF)
positioned updates or deletes using a must be OFF not supported* not supported
cursor on a stored procedure

*When connected to Sybase System 11, you can declare a cursor on a stored procedure and fetch rows from a
result set returned by that stored procedure, but you cannot do positioned updates or deletes on the result
set using that cursor.)SQL.INI Keywords.

25
All SQLWindows applications initialize themselves by reading the SQL.INI file. To find out more about this file,
see “Initializing SQLWindows Applications” on page 28.

Stored Procedures
Most databases allow procedures containing SQL statements and procedure logic to be compiled and
executed on the database server. These procedures can return result sets, and some can accept input bind
variables (host variables) as arguments. If the target database supports stored procedures, you can call them
from a SQLWindows application.
Using stored procedures can improve application performance by reducing network traffic and executing
compiled SQL operations on the server. Because stored procedures are compiled, the execution plans are
stored in a system table; this allows the stored procedure to execute very quickly. Stored procedures can
enhance security by acting as a controlled access point to sensitive data. They can also increase the
robustness and reliability of distributed processing by centralizing critical or core application logic in one
location (the server).
Depending on the database server you are connecting to, you may be able to pass input arguments, retrieve
values in output arguments, retrieve a result status from the stored procedure, and retrieve result sets. In
some cases you may be able to specify input arguments and retrieve output arguments using bind variables.
Read the chapter that covers the database to which you are connecting to find out if that database supports
stored procedures, how to call them, and any other information specific to that database’s support for stored
procedures.

Transactions
A transaction is a set of actions on a database that are treated atomically—all of the actions are made
permanent in the database or none of them are. Normally, you make all the actions permanent by preparing
and executing the COMMIT statement (or calling SqlCommit); if you want to abandon all the actions (undo
them), you prepare and execute the ROLLBACK statement.
There is no SAL function that corresponds to SqlCommit. To roll back a transaction, you prepare and execute
the ROLLBACK statement. For example:
SqlPrepareAndExecute(hSql,’ROLLBACK’)
Each COMMIT or ROLLBACK statement defines a new transaction boundary; all the statements whose actions
are made permanent with COMMIT (or undone with ROLLBACK) are part of the transaction scope.
For additional information, see “Autocommit” on page 12 and “Handles, Contexts, and Connections” on page
17.

26
Chapter 2 – Initializing and Testing Your
Connection
This chapter describes how to connect a SQLWindows application to a database, test the connection, and
configure the initialization of the client.

Synopsis of What to Do
1. Install your network protocol software (such as TCP/IP, NetBIOS, NetBEUI, Named Pipes, or IPX/SPX) and
the client-side software supplied by your database vendor (network access layer software, API library,
and client tools and utilities). Follow the instructions in the documentation provided with those products.
2. Verify that you can connect to your target database using a suitable tool provided by your database or
other vendor. If you need help, talk to your network or database administrator. Some of the tools
available are the following:
 Informix—ILogin.exe
 Oracle—SQL*Plus (if you have a sufficiently recent version of the database), Sample Pro C application
 CA-OpenIngres—Terminal Monitor
 Microsoft SqlServer—ISQL/w
 Sybase—SQL Advantage
3. Install SQLWindows and choose the appropriate SQLRouters for the databases you will be connecting to.
4. Configure the target data source. (This applies only if you are connecting using ODBC.)
5. Configure the initialization of SQLWindows by editing the SQL.INI file appropriately.
6. Verify that you can connect to the target database using Team Developer software by running SQLTalk
for Windows.
Additional information on steps 4 through 6 is provided after the next section.

Before Connecting
Here are some things to check out before trying to connect a SQLWindows application to a database:
• On the client machine:
a. Make sure all the software components listed in the Release Notes are installed on the client
computer.
b. Verify that you can connect to the target database using the appropriate tools provided by the
database vendor. Read the documentation supplied by the vendor to learn about such tools and how
to use them to verify the connection between your client and the database.
c. Check that the SQL.INI file contains the appropriate statements, and that the keyword values are
specified correctly. To learn more about this, read the rest of this chapter, then read the chapters that
cover the databases you are connecting to.
• On the server machine:
27
a. Check that the appropriate network software is running.
b. Be sure the database server is running and that the target database has been created.
c. Verify that you have a user account with the correct privileges on the database.
Consult with your database administrator and network administrator for any additional help you might need.

Configuring an ODBC Data Source


This section applies only if you are connecting to a database using ODBC (such as to Microsoft SQL Server
2000, DB2/400, Microsoft Access, and so on).
The following procedure describes how to use the ODBC Administrator to add and set up a data source.

Adding and Setting Up an ODBC Data Source


1. Select Control Panel from the Start menu and navigate to the ODBC icon. Depending on the operating
system, it might be located within the Administrative Tools icon.
2. In the ODBC Data Source Administrator window, click Add.
The Create New Data Source dialog box is displayed with the list of ODBC drivers installed on your
machine.
3. Select the appropriate driver from this list and click Finish.
4. Fill in the necessary information in the displayed dialog box and click OK.
You normally get the information you need to complete this dialog box from your database administrator.
You can also read the on-line help (click Help) or the documentation supplied by your ODBC driver
vendor.
The data source you specified is added to the list of Data Sources in the ODBC Data Source Administrator
window and to the ODBC.INI file.
5. Click OK to finish.

Note: Do not edit the ODBC.INI file directly. Use the ODBC Administrator to make any changes to your list of
ODBC data sources. Also, check the documentation that comes with your ODBC driver to see if your
ODBC driver vendor also supports other ways of modifying the ODBC.INI file.

Initializing SQLWindows Applications


All Gupta SQLWindows applications (including SQLWindows itself and all its components) read a configuration
file before running. The default name of this file is SQL.INI, but you have the option to specify a configuration
file of a different name in a location of your choosing. (This document uses the name SQL.INI to refer to the
configuration file.) To configure the initialization of your application, add, delete, or modify the statements in
this file. You can use a text editor, but is is recommended that you use the Connectivity Administrator
application that comes with Team Developer.

28
If you installed other Gupta Technologies products on your machine, a configuration file already exists. If you
install SQLWindows in the same directory as the other Gupta Technologies products, the installation program
adds new statements or modifies existing statements in the file.

Note: All statements from the old SQL.INI file are carried over into the new one.

Search Path for SQL.INI


Before running a SQLWindows application, make sure a configuration file can be found. The default file name
is SQL.INI, and it is searched for as as follows:
If this is a design-time application, and you have specified the name and location of your configuration file
under the Tools/Preferences menu item, that name and location will be used. If only a name is specified, that
name will be used in place of SQL.INI and the locations will be searched as described below.
For a SQLWindows application at runtime, the system variable SqlINI is checked first. If a file name (and
optionally location) are found in that variable, that name and location are used to find the configuration file.
Otherwise the search sequence below is used.
When an application needs to search for the configuration file, it looks first in the directory you specify in the
SQLBASE environment variable. If not found there, the search order for this file is:
• Current directory
• The directory of the executable
• \sqlbase directory on the current drive
• Root directory on the current drive
• Directories specified by the PATH environment variable
• The Windows system directory
• The Windows directory

Note: Although Team Developer 3.1 and later allows the existence of multiple configuration files on a single
machine, you should still be cautious when you have multiple files. The most common cause of
connectivity difficulties is an application connecting to one configuration file when the developer
believes it is connecting to a different file. Understand the search sequence above and be certain that
your application is finding the right file.

How SQL.INI is Structured


SQL.INI is a simple text file that you modify using the Connectivity Administrator tool in Team Developer
(recommended) or any ASCII text editor.
The file is subdivided into sections. Each section starts with a section name, a character string enclosed in
square brackets. Each section contains zero or more statements. Each statement starts with a keyword,
followed by the equals sign and a sequence of comma-separated values:
[section_identifier]
keyword1=parameter1(,parameter2 ... )
keyword2=parameter1(,parameter2 ... )

keywordn=parameter1(,parameter2 ... )
29
When this document describes a default value for a SQL.INI keyword, this is either the value as defined in the
SQL.INI file before a user has edited it, or the value assumed when the statement containing the keyword is
absent or commented out.
The order in which the sections appear is not significant. For most sections, the order of keywords within a
section is also not significant. The only exception is the order of the comdll keywords in the [win32client.dll]
section, and the order of the remotedbname keywords in the sections assigned to each individual database.
When a SQLWindows application initializes, it tries to make a connection to a database following the order of
the routers listed using the comdll keywords; for each router, the application tries to connect to a database in
the order specified by the appropriate remotedbname keywords.
If you connect natively to Oracle, or Sybase, the longbuffer keyword in the SQL.INI file specifies the maximum
number of bytes of long data type columns to retrieve, and truncates excess bytes. However, this keyword
does not control how much long data can be inserted or updated.
If you connect to Microsoft SQL Server or any data source using ODBC, this keyword controls how much long
data can be fetched, inserted or updated.

Connection Strings
In Team Developer 5.2, the [ConnectionStrings] section was introduced to the SQL.INI file, and the following
functions were added to Team Developer:
• SqlWriteConnectionString
• SqlGetConnectionStrings
• SqlDeleteConnectionString
• SqlListConnections
See the online help for detailed documentation of these functions. A connection string is formatted as
follows:
alias=<alias>;comdll=<comdll>;settings=<settings>
Here are some examples:
[ConnectionStrings]
comdll=sqlodb32;alias=Sql Server Standard ODBC;dbname=qa2k3odbc
comdll=sqlsyb32;alias=Robby Sybase;dbname=Robby2;database=master
comdll=sqlifx32;alias=Robby;host=robby;dbname=sysutils
comdll=sqlora32;alias=QAOra10G Our favorite Oracle DB;dbname=qaora10g
comdll=sqlodb32;alias=My DB 2 Connection;dbname=DB2ODBC

How to Edit SQL.INI


Gupta recommends using Connectivity Administrator to edit SQL.INI. This tool does syntax checking to ease
your data entry and prevent many inadvertent errors.
However, if you choose to use a text editor on SQL.INI, be mindful of these guidelines:
• List parameters for a keyword in the order indicated in the Syntax section of the reference page for that
keyword. Parameters may be ASCII alphanumeric characters only—no underscores, hyphens, or periods.
• Carefully check the syntax of a keyword statement. In most cases, you use commas (not spaces) to
separate parameters.

30
• Put each statement on one line. Inserting carriage returns or any “end-of-line” markers in the statement
can cause errors.
• Put comments on their own lines rather than placing comments on the same lines as keyword
statements. Use a semi-colon (;) as the first character of the comment line.
A SQLWindows application reads the SQL.INI only when it initializes. To force an application to see any
changes you make to the file, you must stop or exit from all Gupta software running on the client (including
SQLWindows and all running applications created with SQLWindows), then restart the application.

SQL.INI Keywords to Use


To learn how to set up the SQL.INI file, read the appropriate chapter for the database to which you are
connecting.
The SQL.INI file must always have at least one comdll statement identifying the SQLRouter to be used when
connecting to a database.

Testing the Connection


To test the connection between the Gupta software running on your client machine and the database, run
SQLTalk for Windows and try to view a table you know is in the database and that you have permission to
look at.
Make sure to verify that you can connect to the target database using the tools provided by your database (or
other) vendor before using SQLTalk.

Testing the Connection with SQLTalk


1. Start SQLTalk for Windows by running the executable file sqltalk.exe in the Gupta installation directory.
2. Select Session > Connect from the SQLTalk for Windows menu bar to display the Connect dialog box. Fill
in the database name, your user name, and your password.
3. Verify that the connection information appears in the Current Connections box of the Connect dialog
box—this confirms that you have successfully connected to the database. Click Close in the Connect
dialog box.
4. Select File > Exit from the SQLTalk for Windows menu bar.
If you see the connection information in the Current Connections box of the Connect dialog box, you have
correctly configured your client machine to communicate with the target database from a SQLWindows
application.
For more information on how to use SQLTalk, read the Gupta online help topics for SQLTalk.

31
Connecting to Sybase
You do not need to run catalog view scripts before running your SQLWindows applications against Sybase
System 11.x and higher, because SQLRouter/Sybase can access the system catalog directly whenever
necessary.
However, you need to obtain suitable privileges for each Sybase database you will connect to before you can
run SQLWindows or an application you create with SQLWindows.
For each such database:
1. Log in as a user who has System Administrator privileges.
2. Run the program SYBPRIVS.EXE (located in the Gupta installation directory).

Connecting to Oracle
To connect to Oracle, you may have to make certain changes to your Windows Registry. You must also create
certain views to access tables and to run stored procedures.

Modifying the Registry


To allow your Oracle client-side software (OCI libraries and SQL*Net) to work together properly, you need to
modify the Oracle section of your Windows Registry. If you are using version 8.0.x of the Oracle client-side
software, add one of the following entries to the Registry:

Client Side Entry to Add to Registry

Version 8.0.x ORAOCI=ORA83.DLL and


ORAOCI=ORA84.DLL

Oracle version 9.x does not require any modifications to the Registry.

Creating Views
Before you can access tables on Oracle you must run the appropriate views script. You must also run a specific
views script to be able to execute stored procedures on Oracle.

Accessing tables and stored procedures on Oracle 8 and 9


To access tables on Oracle 8 or 9, you must execute the script VIEW8.ORA. To execute stored procedures, you
must execute the script PLSVIEW8.ORA. These scripts are located in the Gupta installation directory.
When you run the script, sign on as SYS (or another user with the same authority as SYS).
Also, if you need to access the SYS account, add a line with the substitute keyword in the [oragtwy] section of
file SQL.INI as follows:
substitute=SYSSQL.,
Put a period after SYSSQL, then a comma, and then press the RETURN key.
For more information about using the substitute keyword with Oracle, read substitute on page 5-10.

32
Running a view script
1. Start SQLTalk from the Gupta Team Developer group in the Program menu, or by running the executable
file sqltalk.exe in the Gupta installation directory.
2. Select Session,Connect from the menu bar. Fill in the dialog box with the appropriate information.
3. Once you are connected, click Close.
4. Select File, Open from the menu bar.
5. In the File of Type selection box, select All Files (*.*).
6. Select the file VIEW8.ORA (or PLSVIEW8.ORA) in the Gupta installation directory if using Oracle8. Select
the file VIEW7.ORA (or PLSVIEW7.ORA) if using Oracle7.
7. Select Session, Execute Script from the menu bar. Let the script run to completion. (Ignore any error
messages about tables being dropped.)
8. Select File, Exit from the menu.
For more information on how to use SQLTalk, read the Gupta online help topics for SQLTalk.

Specifying the Database in Your Application


In your SQLWindows application, you can identify the name of the database to connect to with the
SqlDatabase system variable. For example, if the name of the database is ORDERS, include the following
statement in your application before called SqlConnect:
Set SqlDatabase=’ORDERS’
Make sure the name of the database you assign is exactly the same as the name you assign to the keyword
remotedbname in the SQL.INI file (see the section SQL.INI keywords in the chapter that discusses your target
database). If you are using ODBC, make sure the name you assign to SqlDatabase is the name you used when
setting up the data source (see “Configuring an ODBC Data Source” on page 28).

Troubleshooting Problems
This section discusses some of the problems that can arise when connecting SQLWindows applications to
your database and possible ways to resolve them. It also has a table of some of the errors you may encounter
when running SQLWindows applications along with suggestions for fixing them.

Problems Connecting
When a SQLWindows application initializes, it reads the SQL.INI file. When the application reads the
[win32client.dll] section of that file, it loads all the DLLs specified in the comdll statements.
The application tries to connect to the database by calling the appropriate function in each DLL loaded. If the
DLL finds the requested database, it issues a database connect request. If the DLL gets no response, it returns
the “failure to connect” error to the application, which then calls the appropriate function in the next DLL
that was loaded. This continues until either a connection is established or all the DLLs have been tried.
If you have trouble connecting your SQLWindows application to your target database:

33
1. Check with your database administrator that the target database is running.
2. Verify that you can connect to the database from your client machine, first with native client database
tools (or equivalent), then with SQLTalk for Windows.
3. Read the section on searching for SQL.INI earlier in this chapter. If necessary and practical, temporarily
rename other copies of SQL.INI so that your desired copy is the only one with the original name. Don’t
forget to reverse the renaming process after you have determined the source of the trouble.
If you have multiple SQL.INI files on your machine (which is possible if you install different Gupta
Technologies products into different directories), the file you modify might not be the one being used at
initialization time. Check the order in which directories are searched for the SQL.INI file (see “Search Path
for SQL.INI” on page 29).
4. Check that the appropriate comdll statements appear in the [win32client.dll] section of the SQL.INI file.
(Read the SQL.INI keywords section in each chapter that discusses a database to which you are
connecting for more information about the comdll keyword.)
5. Check that you configured your remotedbname statements correctly. (This step does not apply if you are
connecting to your database using ODBC.)

Truncation of LONG Data


If you find that data from LONG data columns is being truncated, you may not have set the value for the
longbuffer keyword high enough in the SQL.INI file. For more information, read the longbuffer reference page
in the appropriate chapter for the database you are connecting to.

Logging ODBC API Calls


If you connect to your database using ODBC, you can trace the sequence of API calls made on behalf of your
SQLWindows application by including the statement odbctrace=on and the statement odbctracefile=filename
in the SQL.INI file. Read the odbctrace and odbctracefile reference pages in the appropriate chapter for the
database you are connecting to for more information.

Checking the Release Notes


The Release Notes that accompany this product contain useful information about:
• Problems—All known bugs fixed in a particular release, as well as those still outstanding are listed. If the
bug has been reported, you can ask Gupta Technologies for status on it.
• Certified environments—Certified environments and software are those that have actually been tested by
Gupta Technologies, or that have been configured and tested by other vendors using the Gupta DCC
(Database Connectivity Configuration) facility. (For more information about DCC, read Chapter 7,
Connecting to Databases using ODBC.) Note that the matrix of certified database versions and operating
system versions is available on our web site, http://support.guptatechnologies.com.

Contacting Technical Support


Before contacting technical support with a problem, please record the following information:
• Version numbers of:
 Microsoft Windows
34
 Gupta SQLWindows
 Gupta SQLRouters
 The communication software
 The database server
• Version and date of the SQLWNTM.DLL dynamic link library.
• Amount of available memory and operating system resources.
• Name of the component with the problem (name of executables running, or GPF, or error message).
• The exact text of any error messages.
• Contents of the following files:
 SQL.INI
 Client-side initialization files for your database
 Any log files
Determine if you can reproduce the problem consistently. Write down the steps so you can help your
technical support engineer reproduce the problem. A sample application that simply and reliably recreates
the problem is the quickest and most efficient way to get your problem resolved.
If you are reporting a documentation problem, note the page number in the printed book, or the file name
and topic title in on-line help.

Selected Errors and Ways to Fix Them


The following table lists some of the errors that can be generated when running a SQLWindows application,
and suggests possible remedies.

Problem Reason Remedy

111 — SQL Application has re-entered the library Check for any re-entrancy (for
application interface SQLWNTM.DLL while waiting for a example, SAM_Timer messages
recursively entered. server request. SQLWNTM.DLL is re- during asynchronous operations)
entrant only from different and disable them during server
applications. requests.
135 — Invalid cursor A SQL handle was passed to a function Establish a valid connection before
handle. before a valid connection was passing a SQL handle to a function.
established.
201 — No compiled The application is attempting to Compile SQL statements before
command. perform an operation, and either it has executing them.
not compiled an SQL statement, or no
statement exists any longer (for
example, because a ROLLBACK was
executed).
203 — Invalid SQL You constructed an SQL statement with Check the syntax of your SQL
statement. illegal syntax, or spaced the statement statement. Check the concatenation
over several lines without of multi-line SQL statements.
concatenating the parts of the
statement correctly.
321 — Insert or update The converted binary value exceeds Make sure the longbuffer setting is

35
Problem Reason Remedy

value too long. either the capacity of the not lower than the communications
communications API or the current API value.
longbuffer setting.
9279 — Cannot find Usually this message means that the Check that the comdll statements
specified protocol specified protocol cannot find the entry are correctly specified in the SQL.INI
entry. it is looking for in the [win32client.dll] file. Check that you have supplied all
section of the SQL.INI file. the needed keywords in the SQL.INI
file that apply to the SQLRouters you
are using.
30002 — Invalid The SAL functions used to invoke stored Make the calls in the correct
function call sequence procedures were called in an invalid sequence. (Read the Stored
sequence. procedures section of the chapter
for your database for more
information.)
30003 — Invalid An attempt was made to get the next The end of result sets can be
attempt to obtain a result set from a stored procedure even detected by checking the
subsequent result set. though there were no more result sets. bEndOfResult flag in the parameter
list for the SAL function that gets the
next result set. Error code 30003 is
returned if SqlExecute is called even
though bEndOfResult was TRUE.

Sample Applications
Several sample SQLWindows applications are installed in the SAMPLES subdirectory of the installation
directory for SQLWindows.
The sample applications listed in this section illustrate various aspects of coding a SQLWindows application
in conjunction with a particular router and database. The list of sample applications in this section are
organized by target database.

Microsoft SQL Server


The following two sample applications illustrate the calling of stored procedures on Microsoft SQL Server
that return result sets.
• ODBSAL1.APP gives an example of calling the SAL functions OdrExecuteProc and OdrGetNextResults.
• ODBSAL2.APP gives an example of using the SAL functions OdrPrepareProc and OdrPrepareNextResults.
For more information about these functions, read the section Stored Procedures in chapter 4.
Before running these applications, be sure to install (on the server) the sample PUBS database supplied with
Microsoft SQL Server.

36
Oracle
The sample application ORADEMO.APP shows how to use the SAL function SqlPLSQLCommand to call an
Oracle stored procedure. For more information about this function, read the section Stored procedures on
page 5-12.

Sybase System 11.x


The following sample applications illustrate features you might use when running a SQLWindows application
against Sybase System 11.x:
• SQLSYBS1.APP shows how to use the SAL extensions supported by SQLRouter/Sybase.
• SQLSYBS2.APP shows how to use the SAL extensions supported by SQLRouter/Sybase.
• SQLSYBS3.APP shows how to use the specific SAL extensions
• SybPrepareProc and SybPrepareNextResults.
• SQLSYBS4.APP shows how to SELECT and INSERT binary data (for example, IMAGE data) using the
function SqlSetLongBindDatatype.
• SQLSYBS5.APP shows how to insert or update long binary data (for example, IMAGE data) using the
function SybWriteText.
For more information about the extended SAL functions for Sybase System 11.x and related information, see
“Stored Procedures” on page 99, “Writing and Retrieving IMAGE and TEXT Data” on page 106, and “IMAGE
Column Types” on page 18.

37
Chapter 3 – Connecting to Informix
This chapter describes how to connect SQLWindows/32 applications to Informix databases.
It is recommended that you read all of Chapter 1 – Overview and Chapter 2 – Initializing and Testing Your
Connection before you read this chapter.
You connect SQLWindows/32 applications to Informix databases using the native router, SQLRouter/Informix.

Chapter Contents
The following features of Informix can affect the way you write your SQLWindows/32 application or the way it
behaves when you connect to it with SQLRouter/Informix:
• “Arithmetic Functions” on page 38
• “Autocommit” on page 39
• “Cursor Context Preservation” on page 39
• “Cursors” on page 39
• “Data Types” on page 39
• “DDL Statement Support” on page 40
• “Empty Strings” on page 40
• “Isolation Levels” on page 41
• “Lock Time-Out” on page 41
• “Miscellaneous Features” on page 41
• “Positioned Updates” on page 41
• “SET Statement Support” on page 42
• “SQL.INI Keywords” on page 42
• “Stored Procedures” on page 45
• “Storing TEXT and BYTE Data” on page 47

Arithmetic Functions
You can use the following arithmetic functions in your SQLWindows/32 applications:
• ABS, ACOS, ASIN, ATAN, ATAN2, COS, EXP, LOGN, LOG10, MOD, POW, ROOT, SIN, SQRT, TAN

38
Autocommit
Informix databases support autocommit. Autocommit is off by default. Informix has three database modes:
default, ANSI, and Transaction. The default mode can be further configured into two submodes: log on and
log off. These modes (and submodes) are configured by the database administrator—you cannot configure
these from your application.
With the default mode in the log off submode, all the SQL statements you execute in your application are
committed automatically, regardless of whether you have turned autocommit on or off in your application.
With the default mode in the log o submode, or the ANSI or Transaction modes, if you turn autocommit on,
all the SQL statements you execute in your application are committed automatically; if you turn autocommit
off, your application must explicitly commit or roll back any SQL statements you have executed.
For more background information, see “Autocommit” on page 12.

Cursor Context Preservation


Informix supports cursor context preservation (CCP). In SQLWindows/32 applications that connect to
Informix, CCP is off by default.
When CCP is on, cursors are declared using the WITH HOLD option, ensuring that the cursor remains open
even after a COMMIT. The cursor is not closed until the CLOSE cursor command is issued.
If CCP is on (and autocommit is off), you can continue to execute prepared SQL statements (without preparing
them again) even after you commit the transaction that those statements are a part of. If CCP is off, however,
you cannot re-execute those SQL statements once you commit the transaction without preparing them again.
Further, if you roll back the statements, you cannot re-execute them even if CCP is on—you must prepare
those statements all over again before you can re-execute them.
For background information, see “Cursor Context Preservation” on page 15.

Cursors
A SQLWindows/32 application that connects to Informix has no limit on the number of cursors it can create
and use. You can connect to more than one database and more than one database server in the same
application.
You cannot open a cursor WITH OPTIMIZATION or READ ONLY.
For related information, see “Handles, Contexts, and Connections” on page 17.

Data Types
The following table shows the mapping between the data types supported by a SQLWindows/32 application
and the data types supported by an Informix database server.

39
Informix Data Type

STRING CHAR (default is 1) VARCHAR (online only) INTERVAL

LONG STRING TEXT (255 or more bytes; online only) BYTE (255 or more bytes; online only)
CHAR (255 bytes to 32,767 bytes; online only) CHAR (255 bytes to 32,511 bytes;
SE only) VARCHAR (255 bytes; online only)
NUMBER SMALLINT (-215 to 215-1)
SERIAL (1 to 231-1)
INTEGER (-232 to 231-1) SMALLFLOAT
FLOAT
DOUBLE PRECISION REAL
DATE/TIME DATETIME YEAR TO DAY DATETIME HOUR TO FRACTION DATETIME YEAR TO
FRACTION

If you are using the SE database, you cannot CREATE or ALTER a table that has a VARCHAR column.

DDL Statement Support


You can use the following DDL (Data Definition Language) statements in your SQLWindows/32 application:
• Enhanced CREATE INDEX statement.
• Cascading deletes.
• FRAGMENT BY clause in CREATE TABLE statement.
• FRAGMENT BY EXPRESSION in CREATE INDEX statement.
• ALTER FRAGMENT statement.
• ADD ROWIDS and DROP ROWIDS clauses in ALTER TABLE statement.
• Optional specification of RESTRICT/CASCADE on the DROP TABLE, DROP NEW, REVOKE statements.

Empty Strings
On Informix, if you try to insert the empty string into a varying-length character string column (or try to
update such a column with the empty string), the database stores a space instead.
For background information, see “Nulls, Empty Strings, and Spaces” on page 21.

GET DIAGNOSTICS Statement


You cannot execute the GET DIAGNOSTICS statement against an Informix database server from a
SQLWindows/32 application.

40
Isolation Levels
Informix supports the isolation levels shown in the following table:

SQLWindows Parameter Informix Isolation Level

RO Dirty Read

RL Committed Read

CS Cursor Stability

RR Repeatable Read

To set the isolation level in your application, call SqlSetIsolationLevel. For example, to set the isolation level to
Cursor Stability, put the statement in your SQLWindows/32 application:
Call SqlSetIsolationLevel(hSql,’CS’)
See the Function Reference for more information about this function.
For general information on isolation levels, see “Isolation Levels” on page 19.

Lock Time-Out
Informix supports lock time-outs. To specify “no wait”, specify a time-out value of zero. To specify “wait
forever, specify a time-out value of –1.
For general information on lock time-outs, see “Lock Time-Out” on page 21.

Miscellaneous Features
You can use the following Informix database features in your SQLWindows/ 32 applications:
• New data distribution features
• DBINFO utility function
• TRIM string function

Positioned Updates
A SQLWindows/32 application can do positioned updates against an Informix database.
For more information, see “Positioned Updates” on page 23.

41
SET Statement Support
You can use the following SET statements in your SQLWindows/32 application when connecting to an
Informix database:
• SET DATASKIP statement
• SET PDQPRIORITY statement
• SET TRANSACTION statement

SQL.INI Keywords
This section contains the keywords you need or might want to use in the SQL.INI file when connecting a
SQLWindows/32 application to an Informix Database.
For general information about SQL.INI, see “Initializing SQLWindows Applications” on page 28.

buffrow
Use this keyword to control the buffering of data between your SQLWindows application and
SQLRouter/Informix.
Section [infogtwy]
Default 0
Syntax buffrow=number_of_rows
Description This keyword controls the buffering of data between your SQLWindows/32 application
and SQLRouter/Informix. By decreasing the buffrow value, response time can be
improved significantly. In contrast, larger values increase data throughput, but lengthen
the response time.
The values for buffrow can range from 0 through 32767. Assigning a value does not
guarantee that the current INMESSAGE is of sufficient size to fulfill the request. In these
cases, the number of buffered rows may be considerably less.
The default value of zero causes SQLRouter/Informix to revert to buffering data based on
the number of rows which will fit within the current INMESSAGE buffer size.
Example This statement instructs SQLRouter/Informix to perform single row fetches into the
INMESSAGE buffer:
buffrow=1
Notes The buffrow keyword has no effect on the manner in which data is buffered and
transported across the network by Informix-Net.
You can also configure the buffering of data for individual SQL handles using the
SqlSetParameterAll function. Assuming nBuffrow has already been assigned the value
desired for SQL handle hSql, you can make the following call:
SqlSetParameterAll(hSql,DBP_BUFFROW,nBuffRow,FALSE,TRUE)

42
comdll
Identify the SQLRouters available between SQLWindows/32 (or a client application created with
SQLWindows) and the database.
Section [win32client.dll]
Syntax comdll=communication_dll_name
Description This keyword identifies the filename of a SQLRouter DLL. You can put more than one
comdll statement in the SQL.INI file. If you use InformixNet 5.x, the value of
communication_dll_name to use for SQLRouter/Informix is sqlirt32. If you use
InformixNet 7.x, the value to use is sqlifx32.
Notes For related information, read Connect search order on page 1-6

defconnect
Use this keyword to connect using information in the INFORMIX.INI file.
Section [infogtwy]
Default off
Syntax defconnect={on|off}
Description This keyword lets you connect to an informix database using the user name, password,
service, and hosted listed in the INFOMIX.INI file. You specify this information with the
Informix SETNET utility.
Example This statement instructs SQLRouter/Infomix to connect using the information in
INFORMIX.INI
defconnect=on

log
Use this keyword to define the database router activity log and enable logging.
Section [win32client.irt32] or [win32client.ifx32]
Syntax log=fully_qualified_path_name
Description Specify any valid file pathname after the keyword. If the file does not exist, it is created. If
the file already exists, it is overwritten (unless the /Fa option is specified see below).
Once the log statement has been added to the SQL.INI file, logging is performed
automatically.
You can specify one or more of the options listed below after the fully qualified
pathname to customize the output:
/Bx — exclude bind variable values (use [win32client.ifx32] if you are using comdll
SQLIFX32 or [win32client.irt32] if you are using SQLIRT32)
/Fa — append to existing file
/FD — display fetch data
/Ld — display LONG data read or written
/Tx — exclude timestamps

43
Enter the options exactly as shown, keeping upper-case letters in upper-case and lower-
case letters in lower case.
Example This example calls for a log file that does not include timestamps on entries (to conserve
disk space), and displays the input and output of all LONG data (including TEXT and
IMAGE data items).
[win32client.irt32] log=c:\gupta\informix.log /Tx /Ld

longbuffer
Use this keyword to specify the maximum number of bytes of long data type columns to retrieve or send.
Sections [infogtwy]
Default 32 Kbytes. Setting a value less than the default will cause the value to be reset to the
default.
Syntax longbuffer=number_of_bytes
Description This keyword sets the size of the buffer that holds LONG data. The maximum size is
limited only by operating system constraints. Normally, the optimal size that avoids data
loss is the largest column of LONG data to be sent or retrieved by the SQLWindows
application. A larger buffer uses more memory but reduces network traffic. A smaller
buffer uses less memory but increases network traffic. If the buffer is too small to hold all
of the data, the excess is truncated.
It is recommended that you not specify a value for longbuffer larger than the maximum
LONG column size in any of the tables stored in any of those databases.
Example This statement sets to 2K the maximum number of bytes of LONG column data to retrieve
from or send.
longbuffer=2000
Notes You can also set this variable in a SQLWindows/32 program by calling SqlSetParameterAll
with the DBP_LONGBUFFER parameter (defined in SQLNWKCN.APL). This allows you to
tailor the size of the buffer to the application.

remotedbname
Use this keyword to specify the name of the database your SQLWindows/32 application connects to, the
connect string it uses, and (optionally) the host where the database is located and the service through which
you access it.
Section [infogtwy]
Syntax remotedbname=db_name1,connect_string[, -hhostname [ -
sservicename]]
Description db_name specifies the database. The name may not be more than 8 characters long.
connect_string is the INFORMIX-SQL DATABASE statement. This statement is case
sentitive.
If you do not specify hostname and servicename, SQLRouter/Informix uses the values for these names
specified in the Informix initialization file INFORMIX.INI. (You can modify these names
with the Informix SETNET utility.) If you are using InformixNet 7.2, use the SETNET32
utility which modifies entries in the registries.

44
You can specify only one hostname and servicename in INFORMIX.INI. To connect to multiple servers
concurrently, you must specify additional hostnames and servicenames using the -h and -
s flags by including additional remotedbname statements in the SQL.INI file.
Examples: This example identifies a database that SQLWindows/32 applications know as STORES
and that the Informix database server menlo knows as STORES2. The database is located
on a host named jupitor using the service named turbo.
remotedbname=STORES, DATABASE”STORES2@menlo”, hjupitor –sturbo
This example is just like the preceding one, but the service name is not specified; the
value is taken from the INFORMIX.INI file.
remotedbname=STORES, DATABASE”STORES2@menlo”, -hjupitor
This example is like the preceding one, but the host and service names are both taken
from the INFORMIX.INI file.
remotedbname=STORES, DATABASE”STORES2@menlo”
This example identifies a database that SQLWindows/32 applications know as STORES.
The connect string specifies the database server name menlo and the path of the
database STORES2. The hostname and the service name are the defaults specified in the
INFORMIX.INI file.
remotedbname=STORES, DATABASE”//menlo/sd3/infse41/stores2”

yieldonservercall
Use this keyword to turn asynchronous processing in Informix I-Net on or off.
Sections [infogtwy]
Default off
Syntax yieldonservercall={on|off}
Description This statement instructs SQLRouter/Informix to share the CPU with other applications or
other modules of your application. By setting this keyword to “off” (the default), I-Net
lets other applications use the CPU while it waits for the server to complete the
operation it sent.
If your SQLWindows/32 application does asynchronous processing (for example, by
calling SalPostMsg to handle SQL statements) and you set yieldonservercall to off (or
took the default), you may get the message:
Error#111—SQL Application Programming Interface(API) recursively
entered.
To prevent this, set yieldonservercall to on.
Example This example allows Informix I-Net to do asynchronous processing.
yieldonservercall=on

Stored Procedures
Informix stored procedures are programs written in Informix Stored Procedure Language (SPL). Once you
create a procedure, it is stored in an executable format on the database server.
45
Procedures and triggers are database objects, just like tables; therefore, anyone with appropriate privileges
on a procedure may execute it.
If the stored procedure returns a result set, call SqlFetchNext to get rows from that result set.

Creating Stored Procedures


You can create a stored procedure in one of two ways:
Call SqlImmediate(’create procedure p (custnum int) returning char(20);
define company_name char(20); select company into company_name
from customer where customer_num=custnum;
return company_name; end procedure;’)
or:
Call SqlPrepare(hSql,’create procedure p (custnum int) returning char(20);
define company_name char(20); select company into company_name
from customer where customer_num=custnum; return company_name;
end procedure;’)
Call SqlExecute(hSql)

Executing Stored Procedures


You can execute stored procedures in three different ways:
Call SqlRetrieve(hSql,’p’,’:nCustNum’,’:strLBItem’) Call SqlExecute(hSql)
! Need next line if stored procedure gets a result set. SqlFetchNext(hSql,
nRetVal)
or:
Call SqlPrepare(hSql,’execute procedure p(101)’) Call SqlExecute(hSql)
! Need next line if stored procedure gets a result set. SqlFetchNext(hSql,
nRetVal)
or:
Call SqlImmediate(’execute procedure p(101)’)
If another user owns the procedure, you must fully qualify the procedure name using the syntax
user_name.procedure_name. For example, refer to procedure p owned by user informix with informix.p, as
in:
Call SqlRetrieve(hSql,’informix.p’,’:nCustNum’,’:strLBItem’)
Do not use SqlImmediate if the stored procedure returns result sets. SqlImmediate creates a hidden handle,
which disappears after the function completes; you need an explicit handle to call SqlFetchNext and retrieve
rows from a result set.

Dropping Stored Procedures


You can drop stored procedures in two ways:
Call SqlImmediate(’drop procedure p’)
or:
Call SqlPrepare(hSql,’drop procedure p’) Call SqlExecute(hSql)

46
More information on stored procedures
For general information about stored procedures, see “Stored Procedures” on page 26.

Storing TEXT and BYTE Data


When you update or insert TEXT and BYTE columns, you must use LONG STRING variables in your
SQLWindows/32 application to hold the data you will use to do the UPDATE or the INSERT. Once you have
prepared the UPDATE or INSERT statement, call the function SqlSetLongBindDatatype to bind the data in the
LONG STRING bind variable to TEXT or BYTE data, then execute the prepared UPDATE or INSERT statement.
For the second argument to the function SqlSetLongBindDatatype you must specify the integer constant 22
for TEXT columns and the integer constant 23 for BYTE columns.
The integer constant 24 is the correct second argument to the function SqlSetLongBindDatatype when
updating CHAR and VARCHAR data longer than 254 bytes. However, SQLWindows/32 does the appropriate
binding for this kind of data by default when connected to an Informix database; you do not have to call the
function SqlSetLongBindDatatype when inserting and updating CHAR and VARCHAR data longer than 254
bytes.
For example:
Constants
System
User
! Make the code self-documenting with symbolic constants.
Number:BINDTEXT=22
Number:BINDBYTE=23
...
Local variables
SqlHandle:hSql
Number:nNumber
Long String:sLong

Actions

Call SqlConnect(hSql)
If not PrepareAndExecute(hSql,'CREATE TABLE longt(n FLOAT, s TEXT)')
...
If not SqlPrepare(hSql,'INSERT INTO longt

Unicode
VALUES(:nNumber,:sLong)')
...
If not SqlSetLongBindDatatype(2, BINDTEXT)
...
If not SqlExecute(hSql)
...
You must first prepare the INSERT or UPDATE statement, and then call SqlSetLongBindDatatype before you
execute the statement. The binding from the function SqlSetLongBindDatatype lasts only until your
application prepares the next SQL statement.

47
For more information on SqlSetLongBindDatatype, refer to the SQLWindows/32 Function Reference and the
online help.
Unicode support has been certified for Informix 11.5 using Team Developer 5.2 SP1 and above.
To use this unicode support, the following must be in place:
• Database created must be a UTF8 database.
• Informix server locale environment variables must be UTF8. For example:
 CLIENT_LOCALE=EN_US.utf8
 DB_LOCALE=EN_US.utf8
 SERVER_LOCALE=EN_US.utf8
 DBLANG_EN-US.utf8
Use the informix setnet32 utility to set these environment variables.
• Your Informix client machine should match these settings.
• You need to add the sql.ini entry “unicodedb=on” to your informix section of your sql.ini; for example:
[infogtwy]
remotedbname=testdb,DATABASE"testdb",-h192.168.146.119 - s1528 -vqadbs3
longbuffer=102400 buffrow=0 defconnect=off yieldonservercall=off unicodedb=on

Supported Data types


The following informix datatypes are supported:
• CHAR, NCHAR, VARCHAR, NVARCHAR, and TEXT

48
Chapter 4 – Connecting to Microsoft SQL
Server
This chapter describes how to connect SQLWindows applications to Microsoft SQL Server version 6.5 and
above.
It is recommended that you read all of Chapter 1 – Overview and Chapter 2 – Initializing and Testing Your
Connection before you read this chapter.
You connect SQLWindows applications to ODBC data sources with the SQLWindows ODBC router,
SQLRouter/ODBC. This router conforms to the ODBC specification up to core levels 1 and 2 (ODBC 2.0 level
within the 3.0 specification). See Chapter 8 – Connecting to Multiple Databases Concurrently for more on the
ODBC driver.

Autocommit
When you connect to Microsoft SQL Server 6.5 and 7.0, autocommit is on by default.
Data Definition Language (DDL) statements, such as CREATE TABLE, are always committed immediately, even
if autocommit is off.
If autocommit is off, and you either disconnect from the database or exit the application (whether normally
or abnormally) before committing or rolling back a pending transaction, the ODBC driver sends a ROLLBACK
request to the database server.
For background information on autocommit, see “Autocommit” on page 12.
For information on autocommit and cursor context preservation, see the next section, “Cursor Context
Preservation”.

Cursor Context Preservation


Microsoft SQL Server supports cursor context preservation (CCP). In SQLWindows applications that connect to
Microsoft SQL Server, CCP is off by default.
If CCP is on (and autocommit is off), you can continue to execute prepared SQL statements (without preparing
them again) even after you commit the transaction that those statements are a part of. If CCP is off, however,
you cannot re-execute those SQL statements once you commit the transaction without preparing them again.
For background information, see “Cursor Context Preservation” on page 15.

Data Types Supported


A SQLWindows application supports all Microsoft SQL Server data types, including the following:

49
• decimal—The server can be configured to provide a maximum precision of 38 decimal digits (the default
maximum is 28 decimal digits). However, 22 decimal digits is the maximum precision that SQLWindows
supports for this data type.
• numeric—The server can be configured to provide a maximum precision of 38 decimal digits (the default
maximum is 28 decimal digits). However, 22 decimal digits is the maximum precision that SQLWindows
supports for this data type
• double precision—Both the server and SQLWindows support a maximum precision of 17 decimal digits
for this data type.
The decimal and numeric data types, which are identical, allow the exact representation of a fractional value.
This is unlike double precision or float (to which double precision is mapped), which typically allow only
approximate values.
The following table shows the data type mappings between SQLWindows and Microsoft SQL Server using an
ODBC connection:

SQLWindows Microsoft SQL Server


BINARY (fewer than 255 bytes)
CHAR (fewer than 255 bytes)
TIMESTAMP
VARBINARY (fewer than 255
bytes)
VARCHAR (fewer than 255 bytes
LONG STRING BINARY ARY (255 or more bytes) AR (255
or more bytes)
IMAGE
TEXT
VARBINARY (255 or more bytes)
VARCHAR (255 or more bytes)
BINARY VARIABLE AGE BINARY
VARBINARY
NUMBER BINARY DECIMAL
DOUBLE PRECISION
FLOAT
INT MONEY
NUMERIC REAL
SMALLINT
SMALLMONEY
TINYINT
DATE/TIME DATETIME
SMALLDATETIME

Note the following:


• When inserting data, you are only limited by the size of the database datatype.
• When fetching data into a string, the size of the database datatype needs to be < 255 bytes. However, this
is not the case when connecting via oledb.
• When fetching data into a Long String, the size of the database datatype needs to be >= 255 bytes.
However, this is not the case when connecting via oledb.
• The simple rule of thumb is to use a Team Developer Long String when trying to insert or fetch data into
SQLServer’s long datatypes:

50
 image
 binary
 varbinary
 text/ntext
 varchar/nvarchar >= 255
 char/nchar >= 255
• Beginning with Team Developer 5.2, you can use the binary variable when dealing with SQLServer’s binary
datatypes: image, binary, and varbinary. Unlike the method using Team Developer’s Long String, the binary
variable does not require you to call SqlSetLongBindDatatype.

Identity Column Type


SQL Server tables can also include an IDENTITY column. If a column of this type is defined when a table is
created, the values in this column (which you cannot update) are automatically incremented or
decremented (by an amount you can set) every time you create a new row. This makes this data type useful
for automatically generating serial numbers, order numbers, and other values.

Empty Strings
On Microsoft SQL Server 6.5 and 7.0, if you try to insert the empty string into a varying-length character string
column (or try to update such a column with the empty string), the database stores a space instead.
For background information, see “Nulls, Empty Strings, and Spaces” on page 21.

Isolation Levels
Microsoft SQL Server defines four isolation levels in terms of dirty reads, nonrepeatable reads and phantom
rows. (See Isolation Levels on page for the definitions of these terms.)
• SQL_TXN_READ_UNCOMMITTED — Dirty reads, nonrepeatable reads, and phantom rows are all possible.
• SQL_TXN_READ_COMMITTED — Dirty reads are prevented. Nonrepeatable reads and phantom rows are
possible.
• SQL_TXN_REPEATABLE_READ — Dirty reads and nonrepeatable reads are prevented. Phantom rows are
possible.
• SQL_TXN_SERIALIZABLE — Dirty reads, nonrepeatable reads, and phantom rows are all prevented
(transactions are serializable).
SQLWindows supports the RO, RL, CS, and RR isolation levels. When a SQLWindows application connects to
Microsoft SQL Server, these isolation levels are mapped to the SQL Server isolation levels as follows:

Isolation Level Microsoft SQL Server

RO SQL_TXN_READ_UNCOMMITTED

RL SQL_TXN_READ_COMMITTED

51
CS SQL_TXN_REPEATABLE_READ

RR SQL_TXN_SERIALIZABLE

Microsoft SQL Server maps SQL_TXN_REPEATABLE_READ to SQL_TXN_SERIALIZABLE; thus, CS is actually


equivalent to RR.
By default, a SQLWindows application runs against Microsoft SQL Server 6.5 and 7.0 with an isolation level of
RL.
For more background information, see “Isolation Levels” on page 19.

Keywords as Table and Column Names


To name a table or column in Microsoft SQL Server with a reserved keyword (such as SELECT or WHERE) in a
SQLWindows application, you must use lower-case or mixed-case letters. If you use all upper-case letters, you
get an error.

Lock Time-Out
The default value for lock time-out with Microsoft SQL Server is zero. This means there is no lock time-out.
For more background information, see “Lock Time-Out” on page 21.

Native Connectivity
Microsoft SQL Server 6.5 and 7.0 uses ODBC as its native call-level interface. Microsoft SQL Server
automatically maps SAL statements to the appropriate ODBC functions. No ODBC calls appear in your
SQLWindows applications.
The level of SQL syntax and semantics supported is Core. The ODBC API conformance is Level II.

Positioned Updates
SQLWindows applications can perform positioned updates (WHERE CURRENT OF cursor updates) when
connected to Microsoft SQL Server. To learn how to code a positioned update, see “Positioned Updates” on
page 23.
Important: You cannot do positioned updates on a result set returned by a stored procedure. You should
always disable result set mode before calling a stored procedure.
For additional information on stored procedures, see “Stored Procedures” on page 57. For background
information on result set mode, see “Result Set Mode” on page 24.

52
Result Set Mode
Microsoft SQL Server provides backend support for scrollable cursors (result set cursors that scroll both
backwards and forwards). Consequently, you cannot create front end result sets (FERS) if you turn on result
set mode.
You must turn on result set mode to have more than one result set open at the same time. If result set mode
is off, you must fetch all of the rows from one result set before you can SELECT another result set. If you try to
SELECT a result set before fetching all the rows of a previously retrieved result set, you get the error:
[Microsoft][ODBC SQL Server Driver]Connection is busy with result for another
hstmt.
By default, result set mode is off for better performance. Also, you must always turn off result set mode
before calling a stored procedure, even when connecting via ODBC or OLE DB.
For background information on result set mode and scrollable cursors, see “Result Set Mode” on page 24. For
more information on FERS, see “Front End Result Sets” on page 16.

SET Statement
Microsoft SQL Server supports the SET statement. You use this statement to set various query-processing
options for the duration of your work session (that is, until you disconnect), or for the duration of a stored
procedure.
When you execute the SET statement from a SQLWindows application, you do so by specifying a specific SQL
handle. Nonetheless, the option you set applies to all statements executed on your current connection to the
database, regardless of the SQL handle you specify when you execute those statements.
For example, you can use SET to limit the number of rows returned in a result set, such as:
SqlPrepareAndExecute(hSql1,’SET ROWCOUNT 10’)
If you then retrieve a result set, the database returns no more than 10 rows, even if you specify a handle
other than hSql1 when you execute the SELECT statement, for example:
SqlPrepareAndExecute(hSql2,’SELECT * FROM MYTABLE’)
Read the Microsoft SQL Server online documentation for further information about the SET statement.

SqlDirectoryByName Semantics
When a SQLWindows application has connected to Microsoft SQL Server, calling the function
SqlDirectoryByName returns all of the databases listed in the ODBC.INI file. This list is not necessarily the
same as the list of all databases accessible to Microsoft SQL Server.

SQL.INI Keywords
This section contains the keywords you need or might want to use in the SQL.INI file when connecting a
SQLWindows application to Microsoft SQL Server.
53
buffrow
Use this keyword to control the buffering of data between your SQLWindows application and Microsoft SQL
Server.
Section [odbcrtr]
Default 0
Syntax buffrow=number_of_rows
Description This keyword controls the buffering of data between your SQLWindows application and
Microsoft SQL Server. By decreasing the buffrowvalue, response time can be improved
significantly. In contrast, larger values increase data throughput, but lengthen the
response time.
The values for buffrow can range from 0 through 32767. Assigning a value does not
guarantee that the current INMESSAGE is of sufficient size to fulfill the request. In these
cases, the number of buffered rows may be considerably less.
The default value of zero causes Microsoft SQL Server to revert to buffering data based on
the number of rows which will fit within the current INMESSAGE buffer size.
Example This statement instructs Microsoft SQL Server to perform single row fetches into the
INMESSAGE buffer:
buffrow=1
Notes The buffrowkeyword has no effect on the manner in which data is buffered and
transported across the network.
You can also configure the buffering of data for individual SQL handles using the
SqlSetParameterAll function. Assuming nBuffrowhas already been assigned the value
desired for SQL handle hSql, you can make the following call:
SqlSetParameterAll(hSql,DBP_BUFFROW,nBuffRow,FALSE,TRUE)

comdll
Identify the SQLRouters available between SQLWindows (or a client application created with SQLWindows)
and the database.
Section [win32client.dll]
Syntax comdll=communication_dll_name
Description This keyword identifies the filename of a SQLRouter DLL. You can put more than one
comdll statement in the SQL.INI file. The value of communication_dll_name to use for
SQLRouter/ODBC is sqlodb32.
Example comdll=sqlodb32
Notes For related information, see “Connect Search Order” on page 14.

log
Define the database router activity log and enable logging.
Section [win32client.odb32]

54
Syntax log=fully_qualified_path_name
Description Specify any valid file pathname after the keyword. If the file does not exist, it is created. If
the file already exists, it is overwritten (unless the /Fa option is specified see below).
Once the log statement has been added to the SQL.INI file, logging is performed
automatically.
You can specify one or more of the options listed below after the fully qualified
pathname to customize the output:
/Bx — exclude bind variable values
/Fa — append to existing file
/FD — display fetch data
/Ld — display LONG data read or written
/Tx — exclude timestamps
Enter the options exactly as shown, keeping upper-case letters in upper case and lower-
case letters in lower case.
Example This example calls for a log file that does not include timestamps on entries (to conserve
disk space), and displays the input and output of all LONG data (including TEXT and
IMAGE data items).
[win32client.odb32] log=c:\gupta\mssqlsrv.log /Tx /Ld

longbuffer
Specify the maximum number of bytes of LONG column data to retrieve or send.
Sections [odbcrtr]
Default 32 Kbytes. Setting a value less than the default will cause the value to be reset to the
default.
Syntax longbuffer=number_of_bytes
Description This keyword sets the size of the buffer that holds LONG data. The maximum size is
limited only by operating system constraints. Normally, the optimal size that avoids data
loss is the largest column of LONG data to be sent or retrieved by the SQLWindows
application. A larger buffer uses more memory but reduces network traffic; a smaller
buffer uses less memory but increases network traffic. If the buffer is too small to hold all
of the data, the excess is truncated.
If specified, the longbuffer statement applies to all databases connected to using ODBC. It
is recommended that you not specify a value for longbuffer larger than the maximum
LONG column size in any of the tables stored in any of those databases.
Example This statement sets to 64K the maximum number of bytes of LONG column data to
retrieve from or send to any database connected to using ODBC.
longbuffer=65536
Notes You can also set this variable in a SQLWindows program by calling SqlSetParameterAll
with the DBP_LONGBUFFER parameter (defined in SQLNWKCN.APL). This allows you to
tailor the size of the buffer to the application.

55
odbctrace
Trace all calls to the ODBC API.
Section [odbcrtr]
Default off
Syntax odbctrace={on|off}
Description The Microsoft ODBC Driver Manager can trace all calls made to the ODBC API. The trace
information is saved in the log file whose pathname is given in the odbtracefile
statement.
Example This statement enables ODBC API call tracing:
odbctrace=on

odbctracefile
Specify the pathname of the trace file for ODBC API calls.
Section [odbcrtr]
Default root_directory\sql.log
Syntax odbctracefile=fully_qualified_filename
Description This keyword specifies the pathname of the trace file for ODBC API calls. This keyword
has no effect unless the odbtrace keyword is set to “on”(see “odbctrace” on page 56).
Example This statement specifies that the trace file is called odbc.log, and that it is located in the
\logs directory on the client’s c: drive:
odbctracefile=c:\logs\odbc.log

remotedbname
Use this keyword to specify database names and their associated connect strings.
Section [odbcrtr]
Syntax remotedbname=db_name1,connect_string
Description db_name1 specifies the database name as it is known by the SQLWindows application;
the name may not be more than 8 characters long.
connect_string is the exact connect string you will use to connect to the ODBC data
source. Typically, this entry begins with the string "DSN=" to specify the data source
name specified in the ODBC Administrator tool.
The value specified in connect_string can include embedded spaces. Do not put
comments on the same line with the "remotedbname" statement. Everything from the
comma following db_name to the end of line is interpreted as the connect string.
Examples: Assume you have defined two data sources in ODBC Administrator. One is for Microsoft
SqlServer 7.0 (with a data source name of "MS SqlServer 7.0"), and the other is for a
Visual FoxPro database (with a data source name of "Visual FoxPro DB"). The
remotedbname statements look like:
remotedbname=SS70,DSN=MS SqlServer 7.0 remotedbname=VFP,
DSN=Visual FoxPro DB
56
Your Team Developer application sets the variable "SqlDatabase" to either "SS70" (to
connect to the SqlServer data source) or to "VFP" (to connect to the FoxPro database),
set SqlUser and SqlPassword appropriately, and issue the SqlConnect() call.
SqlRouter/ODBC obtains all the necessary information from the configuration maintained
by the ODBC Administrator utility.
Notes: The remotedbname parameter is not necessary for applications built using Team
Developer. It is required if you want to use the SqlTalk utility to connect to the ODBC data
source.
When connecting to any ODBC data source where the data source name was configured
by the ODBC Administrator tool, your application can bypass the remotedbname
parameter altogether, and set the variable SqlDatabase to the actual ODBC data source
name. Using the above example, if you issue the following statements (assume SqlUser
and SqlPassword are set), your program will connect correctly to the Microsoft SqlServer
7.0 data source without using the remotedbname parameter in SQL.INI:
Set SqlDatabase = "MS SqlServer 7.0"
Call SqlConnect (hSql)

Stored Procedures
Procedures containing SQL statements and procedure logic can be compiled and stored on SQL Server. These
procedures can return more than one result set, and can accept input bind variables (host variables) as
arguments (see “Stored Procedures” on page 26).

Creating Stored Procedures


Microsoft SQL Server comes “out of the box” with a number of available stored procedures. See the SQL
Server documentation for more information.
The DBA (database administrator) can create additional stored procedures for you. The DBA needs to let you
know about any such stored procedures.
You can also create your own stored procedures from within a SQLWindows application. To do this, execute
the CREATE PROCEDURE statement on the server by calling the appropriate SAL function(s), such as
SqlImmediate, SqlPrepareAndExecute, or SqlPrepare followed by SqlExecute.
For more information on stored procedures and the CREATE PROCEDURE statement, see the Microsoft SQL
Server documentation.

Deleting Stored Procedures


The DBA can delete stored procedures on SQL Server by executing the DROP PROCEDURE statement. You can
also delete stored procedures from within a SQLWindows application. To do this, execute the DROP
PROCEDURE statement on the server by calling the appropriate SAL function(s), such as SqlImmediate,
SqlPrepareAndExecute, or SqlPrepare followed by SqlExecute.
For more information on the DROP PROCEDURE statement, see the Microsoft SQL Server documentation. For
information on SqlImmediate, SqlPrepareAndExecute, SqlPrepare, and SqlExecute, see the SQLWindows
Function Reference.

57
Executing Stored Procedures
Two new pairs of SAL functions have been introduced to support calling stored procedures.
The first pair is:
• OdrExecuteProc—Execute the specified stored procedure.
• OdrGetNextResults —Get the next result set (if any) from the stored procedure that was invoked with
OdrExecuteProc.
The second pair is:
• OdrPrepareProc—Prepare (compile) the invocation of the specified stored procedure.
• OdrPrepareNextResults—Prepare the (implied) statement that gets the next result set (if any).
You must turn off result set mode before calling a stored procedure.
Either SqlExecute or a function that implies execution of a prepared statement, such as SalTblPopulate, must
be called after calling OdrPrepareProc or OdrPrepareNextResults.
Once the result set has been retrieved, position the cursor to the first and subsequent rows in the result set
by calling the SqlFetchNext function. (For more information about this function, see the SQLWindows
Function Reference.)

Example Code
The code fragment below creates the stored procedure GetNames and stores it on the server. The procedure
returns all the values in the name column and the number column from the rooms table, and then all the
values in the name column from the guests table. The code invokes the stored procedure using the shorthand
ODBC procedure syntax. See the Microsoft ODBC 2.0 Programmer’s Reference and SDK Guide for more
information on the standard and shorthand syntax for calling stored procedures.
The current column values being fetched are stored in the INTO variables :strName and :nNumber from the
first result set, and the INTO variable :strName from the second result set.
Call SqlSetResultSet(hSql, FALSE)
Set strProcedure=’CREATE PROCEDURE GetNames AS ’ ||
’SELECT name,number FROM rooms ’ ||
’SELECT name FROM guests’
Call SqlPrepareAndExecute(hSql, strProcedure)
If OdrExecuteProc(hSql,’{call GetNames()}’,’:strName,:nNumber’) Loop
If SqlFetchNext(hSql, nRet)
...
Else If (nRet=FETCH_EOF)
If NOT OdrGetNextResults(hSql,’:strName’,
bEndOfResults)
If bEndOfResults
Break
In the above code fragment, the functions OdrExecuteProc and OdrGetNextResults were called.
Alternatively, you can prepare the invocation of the stored procedure as a separate step by replacing
OdrExecuteProc with:
If OdrPrepareProc(hSql,’{call GetNames()}’,’:strName’)
If SqlExecute(hSql)...
And you can replace the call to OdrGetNextResults with:
If NOT OdrPrepareNextResults(hSql,’:strName,:nNumber’,

58
bEndofResults)
If NOT bEndOfResults
If SqlExecute(hSql)...

Error Codes for Stored Procedures


Two new error codes have been defined for stored procedures:
• 30002—Invalid function call sequence
This error occurs when you do not call the functions in the correct sequence. The correct sequence is
OdrExecuteProc followed by one or more calls to OdrGetNextResults, or OdrPrepareProc followed by one
or more calls to OdrPrepareNextResults.
• 30003—Invalid attempt to obtain next result set
This error occurs when an attempt is made to get another result set from a stored procedure by calling
OdrPrepareNextResults followed by a call to SqlExecute, SalTblPopulate, or other function that executes a
prepared statement, even though there are no more result sets to retrieve.
The end of result sets can be detected by checking the bEndOfResult flag in the OdrGetNextResults and
OdrPrepareNextResults parameter list. Error code 30003 is returned if you attempt to retrieve another result
set even though bEndOfResult is TRUE.

SAL Extensions
This section describes the Scalable Application Language (SAL) extensions that allow you to work with stored
procedures on Microsoft SQL Server version 6.5 and 7.0.
For more information about stored procedures and ODBC, see the Microsoft ODBC 2.0 Programmer’s
Reference and SDK Guide. For more information about stored procedures and Microsoft SQL Server, see the
SQL Server online documentation (click the SQL Server Books Online icon in the SQL Server Tools program
group).

OdrExecuteProc
Syntax bOk=OdrExecuteProc(hSql,strProc,strIntoList)
Sql Handle: hSql
String: strProc
String: strIntoList
Parameters hSql—A handle that identifies a connection context to the database.
strProc—The text of the call to the stored procedure to be executed.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function executes a stored procedure on Microsoft SQL Server.
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to OdrPrepareNextResults. If you know the

59
stored procedure does not execute a SELECT statement, use strNull as the value for
strIntoList.
If a result set is returned, its associated cursor points to just before the first row of the
result set. To set the INTO variables in strIntoList to the column values of the first row, call
SqlFetchNext. To obtain subsequent rows in the result set, continue to call SqlFetchNext.
Once all the rows in a given result set have been retrieved, get the next result set (if any)
by calling OrdGetNextResults.
Limitations The function OdrExecuteProc does not accept bind variables. (Compare OdrPrepareProc
on page 61, which does accept bind variables.)
Notes See “OdrGetNextResults” on page 60.
Examples This example executes the stored procedure mysp. This procedure accepts no arguments,
and it returns a result set consisting of a single column. The call to the stored procedure
is specified using the shorthand ODBC syntax. The data type of the INTO variable :strOut
is assumed to match the column data type returned by the stored procedure.
Call OdrExecuteProc(hSql,’{call mysp()}’,’:strOut’)
See also the sample application ODBSAL1.APP in the SAMPLES subdirectory of the
SQLWindows installation directory.

OdrGetNextResults
Syntax bOk=OdrGetNextResults(hSql, strIntoList, bEndOfResults)
Sql Handle: hSql
String: strIntoList
Boolean: bEndOfResults
Parameters hSql—A handle that identifies a connection context to the database.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
bEndofResults—Boolean variable that indicates if there were no more result sets to be
retrieved.
Return Value bOK is set to TRUE if the function succeeds and to FALSE if it fails.
Description If the stored procedure invoked by calling OdrExecuteProc returns more than one result
set, use OdrGetNextResults to get the second and subsequent result sets.
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to OdrPrepareNextResults.
If a result set is returned, its associated cursor points to just before the first row of the
result set. To set the INTO variables in strIntoList to the column values of the first row, call
SqlFetchNext. To obtain subsequent rows in the result set, repeatedly call SqlFetchNext.
Once all the rows in a given result set have been retrieved, get the next result set (if any)
by again calling OrdGetNextResults.
If OdrGetNextResults is called and there are no more result sets, bOk is set to FALSE and
bEndOfResults is set to TRUE.

60
Notes See “OdrExecuteProc” on page 59.
Examples This example gets the next result set (if any) returned by the stored procedure invoked
with OdrExecuteProc. The parameter hSql denotes the same handle that was used in the
call to OdrExecuteProc.
Call OdrGetNextResults(hSql,’:strOut’, bEndOfResults)
See also the sample application ODBSAL1.APP in the SAMPLES subdirectory of the
SQLWindows installation directory.

OdrGetReturnStatus
Syntax bOk=OdrGetReturnStatus(hSql, Number_Variable_Name)
Parameters hSql — A handle that identifies a connection context to the database.
Return Value nReturn is the return value of the stored procedure.
Description This function gets the return value of a stored procedure You should compile the stored
procedure using OrdPrepareProc only. If you use OrdExecuteProc, then this function will
fail. If you use OrdExecuteProc, the number variable will be set to 0. It will not contain
the actual return status information.
Notes The calling status for the stored procedures is slightly different if you use
OrdGetReturnStatus. You have to use the complete ODBC syntax for stored procedures.
You have to specify the bind variable name which is expected to contain the return status
information in the call to OrdPrepareProc. See the code example below.
This function should be called only after all the result sets from this stored procedure
have been retrieved.
Examples The syntax used in OrdPrepareProc is different now. You have to specify a bind variable
name before Call in order to use OrdGetReturnStatus. If you call OrdGetReturnStatus
without specifying a variable name in OrdPrepareProc, no error is returned. But the
return variable will contain 0.
Call SqlConnect(hSql)
Call OrdPrepareProc(hSql,’ {:nReturn=call MySP (:param1,
:param2)}’,’:bind1’) Call SqlExecute(hSql)
While SqlFetchNext(hSql, nFetchInd)
<process fetched data>
Call OrdGetReturnStatus(hSql, nReturn)

OdrPrepareProc
Syntax bOk=OdrPrepareProc(hSql, strProc, strIntoList)
Sql Handle: hSql
String: strProc
String: strIntoList
Parameters hSql—A handle that identifies a connection to the database.
strProc—The text of the call to the stored procedure to be prepared.

61
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function prepares (compiles) the invocation of a stored procedure for subsequent
execution on Microsoft SQL Server. The prepared invocation of the stored procedure
must then be executed explicitly (by calling the SqlExecute function) or implicitly (by
calling, for example, the SalTblPopulate function).
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to OdrPrepareNextResults. If you know the
stored procedure does not execute a SELECT statement, use strNull as the value for
strIntoList.
Limitations Input bind variables in the argument list of the call to a stored procedure must be listed
explicitly using the variable name preceded by a colon. You cannot use parameter
markers (the question mark (?) syntax) as defined in ODBC. Also, input bind variables
cannot be of type LONG STRING.
Microsoft SQL Server allows a stored procedure to be defined to pass values back to the
calling program through output bind values, a function result, or both. If the stored
procedure has output parameters, strProc must include output bind variables in the
argument list to match those parameters. However, the values assigned to those output
parameters on the server do not get assigned to the output bind variables in your
SQLWindows application; the application never “sees” those values. After the call to the
stored procedure returns, the values of the output bind variables are undefined (see
“Retrieving Output Parameters from Stored Procedures” on page 64).
Likewise, a function result returned by the stored procedure never gets back to the
SQLWindows application. Consequently, you cannot assign the function result from a
stored procedure to a variable or use it in an expression.
Notes See “OdrPrepareNextResults” on page 62.
Examples This example prepares the stored procedure myproc. This procedure is assumed to
accept two input parameters—the bind variables :v1 and :v2 need to have been declared
of the appropriate type—and to return an initial result set consisting of a single column.
The call to the stored procedure is specified using the shorthand ODBC syntax. The data
type of the INTO variable :strOut is assumed to match the column data type of the first
result set returned by the stored procedure.
Call OdrPrepareProc(hSql,’{call myproc(:v1,:v2)}’,’:strOut’)
See also the sample application ODBSAL2.APP in the SAMPLES subdirectory of the
SQLWindows installation directory.

OdrPrepareNextResults
Syntax bOk=OdrPrepareNextResults (hSql, strIntoList, bEndOfResults)
Sql Handle: hSql
String: strIntoList
Boolean: bEndOfResults

62
Parameters hSql — A handle that identifies a connection to the database.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
bEndofResults — Boolean variable that indicates if there were no more result sets to be
retrieved.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description If the stored procedure specified in the call to OdrPrepareProc returns more than one
result set, use OdrPrepareNextResults followed by explicit execution (calling the
SqlExecute function) or implicit execution (calling, for example, the SalTblPopulate
function) to get the next result set.
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to OdrPrepareNextResults.
If a result set is returned, its associated cursor points to just before the first row of the
result set. To set the INTO variables in strIntoList to the column values of the first row, call
SqlFetchNext. To obtain subsequent rows in the result set, continue to call SqlFetchNext.
Alternatively, you can call SalTblPopulate to fetch the rows of data in the result set.
Once all the rows in a given result set have been retrieved, get the next result set (if the
stored procedure has another SELECT statement it can execute) by again calling
OrdPrepareNextResults.
If OdrPrepareNextResults is called and there are no more result sets, bOk is set to FALSE
and bEndOfResults is set to TRUE.
See Also OdrPrepareProc on page 61
Example This example gets the next result set (if any) returned by the stored procedure specified
in OdrPrepareProc. The parameter hSql denotes the same handle that was used in the
call to OdrPrepareProc.
Call OdrPrepareNextResult(hSql,’:strOut’, bEndOfResults)
See also the sample application ODBSAL2.APP in the SAMPLES subdirectory of the
SQLWindows installation directory.

Restrictions on Stored Procedures


When using stored procedures, the restrictions that follow apply.

Disable result set mode


Always disable result set mode before calling a stored procedure.

No scrollable cursors
The cursor associated with a result set returned by a stored procedure can scroll forward only.

No positioned updates
You cannot do positioned updates on a result set returned by a stored procedure.

63
No output values
When calling a stored procedure that has output parameters in its parameter list, you must include an output
bind variable (of the appropriate type) in the argument list of the call for each such parameter. However, the
values returned by the stored procedure on the server do not get assigned to those output bind variables on
the client, so your SQLWindows application cannot access those values. After the call to a stored procedure
returns, the values of any output bind variables are undefined.
Similarly, if the stored procedure returns a function result, that value cannot be accessed by your
SQLWindows application. Consequently, the return value cannot be assigned to a variable in your
SQLWindows application, or used in an expression.

Retrieving Output Parameters from Stored Procedures


Normally, when you execute a stored procedure that returns values in output parameters by calling
OdrPrepareProc followed by SqlExecute, you cannot retrieve those output values, even though you specify
bind variables as output parameters when you call the stored procedure. The only way a SQLWindows
application can retrieve data from a stored procedure is in the result sets (if any) that the stored procedure
returns.
You can work around this restriction by executing the stored procedure inside a batch of statements. The
batch of statements retrieves the output parameter from the stored procedure as a “result set”, then returns
that “result set” to your SQLWindows application. For example:
Call OdrPrepareProc(hSql,
‘declare @outparm int ‘ ||
‘exec GTIPROC_BASICS ‘ ||
‘@test = 1 ‘ ||
‘@result = @outparm output ‘ ||
‘SELECT @outparm’)
The following is a more complete example to illustrate calling a stored procedure and retrieving an output
parameter. In summary, for the stored procedure created below, you call:
• OdrPrepareProc (as shown above)
• SqlExecute to position to the first “result set” (the set of authors’ last names)
• SqlFetchNext to retrieve the rows in the first result set
• OdrPrepareNextResults, SqlExecute, and SqlFetchNext to get the “result set” from the SELECT statement
against table ‘foo’
• OdrPrepareNextResults, SqlExecute, and SqlFetchNext to retrieve the value of outparm—This is the last
result set generated as part of the command batch outside of the stored procedure.
Set strSqlCreate =‘CREATE PROC GTIPROC_BASICS ‘ ||
‘@test int, @result int output ‘ ||
‘AS SELECT au_lname FROM authors ‘ ||
‘SELECT c1 FROM foo where c1 = @test ‘ ||
‘SELECT @result = @test * 10 ‘ ||
‘RETURN -100 /* Success */‘
If TRUE
When SqlError
Return TRUE
Call SqlPrepareAndExecute(hSql,‘drop procedure GTIPROC_BASICS‘)
Call SqlPrepareAndExecute(hSql,strSqlCreate)
Set strSqlExecute = ‘declare @outparm int ‘ ||
‘exec GTIPROC_BASICS ‘ ||

64
‘@test = :nTest ‘ ||
‘@result = @outparm output ‘ ||
‘SELECT @outparm‘
Set nTest = 2
Call OdrPrepareProc(hSql,strSqlExecute, ‘:dfAu_lname‘)
Call SqlExecute (hSql)
While NOT nFetchInd
Call SqlFetchNext(hSql,nFetchInd)
Call OdrPrepareNextResults (hSql, ‘:dfFoo‘, bMoreResults)
Call SqlExecute (hSql)
Set nFetchInd = FALSE
While NOT nFetchInd
Call SqlFetchNext(hSql,nFetchInd)
Call OdrPrepareNextResults(hSql, ‘:dfOutParm‘, bMoreResults)
Call SqlExecute (hSql)
Set nFetchInd = FALSE
While NOT nFetchInd
Call SqlFetchNext(hSql,nFetchInd)

65
Chapter 5 – Connecting to Oracle
This chapter describes how to connect SQLWindows applications to Oracle databases using the Team
Developer native router, SQLRouter/Oracle. It describes (in alphabetical order) various features of Oracle that
can affect the way you write your SQLWindows application or the way it behaves when you connect to it with
SQLRouter/Oracle.
It is recommended that you read all of Chapter 1 – Overview and Chapter 2 – Initializing and Testing Your
Connection before you read this chapter.
You connect SQLWindows applications to Oracle databases using either the native router, SQLRouter/Oracle,
or the ODBC router, SQLRouter/ODBC. To learn about connecting with the native router, read this chapter. To
learn about connecting with the ODBC router, read Chapter 7 – Connecting to Databases Using ODBC.

Autocommit
Oracle database servers support autocommit. Autocommit is off by default.
If you turn on autocommit, both DML (Data Manipulation Language) statements (such as UPDATE, INSERT,
and DELETE) and DDL (Data Definition Language) statements (such as CREATE TABLE and DROP TABLE) are
committed immediately.
Note: With an Oracle database, DDL statements are always committed immediately, even if autocommit is off.
If you turn on autocommit, you cannot prepare a SQL statement, then execute it multiple times. Once you
execute the statement, the prepare context is lost and you must prepare it again. If you turn off autocommit
and turn on cursor context preservation, however, you can re-execute a prepared state multiple times
(possibly with new values for any bind variables in the prepared statement). For more information, see
“Cursor Context Preservation” which follows.
For background information about autocommit, see “Autocommit” on page 12.

Cursor Context Preservation


Oracle databases support cursor context preservation (CCP). In SQLWindows applications that connect to
Oracle, CCP is off by default.
If CCP is on (and autocommit is off), you can continue to execute prepared SQL statements (without preparing
them again) even after you commit the transaction that those statements are a part of. If CCP is off, however,
you cannot re-execute those SQL statements once you commit the transaction unless you prepare them
again.
For background information, see “Cursor Context Preservation” on page 15.

66
Data Types
The table that follows shows the mapping between the data types supported by a SQLWindows application
and the data types supported by an Oracle database server.

SQLWindows Data Type Oracle Data Type

STRING CHAR (254 or fewer bytes) VARCHAR2 (254 or fewer bytes)


VARCHAR (254 or fewer bytes) RAW
ROWID MSLABEL
LONG STRING CHAR (255 bytes)
LONG (255 to 232 – 1 bytes) VARCHAR2 (255 to maxvarch bytes)
VARCHAR (255 to maxvarch bytes) RAW (255 bytes)
LONG RAW (255 to 232 – 1 bytes)
BINARY LARGE OBJECT (BLOB, max 4 GB)
CHARACTER LARGE OBJECT (CLOB,
max 4 GB) MLSLABEL
NUMBER NUMBER INT

DATE/TIME DATE (without microseconds)

The value of maxvarch in the table above is either 4,000 or 32,000. If you have a LONG STRING bind variable
in an SQL statement, and that bind variable is associated with a VARCHAR or VARCHAR2 column, you can
store at most 4,000 bytes in that variable. If you use a PL/SQL program variable that is associated with a
VARCHAR or VARCHAR2 column, you can store at most 32,000 bytes in that variable.
The MLSLABEL data type applies only to Trusted Oracle database servers.

DBA Authority
The Gupta router for Oracle allows you to indicate that you wish to connect to a database in the “sysdba”
role. You do this from Connectivity Administrator by adding “as sysdba” to the Oracle connection string , as
shown in this example line from SQL.INI:
remotedbname=ora9i,@ora9i as sysdba
Thereafter, you can set the user ID as “sys” during a login and the router will actually connect with “sys as
sysdba”.
If you are connecting to Oracle using an OLE DB data source, the additional phrase in the connect string is not
required; “sys” will connect with sysdba privileges.

OS Authentication
You can connect to Oracle from Team Developer using OS authentication with username “dummy.” The
password is essentially ignored in this case.
Sample code:
67
On SAM_AppStartup
Set SqlUser='dummy'
Set SqlPassword='' ! could be anything
Set SqDatabase='Oracle11gDB' ! database name in sql.ini file.
Call SqlConnect(hSql)
This will connect to the Oracle database using the Operating System login (OS authentication).

Dynamic PL/SQL
SQLWindows applications use dynamic SQL statements to perform operations on the databases you connect
to. These statements (such as SELECT, INSERT, UPDATE, CREATE TABLE, and so on) are compiled and executed
“on the fly” (while the SQLWindows application is running).
Oracle supports a different (though similar) feature called dynamic PL/SQL. This feature lets you assign a
block of PL/SQL statements to a string variable, then have the database compile and execute that block of
statements at runtime.
You cannot execute dynamic PL/SQL blocks in your SQLWindows applications. However, you can achieve a
similar effect in your application as follows: Create a stored procedure that contains the PL/SQL statements
you want to execute, invoke that stored procedure, and then drop the procedure before you exit the
application (so the procedure does not continue to be stored in the database).

Empty Strings
On Oracle, if you try to insert the empty string into a varying-length character string column (or try to update
such a column with the empty string), the database stores NULL instead.
For background information, see “Nulls, Empty Strings, and Spaces” on page 21.

Isolation Levels
Oracle does not support setting isolation levels if an application uses dynamic (rather than static) SQL.
Because all SQLWindows applications use dynamic SQL statements, you cannot call SqlSetIsolationLevel in
your SQLWindows application when connected to Oracle.
You cannot set isolation levels when connected to an Oracle database. However, you can affect data
consistency and concurrent access to data by doing either of the following:
• Lock the entire table with the LOCK TABLE statement.
• Lock certain rows with the SELECT...FOR UPDATE statement.
Locking an entire table is very heavy-handed and risky. No one other than the user who has locked the table
can do any operations on the table until it is unlocked. Even if the application locks the table for only a short
period of time, the penalty paid by other users is usually unacceptably high.
Locking only certain rows during a transaction is much more selective and allows other users to access all
data in the database except for the locked rows. However, if the user who has the lock takes a long time to
complete the transaction, all other users who need to access that data are forced to wait.
68
For some background on isolation levels, see “Isolation Levels” on page 19.

Lock Time-Out
Oracle servers do not support lock time-out. However, you can enable autocommit (see “Autocommit” on
page 5-2), which has the side effect of eliminating deadlocks.
For some background on lock time-outs, see “Lock Time-Out” on page 21.

Optimizing Message Traffic


To speed up the flow of data between a SQLWindows application and the database server, you can use the
array processing feature. This feature consists of:
• Array fetching—you fetch multiple rows from a result set, rather than one row at a time.
• Bulk execute—you send the data for UPDATE, INSERT or DELETE operations to the server in batches,
rather than row by row.
You can modify how array processing works with the fetchrow and longbuffer keywords in the SQL.INI file
(see fetchrow on page 70 and longbuffer on page 71).

Positioned Updates
SQLWindows applications can perform positioned updates against an Oracle database.
For more information, see “Positioned Updates” on page 23.

Result Set Mode


If you turn on result set mode when your application is connected to Oracle, SQLWindows uses front-end
result sets. Oracle does not support (backend) scrollable cursors.
For more information, see “Result Set Mode” on page 24 and “Front End Result Sets” on page 16.

SQL.INI Keywords
This section describes the keywords you should use in the SQL.INI file when connecting a SQLWindows
application to an Oracle database.
For more information about the SQL.INI file, see “Initializing SQLWindows Applications” on page 28.

69
comdll
This keyword identifies the SQLRouters available between SQLWindows (or a client application created with
SQLWindows) and the database.
Section [win32client.dll]
Syntax comdll=communication_dll_name
Description This keyword identifies the filename of a SQLRouter DLL. You can put more than one
comdllstatement in the SQL.INI file. The value of communication_dll_nameto use for
SQLRouter/Oracle is sqlora32.
Example comdll=sqlora32
Notes For related information, see “Connect Search Order” on page 14.

fetchrow
This keyword specifies the maximum number of rows SQLRouter/Oracle retrieves per each network
message during cursor operations (fetching) on a result set.
Section [oragtwy]
Default 64
Syntax fetchrow=number_of_rows
Description number_of_rows must be a positive integer (but do not put a plus sign).
The fetchrow value specifies the number of rows that the OCI (i.e. Oracle Call Interface)
layer "prefetches" and makes available to your application. If the value is not set or the
value is less than 64, a default value of 64 is used.
You can improve performance by assigning a suitable value to fetchrow.
To achieve good performance, you must balance the number of rows per fetch (which
increases the memory used) against the operating system's need for memory (which
causes the OS to swap memory when not enough is available).
In general, on a slow network connection such as a WAN, higher values for fetchrow (up
to the actual number of rows in a given query) give the best results.
Your SQLWindows application still receives a single row for each call to SqlFetchNext,
regardless of the value assigned to fetchrow.
Example This example indicates that you want to retrieve only 100 rows with each fetch.
fetchrow=100
The following example indicates that you want to fetch only a single row (in effect,
disabling the feature).
fetchrow=1

log
This keyword defines the database router activity log and enable logging.
Section [win32client.ora32]
Syntax log=fully_qualified_path_name
70
Description Specify any valid file pathname after the keyword. If the file does not exist, it is created. If
the file already exists, it is overwritten (unless the /Fa option is specified see below).
Once the log statement has been added to the SQL.INI file, logging is performed
automatically.
You can specify one or more of the options listed below after the fully qualified
pathname to customize the output:
/Bx—exclude bind variable values
/Fa—append to existing file
/FD—display fetch data
/Ld—display LONG data read or written
/Tx—exclude timestamps
Enter the options exactly as shown, keeping upper-case letters in upper case and lower-
case letters in lower case.
Example This example calls for a log file that does not include timestamps on entries (to conserve
disk space), and displays the input and output of all LONG data (including TEXT and
IMAGE data items).
[win32client.ora32]
log=c:\gupta\oracle.log /Tx /Ld

longbuffer
This keyword specifies the maximum number of bytes of long data type columns to retrieve or send, and
truncates excess bytes.
Sections [oragtwy]
Default 32,768. Setting a value less than the default will cause the value to be reset to the
default.
Syntax longbuffer=number_of_bytes
Description The maximum size of this keyword is limited only by operating system constraints.
Reduce the value of this keyword to conserve memory or reduce network traffic. If you
use longbuffer to limit the number of bytes to retrieve, the retrieved data is truncated. To
avoid truncating long data, set the value equal to the largest data type size that your
applications need to read.
The longbuffer keyword applies to all databases defined in [oragtwy] section. You cannot
specify a longbuffer value larger than the maximum size for the data type to be retrieved.
Maximum value for this setting is dependent on your operating system and database
server environment..
Example longbuffer=65536

Note: You can also set this environment variable in your SQLWindows application using the
SqlSetParameterAll with the DBP_LONGBUFFER parameter. (Refer to the SQLWindows Function
Reference and the online help for more information about this function and the parameter.)

71
nodefparse
This keyword enables or disables deferred parsing.
Sections [oragtwy]
Default no
Syntax nodefparse={yes|no}
Description This keyword (a shortened form of “no deferred parse”) enables or disables deferred
parsing.
SQLRouter/Oracle uses deferred parsing for improved performance at runtime. However,
deferred parsing makes debugging more difficult because the router cannot return the
position of a column that may have generated an error.
You should especially disable deferred parsing when debugging complex SQL statements
with many columns in the result set. If deferred parsing is disabled and an error is raised,
the cursor automatically highlights the offending column in the result set.
When you put an application into production, you should enable deferred parsing.
Example This example disables deferred parsing.
nodefparse=yes

remotedbname
This keyword specifies database names and their associated connect strings.
Section [oragtwy]
Syntax remotedbname=db_name,connect_string
Description db_name specifies the value you will use as SqlDatabase for SqlConnect()
connect_string is the character “@” followed by the Oracle “Service Name” as
understood by SQL*Net. Only use the first portion of the service name, not the “.world”
portion.
Example This example assumes you have configured your Oracle 7 or Oracle 8 client for an Oracle
Service Name of ARTHUR.world.
Remotedbname=ART,@ARTHUR
You assign the variable SQLDatabase to the value “ART” and then issue the SqlConnect()
call. SqlRouter Oracle finds all of the necessary connection information and connect you
to the Oracle server.
Additional Requirements:
SqlRouter/ Oracle depends upon the Oracle Call Interface (OCI). This lets
SQLRouter/Oracle communicate effectively with versions 7.1, 7.2, 7.3, 8.0.x, 9.x, and
future versions of Oracle. To make sure the proper version of Oracle’s “OCI” software is
used, the registry key HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE contains a value for
the parameter ORAOCI. This can be set to a specific verson of Oracle’s DLL:
Oracle 8.1.X ORACLIENT8.DLL
Oracle 9.2.0.1 ORACLIENT9.DLL

72
Make sure the fully qualified path name is used in the registry entry.
Refer to the Oracle documentation for more information about CONFIG.ORA,
TNSNAMES.ORA, and ORACLE.INI and how to set up these files.
Oracle versions earlier than 8.1.5 are not supported by SqlRouter/Oracle.
Note For related information, see “Connect Search Order” on page 14.

substitute
This keyword specifies that the string in the first parameter should replace the string in the second
parameter whenever the first parameter appears in an SQL statement.
Section [oragtwy]
Syntax substitute=original_string,substitute_string
Description You can have an unlimited number of substitute statements in the initialization file.
However, this statement changes all occurrences of the replaced word in your SQL
statements: operators, WHERE clauses, object names, and literals (but not bind
variables).
The second string can include the first, as in:
substitute=od,odd
The strings can include embedded blank spaces, but not commas. You must use a comma
to separate original_string from substitute_string.
Notes Because each SQL statement is parsed, having substitute statements in your SQL.INI file
adds a small amount of overhead to the compilation of those statements. If you are not
running any Gupta software that require substitution, you may want to remove these
statements from the file.
If you use applications that rely on the “CATALOG” commands, such as Quest, Report
Builder, and DB Explorer, you must include the following statement in your SQL.INI:
substitute=SYSSQL.,
Include this statement exactly as shown with the upper case letters ”SYSSQL” followed by
a period followed by a comma. Without this, Quest, Report Builder, and or DB Explorer
will not be able to use the “CATALOG” commands in your application.
Example This example replaces double quotes, which Oracle does not accept, with nulls. This
statement is useful to run a program or tool you cannot modify that executes in the
Gupta runtime environment and, in certain contexts, places double quotes around names
it sends to the database.
The following statement causes SQLRouter/Oracle to strip off the double quotes before it
sends the data to the database:
substitute=”,

uselob
This keyword specifies whether the SQLBase API should use Oracle Call Interface (OCI) version 8 to work with
LOB datatypes (BINARY LARGE OBJECT and CHARACTER LARGE OBJECT), or use version 7, which cannot
handle LOB datatypes.

73
Default 0
Section [oragtwy]
Syntax uselob=integer
Description The LOB datatypes in Oracle version 9 and greater require the use of OCI version 8. Earlier
versions of the router for Oracle used OCI version 7. If you need compatibility with Oracle
LOB datatypes, set the value of integer to 1 to use OCI 8. If not, set the value to 0 to use
OCI 7. The default is 0.
Note You can also set this environment variable in your SQLWindows application using the
SqlSetParameterAll with the DBP_ORAUSELOB parameter. (Refer to the SQLWindows
Function Reference and the on-line help for more information about this function and
the parameter.)

Stored Procedures
SQLWindows applications can execute PL/SQL stored procedures on an Oracle database. These stored
procedures can also generate trigger events. To execute a PL/ SQL stored procedure, use the
SqlPLSQLCommand function.
A PL/SQL stored procedure invoked by SqlPLSQLCommand can have no more than 254 arguments. Also, the
string used to invoke the procedure cannot contain new line characters, nor can it execute stand-alone
functions. However, the array arguments can be dynamic arrays (see “Dynamic Array Support for PL/SQL
Stored Procedures” on page 76).
The SqlPLSQLCommand cannot process stored procedures without arguments. To execute a stored procedure
without arguments, use SqlPrepareAndExecute; for example:
If NOT SqlPrepareAndExecute(hSql,
'BEGIN anonymous_PL/SQL_block;END;')
SqlPLSQLCommand supports only the following Oracle data types for both scalar and array arguments for
stored procedures:
• NUMBER
• DATE
• CHAR
• VARCHAR
• VARCHAR2
You must explicitly specify the size of CHAR, VARCHAR, or VARCHAR2 data types as arguments to a PL/SQL
stored procedure. You can use either %type or define your own data type. For example, if the stored
procedure takes a CHAR argument that is a value for a column in a table, use %type with
table_name.column_name to declare that variable. If the argument does not tie in with a database column,
you can define MYCHAR CHAR(20), then use %type with MYCHAR to declare your argument.
If you do not specify the size, SQLNetwork binds the arguments with the maximum size allowed for each data
types.

74
Overloading Stored Procedures
SQLRouter/Oracle supports overloaded stored procedures in PL/SQL packages; however, you must:
• Define procedures with greater number of arguments before procedures with fewer arguments.
• Use all the same types in any argument position IN, OUT, or IN/OUT types.
• Use all scalar or array types in any argument position.
You cannot mix data types, nor can you use the DATE type in arguments for overloaded procedures.
For general information about stored procedures, see “Stored Procedures” on page 26.

SqlPLSQLCommand
Use this function to execute PL/SQL stored procedures in your SQLWindows application. Call it once for each
invocation of PL/SQL.
Syntax bOk = SqlPLSQLCommand(hSql, strCommand)
Parameters hSqlHandle—The connected SQL handle to an Oracle database.
strCommand—Command string used to invoke a PL/SQL procedure.
Description The first parameter identifies the SQL handle to the database. The second parameter is a
command string used to invoke a PL/SQL stored procedure. Like a stored procedure call,
this command string must have a name.
You can use PL/SQL IN variables the same way you use any other parameter in SAL
function calls.
PL/SQL OUT or IN/OUT variables must be valid SAL Receive parameters. These
parameters, like SAL Receive parameters, are set when the call returns.
You can use arrays for IN, OUT, and IN/OUT parameters.
IN/OUT parameters can be made to pass data to the PL/SQL stored procedure and to
receive data from a PL/SQL stored procedure in the same parameter.
Return Value bOk is TRUE if the function succeeds and FALSE if it fails.

Example PL/SQL stored procedure


The following example shows a PL/SQL stored procedure named INVOICES that contains a procedure,
INSERT_INVOICES. This procedure inserts one master record and a set of detail records into the database.
CREATE or REPLACE PACKAGE invoices AS
TYPE item_no_tbl IS TABLE OF invoice_detail.item_no%TYPE
INDEX BY BINARY_INTEGER;
TYPE quantity_tbl IS TABLE OF invoice_detail.quantity%TYPE
INDEX BY BINARY_INTEGER;
TYPE amount_tbl IS TABLE OF invoice_detail.amount%TYPE
INDEX BY BINARY_INTEGER;
PROCEDURE insert_invoice
(o_invoice_id OUT invoice_master.invoice_id%TYPE,
i_client_name IN invoice_master.client_name%TYPE,
i_invoice_date IN invoice_master.invoice_date%TYPE,
i_item_no IN item_no_tbl,
i_quantity IN quantity_tbl,
i_amount IN amount_tbl,
i_num_details IN NUMBER);

75
END INVOICES;
CREATE or REPLACE PACKAGE BODY invoices AS
next_invoice_id NUMBER;
PROCEDURE insert_invoice
(o_invoice_id OUT invoice_master.invoice_id%TYPE,
i_client_name IN invoice_master.client_name%TYPE,
i_invoice_date IN invoice_master.invoice_date%TYPE,
i_item_no IN item_no_tbl,
i_quantity IN quantity_tbl,
i_amount IN amount_tbl,
i_num_details IN NUMBER) IS
BEGIN
SELECT INVOICE_SEQ.NEXTVAL INTO next_invoice_id FROM DUAL;
o_invoice_id:=next_invoice_id;
INSERT INTO INVOICE_MASTER
(INVOICE_ID,CLIENT_NAME,INVOICE_DATE)VALUES
(next_invoice_id,i_client_name,i_invoice_date);
FOR n IN 1..i_num_details LOOP
INSERT INTO INVOICE_DETAIL
(INVOICE_ID,ITEM_NO,QUANTITY,AMOUNT) VALUES
(next_invoice_id,i_item_no(n),i_quantity(n), i_amount(n));
END LOOP; COMMIT;
END insert_invoice;
END invoices;

Executing the Sample Stored Procedure


The following example shows how to execute the example PL/SQL stored procedure from a SQLWindows
application.
Number:ninv_id
String:strClient
String:strCommand
Date:dt_inv_dt
Number:nitem_No[3]
Number:nQnty[3]
Number:nAmt[3]
Number:nNum
Set ninv_id = 3
Set strClient = ’John Doe’
Set strCommand = ’INVOICES.INSERT_INVOICE’
Set strCommand = strCommand || ’(ninv_id,strClient,dt_inv_dt,’
Set strCommand = strCommand || ’nitem_No,nQnty,nAmt,nNum)’
Set dt_inv_dt = 1993-07-12
Set nitem_No[0] = 3 Set nitem_No[1] = 4 Set nitem_No[2] = 5
Set nQnty[0] = 13 Set nQnty[1] = 14 Set nQnty[2] = 15
Set nAmt[0] = 21
Set nAmt[1] = 22
Set nAmt[2] = 23
Set nNum = 3
SET bOK=SqlPLSQLCommand(hSql, strCommand)

Dynamic Array Support for PL/SQL Stored Procedures


You can use dynamic arrays as input, output, and input/output arguments to Oracle8 PL/SQL stored
procedures. The arrays can be of type NUMBER, STRING and DATE/TIME.

76
Dynamic Arrays as INPUT arguments
Dynamic arrays grow to the size of the largest index value used in an assignment statement using the dynamic
array variables. For example, the following statements cause nDynVar to be a dynamic array of 10 elements:
nDynVar[0] = 0
nDynVar[1] = 1
nDynVar[9] = 9
When you pass a dynamic array as an INPUT argument to a PL/SQL stored procedure using
SqlPLSQLCommand, the number of elements created on the client are available on the database server. For
example, the following stored procedure inserts the value 9 into the table:
insert into foo values(nDynVar[9])
The following statement, however, causes the error “Attempting to fetch beyond end of result set”:
insert into foo values(nDynVar[10])

Uninitialized values
Do not initialize dynamic arrays. The arrays are passed as NULL for data types STRING and DATE/TIME, and
as zero for data type NUMBER. To pass NUMBER as a NULL, the user must explicitly assign it the value
Number_Null.

Dynamic Arrays as OUTPUT arguments


The size of output arguments of type dynamic array is determined in the backend stored procedure. For
example, the following statements return an array of ten elements to your SQLWindows application:
sOutVar(1) := ‘abc’;
sOutVar(10) := ‘def’;

Uninitialized values
Values for output arguments are not initialized on the backend, but are returned as NULL values.

Dynamic Arrays as INPUT/OUTPUT arguments


INPUT/OUTPUT dynamic arrays behave as INPUT dynamic arrays on input and as OUTPUT dynamic arrays on
OUTPUT.

Transactions, Disconnects, and Exits


If autocommit is off in your application, you are responsible for explicitly committing or rolling back
transactions. The rest of this section assumes that autocommit is off.
If you disconnect from the database without committing or rolling back a pending transaction, the database
commits the transaction for you.
If you exit the application normally without committing or rolling back a pending transaction, the database
commits the transaction for you.
If you exit the application abnormally without committing or rolling back a pending transaction, the database
rolls back the transaction for you.

77
Writing RAW Data
To INSERT or UPDATE columns that contain RAW data, you must first assign the data to be placed in that
column to an appropriate bind variable, then prepare an INSERT or UPDATE statement using that bind
variable.
The bind variable must be of type LONG STRING. Once you have prepared the INSERT or UPDATE statement,
you must call SqlSetLongBindDatatype to bind the LONG STRING data to the RAW data type. You then execute
the INSERT or UPDATE statement.

Note: You must prepare the INSERT or UPDATE statement without executing it, then call
SqlSetLongBindDatatype, then execute the INSERT or UPDATE statement. The binding done by
SqlSetLongBindDatatype lasts only until your application prepares the next SQL statement.

The call to SqlSetLongBindDatatype takes two arguments: the first is the ordinal position of the bind variable
that has the LONG STRING data in the INSERT or UPDATE statement; the second is the constant 23—this
constant denotes RAW data in the case of Oracle databases. (You can declare a user constant with a symbolic
name like nRaw and assign it the value 23; this makes your code more self-documenting.)
For example, assume an Oracle table called PRODUCT with the following column data types:
• INTEGER —for style ID information
• VARCHAR(30) —for style information
• VARCHAR(254)—for description information
• DEMICAL(8,2)—for price information
• LONG RAW—for picture information
The example would work just the same if the last column’s datatype were CHARACTER LARGE OBJECT or
BINARY LARGE OBJECT instead of LONG RAW.
Also assume the following variable declarations in your application:
Number: nStyleId
String: strStyle
String: strDesc
Number: nPrice
Long String: lstrPicture
Now assume you assigned these variables values in your program. You can add a row to the PRODUCT table
as follows:
Set strStmnt=’INSERT INTO PRODUCT VALUES’
Set strStmnt=strStmnt || ‘(:nStyleId,:strStyle,:strDesc,’
Set strStmnt=strStmnt || ‘:nPrice,:lstrPicture)’
Call SqlPrepare(hOracle,strStmnt)
Call SqlSetLongBindDatatype(5,23)
Call SqlExecute(hOracle)
The first argument to SqlSetLongBindDatatype is 5 because :lstrPicture is the bind variable that has the data
to be inserted into the LONG RAW column, and it appears as the fifth bind variable in the INSERT statement.
The second argument must be 23 (or a symbolic integer constant you define yourself that has that value).

78
Chapter 6 – Connecting to Sybase

This chapter describes how to connect SQLWindows applications to Sybase. For the specific versions of
Sybase supported, please refer to the Release Notes.
It is recommended that you read all of Chapter 1 – Overview and Chapter 2 – Initializing and Testing Your
Connection before you read this chapter.
You connect SQLWindows applications to a Sybase server using the native router, SQLRouter/Sybase.

Autocommit and Chained Transactions


Sybase supports autocommit.
To turn autocommit on or off, set the sybautocommit keyword in the SQL.INI file to the appropriate value (see
“sybautocommit” on page96 ).
If you turn autocommit on, the database server automatically commits results from each SQL statement that
executes successfully; the server also disables chained transactions.
If you disable autocommit, you must explicitly COMMIT (or ROLLBACK) any changes to the database. Because
disabling autocommit also enables chained transactions, you do not need to place BEGIN TRANSACTION in
front of the statements that make up the transaction, just put a COMMIT (or ROLLBACK) at the end of that
group of statements.

Note: By default, DDL (Data Definition Language) statements, such as CREATE TABLE, are not valid within a
transaction. However, your Sybase database administrator can configure the database to allow some
DDL statements as part of a transaction.

Even if you enable autocommit, you can still precede a specific sequence of SQL statements with the BEGIN
TRANSACTION statement and follow them with a COMMIT (or ROLLBACK) statement. The statements in
between BEGIN TRANSACTION and COMMIT are not committed until the COMMIT is executed. And they are
rolled back if ROLLBACK is executed. Bracketing statements in this way allows you to suppress autocommit for
those statements.
Important: Use the sybautocommit keyword to enable or disable autocommit; do not use the Transact-SQL
SET statement.
For background information on autocommit, see “Autocommit” on page 12.

Bind Variables
Sybase System 11.x and later versions provide native support for pre-compiled commands, and hence, input
bind variables.
For general information about bind variables, see “Bind Variables” on page 13. There are certain limitations
on how you can use bind variables with Sybase.

79
For example, the following SQL statement fails because you cannot use a bind variable to specify a table
name:
Call SqlPrepare(hSql,
‘INSERT INTO :dfTabName VALUES(1)’)
Call SqlExecute(hSql)

Note: If you have existing applications that worked with a Sybase database prior to System 11.x, you may
have code like this that you will have to change.

SQLRouter/Sybase implements the native input bind variable support of Client-Library and SQL Server
whenever possible. This includes the following:
• SELECT
• INSERT
• UPDATE (except positioned UPDATEs)
• DELETE (except positioned DELETEs)
In all other cases (stored procedures, DDL, and so on), bind variable support is simulated through substitution
of bind variable values just prior to execution. Unlike native bind variables, input bind variables are supported
merely as a programming convenience, not as a way of improving performance.
For example, say that dfInput has the value 231 and the code is the following:
Call SybPrepareProc(hSql,’sp_foo @var1 = :dfInput’,
‘:dfInput’)
Call SqlExecute(hSql)
It sends the following SQL statement to the database server:
sp_foo @var1 = 231
Important: For maximum portability, avoid using bind variables that are not ANSI-compliant.

COMPUTE Clause
SQLRouter/Sybase does not support the Transact-SQL extension of COMPUTE. Any query containing a
COMPUTE clause returns error #32028:
COMPUTE clause not supported.
Also, any COMPUTE results from stored procedures can result in a premature END-OF-FETCH.

Cursor Context Preservation


SQLRouter/Sybase supports cursor-context preservation (CCP) through the Client-Library database option
CLOSE ON END TRANS. In SQLWindows applications that connect to Sybase, CCP is off by default.
Sybase considers all cursors to belong to the same database transaction. When CCP is enabled, all cursors
maintain their contexts. Likewise, when CCP is disabled, all cursors lose their context after a COMMIT or
ROLLBACK is executed. Although you call the function SqlSetParameter (with the parameter DBP_PRESERVE)
to enable or disable CCP, and you specify a particular SQL handle as the first parameter in that call, the scope
80
of CCP is not limited to that one handle—the effect is global to the connection and to all cursors associated
with the transaction being executed on that connection. For more information about SQL handles and
connections, see enablemultipleconnections on page 91.
Important: Call SqlSetParameter to enable or disable CCP. Do not execute the Transact-SQL SET statement
CLOSE ON END TRANS.
If CCP is on (and autocommit is off), you can continue to execute prepared SQL statements (without preparing
them again) even after you commit the transaction that those statements are a part of. If CCP is off, however,
you cannot re-execute those SQL statements once you commit the transaction without preparing them again.
For general background information about CCP, see “Cursor Context Preservation” on page 15.

Data Types
The following table shows the mapping between the data types supported by a SQLWindows application and
the data types supported by a Sybase database server:

SQLWindows Data Type Sybase Data Type

STRING CHAR (1–254 bytes)


VARCHAR (1–254 bytes)
BINARY (1–127 bytes)
VARBINARY (1–127 bytes) TIMESTAMP
LONG STRING CHAR (1–255 bytes)
VARCHAR (1–255)
IMAGE (1–(231–1) bytes)
TEXT (1–(231–1) bytes)
BINARY (128–255 bytes)
VARBINARY (128–255 bytes)
NUMBER BIT NUMERIC TINYINT SMALLINT INT
FLOAT DECIMAL
DOUBLE PRECISION REAL SMALLMONEY MONEY
DATE/TIME SMALLDATETIME DATETIME

Note: To write more than 31,997 bytes of IMAGE or 64,000 bytes of TEXT data, you must call the function
SybWriteText (see “Writing and Retrieving IMAGE and TEXT Data” on page 106).

Using the Timestamp Data Type


TIMESTAMP is a Sybase-supplied but user-defined data type that is treated as the data type VARBINARY(8) for
storage and retrieval. You fetch this data into a STRING variable, where it is stored in hexadecimal format.
You cannot update a TIMESTAMP column. You also cannot insert a row with a TIMESTAMP column where you
specify your own TIMESTAMP value. Specify NULL for the TIMESTAMP and the database will insert the correct
value for you.
For example, suppose you create a table as follows:
create table t1(c1 int,c2 timestamp,c3 int)
81
To insert a new row, you would execute the statement:
insert into t1 values(1,NULL,3)
The database server stores the integer 1 in column c1, the integer 3 in column c3, and the current system-
generated timestamp value in column timestamp.

Empty Strings
On Sybase, if you try to insert the empty string into a varying-length character string column (or try to update
such a column with the empty string), the database stores a space instead.
For background information, see “Nulls, Empty Strings, and Spaces” on page 21.

Error Processing
SQLRouter/Sybase derives its error code and text from one of three possible sources: the SQLWindows file
ERROR.SQL, Client-Library messages, or database messages. SQLRouter/Sybase can receive multiple
messages; it evaluates all the messages and tries to report only the one that is most informative.
As a result, the first message returned by SybGetClientMsg or SybGetServerMsg does not necessarily match
the error code and text that SQLRouter/Sybase reports to the application. (For more information on
SybGetClientMsg and SybGetServerMsg, see “Retrieving Error and Info Messages” on page 83.)
A separate message buffer is created for each connection associated with a given application, database name,
and user name. All SQL handles on the same connection share this single message buffer. The message buffer
is cleared whenever a new SQL command is sent to the database server using one of the SQL handles
associated with the connection.
The number of messages that can be saved is limited. Each saved message consumes a small amount of fixed
overhead, limiting the total number of saved messages to 5000. Attempts to save more than 5000 messages
are ignored. All message text for any single command must fit within 64,000 bytes; otherwise, the saving of
messages is suspended.
The formula for calculating memory consumption is:
nMemory = (nMaxMessages * (nMaxLength + 22))
* nConnections

Error Precedence
With few exceptions (for example, connection failure or results pending), Client-Library messages usually
indicate a problem with SQLRouter/Sybase or Client-Library itself.
When multiple sources are possible for reporting an error condition, the source is based on the following
order of precedence:
• SQLWindows runtime error code (0–32,767)
• Sybase message (0–65,536)
• Client-Library message (0–65,536)

82
All of the error code ranges overlap. To help keep them separate, SQLRouter/Sybase reports the error code to
the application using the following modified ranges:
• SQLWindows runtime error code (0– 19,999)
• • Sybase message (20,000–29,999)
• Client-Library message (30,000–32,767)
Errors greater than 32,767 always return error 32,100; the actual error number appears at the beginning of
the message text.

Note: You can always get the actual error code and text from Client-Library (Sybase) messages by calling the
SQLRouter/Sybase Message API (see “Retrieving Error and Info Messages” on page 83).

RAISERROR
RAISERRORs are user-defined errors typically added to stored procedures. They are considered informational
and non-fatal.
SQLBase Router/Sybase returns different values depending on the value specified in the first RAISERROR
statement that is actually executed in the stored procedure:
• If the value of the first RAISERROR is less than 17,000, then the Sybase backend returns an error.
• If the value of the first RAISERROR is exactly 17, 000, then SqlRouter/ Sybase will return that value plus
any additional RAISERROR values (You have to call SybGetServerMsg () repeatedly to get all of the
RAISERROR values, and the reult set (if any )WILL be returned).
• If the value of the first RAISERROR is greater than 17,000, then SqlRouter/ Sybase will return that value
and any subsequent values of RAISERROR, but WILL NOT return a result set, regardless if one was actually
returned by the Sybase backend.
If you plan to use RAISERROR as a “warning” or as an indicator to direct further logic in your program, then
you should make sure your stored procedure always first has a value of 17,000, an then use additional
RAISERROR statements to indicate different conditions. Your program can check for the first error to be
exactly 17,000 and would then know to look for additional RAISERROR values. At the same time, it could
retrieve the result set from the stored procedure execution . The result set will NOT be available if the
RAISERROR statement specifies any value other than 17,000.

Retrieving Error and Info Messages


To retrieve client and server error and informational messages, or find out how many messages there are, call
one of the SAL function listed below. These functions are understood by SQLRouter/Sybase.
Important: To enable the saving of messages, you must include the keyword sybmaxmessages in the SQL.INI
file.

SybGetClientMsgCount
Use this function to return the number of currently buffered client messages.
Syntax bOk=SybGetClientMsgCount(hSql, nCount)
Sql Handle: hSql

83
Receive Number: nCount
Parameters hSql—A handle that identifies a connection context to the database.
nCount—The number of buffered client messages.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function returns the number of client messages currently buffered on handle hSql
in nCount. Messages occupy positions 1 through nCount in the buffer.
Note To enable the buffering of messages, include the sybmaxmessages keyword in the SQL.INI
file.

SybGetClientMsg
Use this function to retrieve the number, text, and severity of an arbitrary client message.
Syntax bOk=SybGetClientMsg(hSql, nPosition, nMsgNumber, strMsgText,
nSeverity)
Sql Handle: hSql
Number: nPosition
Receive Number: nMsgNumber
Receive String: strMsgText
Receive Number: nSeverity
Parameters hSql—A handle that identifies a connection context to the database.
nPosition—The position of the message to be retrieved in the message buffer.
nMsgnumber—The number associated with the retrieved message.
strMsgtext—The text of the retrieved message.
nSeverity—The severity of the retrieved message.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function returns the number, text, and severity of the client message in position
nPosition of the message buffer.
The function fails if the buffer is empty, the end of the buffer has been reached, or you
supply an invalid value for nPosition.
Note To enable the buffering of messages, include the sybmaxmessages keyword in the SQL.INI
file.

SybGetNextClientMsg
Use this function to retrieve the number, text, and severity of the next client message.
Syntax bOk=SybGetNextClientMsg(hSql, nMsgNumber, strMsgText, nSeverity)
Sql Handle: hSql
Receive Number: nMsgNumber
Receive String: strMsgText
Receive Number: nSeverity

84
Parameters hSql—A handle that identifies a connection context to the database.
nMsgnumber—The number associated with the retrieved message.
strMsgtext—The text of the retrieved message.
nSeverity—The severity of the retrieved message.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function returns the number, text, and severity of the next client message in the
message buffer.
The function fails if the buffer is empty or the end of the buffer has been reached.
Note To enable the buffering of messages, include the sybmaxmessages keyword in the SQL.INI
file.

SybGetServerMsgCount
Use this function to return the number of currently buffered server messages.
Syntax bOk=SybGetServerMsgCount(hSql, nCount)
Sql Handle: hSql
Number: nCount
Parameters hSql—A handle that identifies a connection context to the database.
nCount—The number of buffered server messages.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function returns the number of server messages currently buffered on handle hSql.
Messages occupy positions 1 through nCount in the buffer.
Note To enable the buffering of messages, include the sybmaxmessages keyword in the SQL.INI
file.

SybGetServerMsg
Use this function to retrieve the number, text, and severity of an arbitrary server message.
Syntax bOk=SybGetServerMsg(hSql, nPosition, nMsgNumber, strMsgText,
nSeverity)
Sql Handle: hSql
Number: nPosition
Receive Number: nMsgNumber
Receive String: strMsgText
Receive Number: nSeverity
Parameters hSql—A handle that identifies a connection context to the database.
nPosition—The position of the message to be retrieved in the message buffer.
nMsgnumber—The number associated with the retrieved message.
strMsgtext—The text of the retrieved message.
nSeverity—The severity of the retrieved message.
85
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function returns the number, text, and severity of the server message in position
nPosition of the message buffer.
The function fails if the buffer is empty, the end of the buffer has been reached, or you
supply an invalid value for nPosition.
Note To enable the buffering of messages, include the sybmaxmessages keyword in the SQL.INI
file.

SybGetNextServerMsg
Use this function to retrieve the number, text, and severity of the next server message.
Syntax bOk=SybGetNextServerMsg(hSql, nMsgNumber, strMsgText, nSeverity)
Sql Handle: hSql
Receive Number: nMsgNumber
Receive String: strMsgText
Receive Number: nSeverity
Parameters hSql—A handle that identifies a connection context to the database.
nMsgnumber—The number associated with the retrieved message.
strMsgtext—The text of the retrieved message.
nSeverity—The severity of the retrieved message.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Description This function returns the number, text, and severity of the next server message in the
message buffer.
The function fails if the buffer is empty or the end of the buffer has been reached.
Note To enable the buffering of messages, include the sybmaxmessages keyword in the SQL.INI
file.

Examples
To retrieve all messages from the DBCC (Database Consistency Checker) command, you could code the
following:
Call SqlPrepareAndExecute(hSql,’dbcc checkdb’)
! Establish position in message buffer.
If SybGetServerMsg(hSql, 1, nMsgno, strMsgtext, nSeverity)
! Traverse the remaining messages.
Loop
If NOT SybGetNextServerMsg(hSql, nMsgno, strMsgtext, nSeverity)
Break
...
...
Because this is the first time the application traverses the message buffer for this command, the following
example is functionally equivalent:
Call SqlPrepareAndExecute(hSql,’dbcc checkdb’)
! Traverse all the messages.
Loop

86
If NOT SybGetNextServerMsg(hSql, nMsgno, strMsgText, nSeverity)
Break
...
Important: All messages belong to the connection. Individual SQL handles within the same connection access
a single message buffer and inherit their current positioning from any positioning established by prior SQL
handles.

Getting Multiple Connections


When you obtain multiple SQL handles by calling SqlConnect more than once, you can choose to have all the
SQL handles that are associated with the same user ID, password and database name share the same
connection, or you can choose to have each SQL handle get its own connection. You control this behavior
with the enablemultipleconnections keyword in the SQL.INI file (see “enablemultipleconnections” on page
91).

Handles and Connections


SQLRouter/Sybase allows your application to connect to Sybase with either multiple connections and a single
SQL handle on each connection, or with multiple SQL handles on a single connection. This behavior is
controlled by a keyword in the SQL.INI file (see “enablemultipleconnections” on page 91).
SQLRouter/Sybase can support no more than 50 connections per application. Each connection can support no
more than 255 SQL handles.

Note: Because each connection consumes certain resources on the client, the actual number of
connections and handles available at runtime can vary.

SQLRouter/Sybase takes advantage of native Sybase cursors to support temporary tables, the SET
database_options statement, and the changing of passwords (with sp_password). For more background
information, see “Handles, Contexts, and Connections” on page 17).

Isolation Levels
Sybase supports isolation levels through the transaction isolation level feature. Level 1 prevents dirty reads.
Level 3 prevents phantom rows. Support for these levels varies according to the version of Sybase.
• Sybase 11.5: SQLRouter/Sybase supports these isolation levels by mapping the SQLWindows isolation
level CS to Level 1 and the isolation level RR to Level 3.
• Sybase11.9.2 and later: Transaction isolation level 2 is explicitly provided for data-only locked tables.
The following table shows the mapping of isolation levels:

Isolation Level System 11.5 System 11.9.2

87
RO Isolation Level 0 Isolation Level 0

CS Isolation Level 1 Isolation Level 1

RL Isolation Level 2 (Not supported) Isolation Level 2

RR Isolation Level 3 Isolation Level 3

For background information, see “Isolation Levels” on page 19.

Positioned Updates
Client-Library supports positioned UPDATEs and DELETEs through its native cursor support. Consequently,
SQLRouter/Sybase does not support the FOR BROWSE clause. Use the FOR UPDATE clause in your
SQLWindows applications.
Important: To update IMAGE and TEXT data, you need to use the FOR LONG UPDATE clause (a SQLWindows
extension) instead of the standard FOR UPDATE clause. For more information, see “Writing and Retrieving
IMAGE and TEXT Data” on page 106.
You might code a typical application as follows:
! Disable result set mode; otherwise, we will fetch
! only one row at a time.
SqlSetResultSet(hSql, FALSE)

! Compile the statement, execute it,


! and name the cursor.
SqlPrepare(hSql,’SELECT key, timestamp, name’ ||
’FROM foo into :dfKey,:dfTS,:dfName’ ||
’FOR UPDATE’)
SqlOpen(hSql,’CurA’)
! Position us on the appropriate row.
SqlFetchNext(hSql, nRetVal)

! Switch to another context (SQL handle)


! to handle the UPDATE.
! Point to the SELECT targeted for the UPDATE by
! referencing the named cursor.
SqlPrepare(hSql2,’UPDATE foo SET name = :dfName’ ||
’WHERE CURRENT OF CurA’)
SqlExecute(hSql2)
Committing the change (assuming sybautocommit=off is in the SQL.INI file) destroys the context of the
SELECT (unless CCP has been enabled for the appropriate cursors).

Note: Unlike FOR BROWSE (which creates a temporary copy of the results set from which rows are fetched),
FOR UPDATE is a true, positioned update operation. “Intent to update” locks are held on the actual
table until EOF or a COMMIT, so you get less concurrency with FOR UPDATE than with FOR BROWSE.

88
Positioned Updates and fetchrow
Rows are fetched in batches sized for the buffers allocated at the network message layer. Handles allow the
application to control the way data is sent across the network. The keyword fetchrow in the SQL.INI file (see
“fetchrow” on page 92) is used to request of the communications layer that it bundle multiple rows of data
per network fetch message back to Net-Library.
SQLRouter/Sybase implements handles for explicit SELECT statements in stored procedures that contain a
single SELECT statement. During positioned UPDATEs and DELETEs, rows are always fetched one at a time.
Therefore, these operations are not affected by the fetchrow setting. Since the client and server must be “in
sync” in terms of current row positioning, permitting multiple rows to be fetched at the network level is not
appropriate. For performance reasons, you typically do not want to use SELECT... FOR UPDATE requests unless
you have very small result sets or you think it likely your application will need to do a positioned update or
delete while fetching rows from a result set.

Note: SQLRouter/Sybase depends on the default locking behavior of the Sybase database (unless the user
explicitly changes this behavior). You should use the SHARE option in your SELECT statements
whenever possible to avoid lock contention.

The more rows your application fetches per message, the longer it takes for control to return to the
application. Thus, users of your application may actually perceive better performance with smaller fetchrow
values, since the application can populate a table window, list box, or other object sooner. Large fetchrow
values can even cause unnecessary rows to be fetched, if the row of interest is located at the beginning of the
result set and the buffer is filled with rows that are eventually discarded.
In general, the larger the fetchrow setting, the faster the result sets can be sent to the client. However, pacing
problems and protocol buffer limits influence the performance level you can expect; at some point you may
even experience performance degradation with a high fetchrow setting. Factors including result set size,
typical row size, network configuration, client and server operating systems, and application requirements, all
affect the optimal setting of this value. You may also need to increase the network packet size (see
“remotedbname” on page 94) to use this feature effectively.

Note: You can set the number of rows retrieved per network message either in the SQL.INI file with the
fetchrow keyword or in your SQLWindows application by calling the function SqlSetParameterAll.

For background information, see “Positioned Updates” on page 23.

Releasing Locks
You can expedite the releasing of locks on a result set by forcing Sybase to close the result set cursor once you
have fetched the last row from a result set. You enable this behavior using the closecursorateof keyword in
the SQL.INI file (see “closecursorateof” on page 90).

Reserved Strings
The following are SQLRouter/Sybase reserved words, clauses, or names:

89
• CANCEL
• CANCEL ALL
• WITH CURSOR
• _BIN2HEX_
• _HEX_
• FOR LONG UPDATE
For more information about _BIN2HEX_, _HEX_, and FOR LONG UPDATE, see “Writing and Retrieving IMAGE
and TEXT Data” on page 106.

SQL.INI Keywords
This section describes the SQL.INI keywords you want or need to use to initialize a SQLWindows application
that connects to Sybase.
For more information about the SQL.INI file, see “Initializing SQLWindows Applications” on page 28.

checkexists
Use this keyword to get an error after executing an UPDATE or DELETE statement that has no effect.
Section [sybgtwy]
Default off
Syntax checkexists={on|off}
Description This keyword causes SQLRouter/Sybase to return an error when the application executes
an UPDATE or DELETE statement that has no effect (that is, no rows in the database are
updated or deleted). The error that SQLRouter/Sybase returns is #380, “CHECK EXIST
failure”.
Notes If your application calls a stored procedure to execute the UPDATE or DELETE statement
(instead of executing the statement directly), SQLRouter/Sybase cannot determine if any
rows were affected. Therefore, your application will not get the error, even if you set
checkexists to on and no rows were updated or deleted by the stored procedure.
Example This statement causes an application to get the “CHECK EXIST failure” if the application
executes an UPDATE or DELETE statement that affects no rows.
checkexists=on

closecursorateof
Use this keyword to force the closing of a result set cursor once you have fetched the last row from that result
set.
Section [sybgtwy]
Default off
Syntax closecursorateof={on|off}

90
Description If you set this keyword to “off” (or you accept this value by default), you keep the result
set cursor (and your lock on the rows in the result set) until you successfully execute a
COMMIT or ROLLBACK, or until you disconnect from the database.
If you set this keyword to “on” (and CCP is off—see “Cursor Context Preservation” on
page 80), you lose the result set cursor (and your lock on the rows in the result set) once
you have fetched the last row of the result set.
Setting this keyword to “on” allows you to perform DML (Data Manipulation Language)
statements (such as UPDATE, DELETE, INSERT) on the same table from which you have
gotten a result set (with a SELECT statement). If you do not set this keyword to “on”, a
DML statement that operates on that table will fail (time out); this is because the SELECT
statement that got the result set in the first place still has a lock on that table.
Example This example (assuming CCP is off) causes the cursor in a result set to be closed once the
last row from that result set has been fetched; this releases the lock on the table from
which you got the result set.
closecursorateof=on

comdll
Identify the SQLRouters available between SQLWindows (or a client application created with SQLWindows)
and the database.
Section [win32client.dll]
Syntax comdll=communication_dll_name
Description This keyword identifies the filename of a SQLRouter DLL. You can put more than one
comdll statement in the SQL.INI file. The value of communication_dll_name to use for
SQLRouter/Sybase is sqlsyb32.
Example comdll=sqlsyb32
Notes For related information, see “Connect Search Order” on page 14.

enablemultipleconnections
Use this keyword to force the creation of a new connection to the database for each call to SqlConnect.
Section [sybgtwy]
Default off
Syntax enablemultipleconnections={on|off}
Description If this keyword is set to “on”, you get a new connection to the database each time you
call SqlConnect, even if the SQL handle argument to SqlConnect is associated with the
same user ID, password, and database name as one of the already connected SQL
handles.
If this keyword is set to “off”, and you have already called SqlConnect at least once, you
may or may not get a new connection to the database when you call SqlConnect again.
You do not get a new connection if the SQL handle argument in the call is associated with
the same user ID, password, and database name as one of the already connected SQL
handles; otherwise, you do get a new connection.

91
Example This example forces the creation of a new connection to the database for each call to
SqlConnect.
enablemultipleconnections=on

fetchrow
This keyword specifies the maximum number of rows SQLRouter/Sybase retrieves for each network message
during cursor operations (fetching) on a result set.
Section [sybgtwy]
Default 20
Syntax fetchrow=number_of_rows
Description number_of_rows must be a positive integer (but do not put a plus sign).
The server uses an array to hold the fetched column values. SQLRouter/Sybase retrieves
rows for each network fetch requested, according to the following algorithm:
1. Take the width of the widest column and divide it into the maximum size of the array
buffer.
2. If the calculated value is smaller than the value of fetchrow, use the calculated value.
3. Otherwise, use the value of fetchrow. If no value is defined, use the default value of
20.
The value assigned to fetchrow is not used if:
– You use the FOR UPDATE clause in a SELECT statement (CURRENT OF cursor).
– You specify a LONG column in the SELECT list.
You can improve performance by assigning a suitable value to fetchrow. To achieve good
performance, you must balance the number of rows per fetch (which increases the
memory used) against the operating system’s need for memory (which causes the
operating system to swap memory when not enough is available).
Your SQLWindows application still receives a single row for each call to SqlFetchNext,
regardless of the value assigned to fetchrow.
Example This example indicates that only 10 rows are retrieved for each fetch.
fetchrow=10
The following example indicates that you want to fetch only a single row (in effect,
disabling the feature).
fetchrow=1

locktimeout
Use this keyword to set the number of seconds SQLRouter/Sybase waits for Sybase to respond to a login
request or execute a statement.
Section [sybgtwy]
Default 60 second wait for login; infinite wait for statements to execute
Syntax locktimeout=number_of_seconds

92
Description This keyword sets the login time-out and the statement execution time-out to
number_of_seconds. SQLRouter/Sybase will wait number_of_seconds to obtain a server
connection (login time-out). SQLRouter/Sybase will also wait number_of_seconds for the
server to obtain the resources it needs to execute a statement (statement execution
time-out).
If you set number_of_seconds to zero, SQLRouter/Sybase uses the default values of 60
seconds for the login time-out, and infinity (wait forever) for the statement execution
time-out.
When the time-out expires, SQLRouter/Sybase notifies Sybase to abandon the attempt to
connect (login time-out) or the attempt to execute the statement (statement execution
time-out).
Notes This keyword sets the time-out value for any SQLWindows application and for all
connections the application makes to Sybase. To set (or override) the time-out value in
your application, call SqlSetParameter with the DBP_LOCKWAITTIMEOUT argument, or
call SqlLockWaitTimeout.
Example The following example sets the login time-out and the statement execution time-out to
three minutes:
locktimeout=180

log
Use this keyword to activate the SQLRouter activity log.
Section [win32client.syb32]
Syntax log=fully_qualified_file_name
Description You can specify any path or filename for the keyword. If the file already exists, it is
overwritten. Once the keyword is specified, logging is performed automatically with each
SQLRouter initialization. You can apply one or more of the following case-sensitive
options to customize the output:
/Bx—exclude display of bind variable values
/Fa—append to existing file
/FD—display fetch data
/Ld—display LONG data read/written
/Tx—exclude display of timestamps
Enter the options exactly as shown, keeping upper-case letters in upper case and lower-
case letters in lower case.
Example The following example results in a log file that does not include timestamps on entries
(conserving space) and displays the input and output of all LONG (that is, TEXT or IMAGE)
data items.
[winclient.syb32]
log=c:\gupta\sybsys11.log /Tx /Ld

93
longbuffer
This keyword specifies the maximum number of bytes of long data type columns to retrieve or send, and
truncates excess bytes.
Sections [sybgtwy]
Default 32768. Setting a value less than the default will cause the value to be reset to the default.
Syntax longbuffer=number_of_bytes
Description The maximum size of this keyword is limited only by operating system constraints.
Reduce the value of this keyword to conserve memory or reduce network traffic. If you
use longbuffer to limit the number of bytes to retrieve, the retrieved data is truncated. To
avoid truncating long data, set the value equal to the largest data type size that your
applications need to read.
The longbuffer keyword applies to all databases defined in the [sybgtwy] section. You
cannot specify a longbuffer value larger than the maximum size for the data type to be
retrieved.
Example longbuffer=65536
Important: You can also set this environment variable in your SQLWindows application. If
you do, call SqlSetParameterAll with the DBP_LONGBUFFER parameter. Do not set the
size of this buffer with the Transact-SQL SET statement.

remotedbname
Use this keyword to specify database names and associated information.
Section [sybgtwy]
Syntax remotedbname=db_name1,server,db_name2,network_packet_size
Description db_name1 specifies the database name as it is known by the SQLWindows application;
the name may not be more than 8 characters long.
server specifies the name of the Sybase server on which the database is located. The
server name can be found in the Sybase SQL.INI file located in the Open Client installation
subdirectory.
db_name2 specifies the actual database name as known by the server. This parameter is
optional; if you omit it, the name of the database is assumed to be the same as
db_name1. Unlike db_name1, the value of db_name2 is not limited to 8 characters.
network_packet_size is an optional parameter that specifies the network packet size to
use for this server connection. If you omit this parameter or give it a value of zero, the
connection uses the default network packet size negotiated between Client-Library/Net-
Library and the server during connect processing.
Notes For related information, see “Connect Search Order” on page 14.
Examples This example specifies a database known by your SQLWindows application as PUBS. This
database is located on the server SYBASE11. The server knows this database as pubs2.
The network packet size is the default negotiated between Client-Library and the server.
remotedbname=PUBS,SYBASE11,pubs2,0
The following example differs from the preceding one in that SQLRouter/Sybase requests

94
a network packet size of 2048 bytes when it tries to connect to the database.
remotedbname=PUBS,SYBASE11,pubs2,2048

substitute
Use this keyword to specify that the string in the first parameter should replace the string in the second
parameter whenever the first parameter appears in an SQL statement.
Sections [sybgtwy]
Syntax substitute=original_string,substitute_string
Description Values for this keyword are case-sensitive and are separated by a comma (not a space).
You can have an unlimited number of substitute statements in the initialization file.
However, this statement changes all occurrences of the replaced word in your SQL
statements—operators, WHERE clauses, object names, and literals (but not bind
variables).
The second string can include the first, as in the following:
substitute=od,odd
Important: The strings can include embedded blank spaces, but not commas—you must
use a comma to separate original_string from substitute_string.
Notes Because each SQL statement is parsed, having substitute statements in your SQL.INI file
adds a small amount of overhead to the compilation of those statements. If you are not
running any software that require substitution, you may want to remove these
statements from the file.
If you do not want to have any substitutions done, leave this keyword out of the SQL.INI
file or comment out any lines that have this keyword. Dnot have a line that looks like the
following:
substitute=
Example The substitute statement below could be used with an application that (for example)
encloses table names in quotes. If you cannot modify the application, and if the database
you are connecting the application to does not accept double quotes, you can have
SQLRouter strip them off as follows:
substitute=”,

sybapplicationname
Use this keyword to define the client’s application name when logging in to the database server.
Section [sybgtwy]
Default Gupta Sybase App
Syntax sybapplicationname=program_name
Description program_name is stored in the dbo.sysprocesses of the master database for the
connection.
Notes SQLRouter/Sybase defines the reserved identifier @APPNAME as the title of the
application’s top-level window.
Example This statement defines the client application name when logging in to the database
95
server as MYAPP:
sybapplicationname=MYAPP

sybautocommit
Use this keyword to turn autocommit on or off.
Section [sybgtwy]
Default off
Syntax sybautocommit={on|off}
Description To turn on autocommit, set sybautocommit to on; to turn off autocommit, set
sybautocommit to off.
Notes For more information about autocommit, see “Autocommit and Chained Transactions” on
page 79.
You can also turn autocommit on and off in your SQLWindows applications by calling
SqlSetParameter. For example, to disable autocommit, make either one of the following
calls:
Call SqlSetParameter(hSql,DBP_AUTOCOMMIT,FALSE,strNull)
Call SqlSetParameterAll(hSql,DBP_SYBAUTOCOMMIT,FALSE,strNull,TRUE)
Calling either of these functions overrides the value of the sybautocommit keyword in
the SQL.INI file.
Example This statement enables autocommit.
sybautocommit=on

sybmaxmessages
Use this keyword to instruct SQLRouter/Sybase how to save routine messages sent from the server.
Section [sybgtwy]
Default 0,0
Syntax sybmaxmessages=max_messages,max_length
Description max_messages specifies the maximum number of messages to be saved.
max_length specifies the maximum length of a saved message; this parameter is
optional. Any message text beyond the specified limit is truncated. If you do not specify
max_length, the default of 1024 bytes (full message length) is assumed.
Note: Message length is always preserved in favor of message count.
Example The following example saves a maximum of 30 messages, and limits each message to 100
bytes. These settings limit the amount of memory used to store messages to 3660 bytes.
sybmaxmessages=30,100
The following example saves a maximum of 300 messages with full message length (1024
bytes). These settings limit the amount of memory used to store messages to 313,800
bytes. Because the message buffer is limited to much less (64,000 bytes), the actual
number of messages saved may be considerably less than 300. The number saved will

96
depend on the size of the messages.
sybmaxmessages=300
The following example saves a maximum of 30 messages and discards all message text.
Only the message number and severity level are saved. These settings limit the amount
of memory used to store messages to a mere 660 bytes.
sybmaxmessages=30,0

sybtracefile
Use this keyword to invoke the Client-Library trace facility for debugging.
Section [sybgtwy]
Syntax sybtracefile=c:\sybase\sybase.log
Description The trace file can produce information useful to Sybase Technical Support when a
problem occurs in a Sybase software component. The trace file is automatically
configured to dump all available debugging information.
The trace file can quickly grow quite large. It also requires that the debugging version of
Client-Library be installed. If the debugging version is not found during the first LOGIN
attempt, SQLRouter/Sybase generates a dialog box reporting the error and continues
with tracing disabled.
Notes If you do not want to do any tracing, leave this keyword out of the SQL.INI file or
comment out any lines that have this keyword. Do not have a line that looks like the
following:
sybtracefile=

sybworkstationname
Use this keyword to define the machine name by which clients identify themselves.
Section [sybgtwy]
Default workstation_name
Syntax sybworkstationname=hostname
Description hostname is stored in the connection entry of the dbo.sysprocesses of the master
database. You can display it within certain system stored procedures (for example,
sp_who) whenever you have a live connection to the server.
SQLRouter/Sybase defines the reserved identifier @WORKSTATION as the name of the
client computer
If you do not specify a value for this keyword, the literal value used is workstation_name.
Example This statement defines the client machine name as MYHOST:
sybworkstationname=MYHOST

yieldonservercall
Use this keyword to enable and configure asynchronous processing for applications that connect to a Sybase
server using SQLRouter/Sybase.
97
Section [sybgtwy]
Default 0
Syntax yieldonservercall=numeric_ value
Description numeric_value must be an integer between –60 and 600 inclusive.
Asynchronous processing allows a SQLWindows application to send requests to the
Sybase server for processing and periodically poll for a response. In between polls, the
application can either do other work or yield to the operating system (Microsoft
Windows ) to allow other tasks to run. A special function (the callback function)
determines what the application does between polls. SQLWindows defines a default
callback function for you; this function yields to the operating system.
If you disable asynchronous processing, your SQLWindows application does not return
control to the operating system between the time the application sends a request to the
server and the time it receives a reply.
To disable asynchronous processing, set yieldonservercall to zero (or comment out the
statement). To enable asynchronous processing, set yieldonservercall to a non-zero value
in the range from -60 to 600.
A non-zero value defines a polling interval for SQLRouter/Sybase. A positive value defines
a minimum polling interval measured in tenths of a second; a negative value defines a
maximum polling interval measured in seconds. After each polling request,
SQLRouter/Sybase invokes the callback function.
If you assign a positive value to yieldonservercall, you are specifying the minimum time
interval between polling requests. Upon every return from the callback function,
SQLRouter/Sybase determines whether the minimum time interval has passed. If so,
SQLRouter/Sybase polls for a response from the server; if not, SQLRouter/Sybase returns
immediately to the callback function.
If you assign a negative number to yieldonservercall, you are setting the maximum polling
interval to the absolute value of this number; it is SQLRouter / Sybase that determines
the actual polling frequency for you. SQLRouter/Sybase polls very often initially (about
twice a second), but the polling interval quickly increases over time, up to the maximum
value you set. This algorithm assumes that, over time, a response time of perhaps even
several seconds becomes far less significant relative to the total time required to fulfill
the request. Reducing unnecessary polling improves the responsiveness of the
environment and reduces network traffic generally.
Under most circumstances, a setting of +10 (1 call/second) or –5 (dynamic polling interval
with a maximum of 5 seconds) permits reasonable response time and smooth control of
the operating environment. You should use these values if you assume that any task
which gains control during the callback function returns control to SQLRouter/Sybase in a
timely manner, and that the callback function yields to the operating system. (Like any
other application, SQLRouter/Sybase must wait for return of control from the callback
function before it can poll the database server again.)

98
Stored Procedures
You can call stored procedures, execute them on Sybase, and retrieve results sets and return status from
them by using the following SAL functions in your SQLWindows application. These functions are:
• SybExecuteProc on page 6-29
• SybExecuteProcEx on page 6-30
• SybGetNextResults on page 6-31
• SybGetReturnStatus on page 6-32
• SybPrepareNextResults on page 6-34
• SybPrepareProc on page 6-33
You can also execute stored procedures using, for example, SqlPrepare and SqlExecute. However, you should
do this only for stored procedures that do not return result sets. When you execute a stored procedure by
calling SqlPrepare and SqlExecute, SqlPrepareAndExecute, and so on, any result sets that the stored
procedure might return are discarded.
Stored procedures can perform all the usual SQL operations, including positioned updates.
For some general information on stored procedures, see “Stored Procedures” on page 26.

Using Cursors with Stored Procedures


SQLRouter/Sybase automatically implements Client Library cursors for all SELECT statements. You can declare
your own cursors for stored procedures as follows:
Call SybPrepareProc(hSql,’sp_one_select with cursor’,’:dfOut1’)
The WITH CURSOR clause is an extension to the SQL language understood by SQLRouter/Sybase. By using
WITH CURSOR, you instruct SQLRouter/Sybase to declare a cursor for the stored procedure. You can declare a
cursor only for stored procedures that have exactly one SQL statement—a SELECT statement. If a stored
procedure has more than one SELECT statement, or SQL statements that are not SELECT statements, your
attempt to declare a cursor fails.
If you attempt to execute an SQL statement on any other SQL handle while rows or additional result sets
remain unfetched from a stored procedure, you get the following Client-Library error:
30041: "This routine cannot be called because another command structure has
results pending."
You must either fetch all remaining result sets from the stored procedure or cancel them. If CCP is off, you can
cancel an active result set (and all pending result sets for all SQL handles on the same connection) by
preparing a new statement on the same SQL handle. The following example illustrates a technique that uses a
“dummy” command to cause the cancel.
Call SqlPrepare(hSql,’SELECT * FROM dbo.sysobjects WHERE 1 = 2’)
If CCP is off, you can use the CANCEL statement (instead of preparing a new command) to cancel the active
result set and all pending result sets for all SQL handles on the same connection immediately. This statement
is an extension to the SQL language understood by SQLRouter/Sybase. Using the CANCEL statement is the
most efficient way of canceling all pending result sets.
Call SqlPrepareAndExecute(hSql,’cancel’)

99
If CCP is off, and you call SqlCommit (or execute the COMMIT statement), you implicitly cancel pending result
sets on all SQL handles on the same connection, not just the handle specified as an argument to the function.
For example, the call below does not cancel pending result sets just for hSqlA, but for all other open SQL
handles on the connection (assuming CCP is off).
Call SqlCommit(hSqlA)
Like COMMIT, a ROLLBACK implicitly cancels all pending result sets on all SQL handles (provided CCP is off).
For example, the call below does not cancel pending result sets just for hSqlA, but for all other open SQL
handles on the connection.
Call SqlPrepareAndExecute(hSqlA,’rollback’)
Closing the connection also cancels all pending result sets.
Important: If CCP is on, preparing a new statement on a given SQL handle, or executing a CANCEL, COMMIT,
or ROLLBACK cancels just the result set associated with the SQL handle you specify, not all the result sets for
all of the SQL handles on the same connection.

SybExecuteProc
Use this function to execute a stored procedure or a sequence of SQL statements.
Syntax bOk=SybExecuteProc(hSql, strStatements, strIntoList)
Sql Handle: hSql
String: strStatements
String: strIntoList
Parameters hSql—A handle that identifies a connection context to the database.
strStatements—The stored procedure or SQL statements to be executed.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
Description SybExecuteProc executes the stored procedure or sequence of SQL statements specified
in strStatements. It stops at the first SELECT statement (if any) and returns the result set
from that statement—call SqlFetchNext repeatedly to retrieve the rows in the result set.
If the stored procedure returns additional result sets, call SybGetNextResults once for
each additional result set.
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to SybExecuteProc. If you know the stored
procedure does not execute any SELECT statements (that is, it does not return any result
sets), specify the value strNull for strIntoList.
If the stored procedure accepts input parameters, you can specify literals, constants, or
bind variables (of any type except LONG STRING) as input arguments.
Note: Even though you cannot specify a LONG STRING bind variable as an input
parameter to a stored procedure, there is one exception: you can specify a bind variable
assigned a string of exactly 255 bytes.
If the stored procedure returns values in output parameters, you must supply output
arguments in order to call the procedure. However, you cannot retrieve the values of the
output arguments. SQLRouter/Sybase does not support returning the values of output
parameters from a stored procedure to your SQLWindows application.
100
Note: You can work around this restriction (see “Retrieving Output Parameter Values” on
page 104).
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Examples This function is demonstrated in the sample application SQLSYBS3.APP.

SybExecuteProcEx
Use this function to execute a stored procedure or a sequence of SQL statements when you do not know if
the stored procedure returns any result sets.
Syntax bOk=SybExecuteProcEx(hSql, strStatements, strIntoList, bRows)
Sql Handle: hSql
String: strStatements
String: strIntoList
Receive Boolean: bRows
Parameters hSql—A handle that identifies a connection context to the database.
strStatements—The stored procedure or SQL statements to be executed.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
bRows—Flag that indicates whether the stored procedure is able to return rows (but not
whether any rows are actually returned).
Description When SybExecuteProcEx is called, it causes all statements in the stored procedure up
through the first SELECT statement to be executed, then returns the result set from that
SELECT statement. Call SqlFetchNext repeatedly to fetch the rows in the result set, then
call SybGetNextResults to execute the next statements in the stored procedure up
through the next SELECT statement.
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to SybExecuteProcEx.
The parameter bRows is set to TRUE if the stored procedure executes at least one SELECT
statement (even if the statement returns zero rows). If the stored procedure does not
execute a SELECT statement, bRows is set to FALSE.
If the stored procedure accepts input parameters, you can specify literals, constants, or
bind variables (of any type except LONG STRING) as input arguments.
Note: Even though you cannot specify a LONG STRING bind variable as an input
parameter to a stored procedure, there is one exception: you can specify a bind variable
assigned a string of exactly 255 bytes.
If the stored procedure returns values in output parameters, you must supply output
arguments in order to call the procedure. However, you cannot retrieve the values of the
output arguments. SQLRouter/Sybase does not support returning the values of output
parameters from a stored procedure to your SQLWindows application.
Note: You can work around this restriction (see “Retrieving Output Parameter Values” on
page 104).
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
101
Example This example stores a procedure on Sybase, then executes that stored procedure while
checking if the procedure returns result sets.
Call SybExecuteProc(hSql,
‘create procedure authorproc @switch int as if @switch = 1
select au_lname from authors
else
update authors set au_lname = \’dummy\’
where au_lname = \’cognoscente\’’,’’)
Call SybExecuteProcEx(hSql,’exec authorproc 1’,’:df1’,bRows)
If bRows
Call SqlFetchNext(hSql,nFetchInd)
Call SybExecuteProcEx(hSql,’exec authorproc 2’,’:df1’,bRows)
If bRows
Call SqlFetchNext(hSql,nFetchInd)
Else
Call SalMessageBox(‘Row updated.’,’Procedure Results’,MB_Ok)

SybGetNextResults
Use this function to get the next result set from a stored procedure.
Syntax bOk=SybGetNextResults(hSql, strIntoList)
Sql Handle: hSql
String: strIntoList
Parameters hSql—A handle that identifies a connection context to the database.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
Description Results of stored procedures are returned in one or more sets with zero or more rows in
each set. SybExecuteProc returns the first result set. Use SybGetNextResult to retrieve
subsequent sets. Each set of data is returned in the variables specified by strIntoList.
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to SybGetNextResults.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Notes Once you retrieve the next result set, call SqlFetchNext repeatedly to fetch each row of
data in the result set.
Example This function is demonstrated in the sample application SQLSYBS1.APP.

SybGetReturnStatus
Use this function to get the return status from a stored procedure.
Syntax bOk=SybGetReturnStatus(hSql, nRetVal)

102
Sql Handle: hSql
Number: nRetVal
Parameters hSql—A handle that identifies a connection context to the database.
nRetVal—The return value from a stored procedure.
Description This function assigns the return status of a stored procedure to nRetVal. You can get the
return status (as defined by Sybase) only after you have fetched the last row of the last
result set returned by the stored procedure.
If you prepare and execute another statement on the same handle (hSql) before calling
SybGetReturnStatus, you can no longer obtain the return status for the stored procedure.
However, you can perform operations on a different SQL handle (including calling
SybGetReturnStatus with a different handle to get the return status of a different stored
procedure) and still come back and get the return status of the stored procedure
associated with hSql.
Return Value bOk is set to TRUE if the return status was available and to FALSE if it was not available. If
bOk is FALSE, the value of nRetVal is undefined.
Example This function is demonstrated in the sample application SQLSYBS1.APP.

SybPrepareProc
Use this function to prepare a stored procedure for execution.
Syntax bOk=SybPrepareProc(hSql, strStatements, strIntoList)
Sql Handle: hSql
String: strStatements
String: strIntoList
Parameters hSql—A handle that identifies a connection context to the database.
strStatements—The statements to be prepared by the database.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
Description Call this function to prepare the statements in strStatements that create or invoke a
stored procedure.
If the stored procedure accepts input parameters, you can specify literals, constants, or
bind variables (of any type except LONG STRING) as input arguments.
Note: Even though you cannot specify a LONG STRING bind variable as an input
parameter to a stored procedure, there is one exception: you can specify a bind variable
assigned a string of exactly 255 bytes.
If the stored procedure returns values in output parameters, you must supply output
arguments in order to call the procedure. However, you cannot retrieve the values of the
output arguments. SQLRouter/Sybase does not support returning the values of output
parameters from a stored procedure to your SQLWindows application.
Note: You can work around this restriction (see “Retrieving Output Parameter Values” on
page 104).
The INTO variables in strIntoList can be of any data type. Separate the variables listed in

103
strIntoList with commas and precede each variable name with a colon. If the stored
procedure returns zero rows, the variables in strIntoList keep whatever values they had
before the call to SybPrepareProc. If you know the stored procedure does not execute
any SELECT statements, specify the value strNull for strIntoList.
To actually create or invoke the stored procedure, you need to execute the prepared
statements. You can do this explicitly (by calling SqlExecute), or implicitly (for example, by
calling SalTblPopulate).
You cannot mix calls to SybPrepareProc with SybGetNextResult, nor SybExecuteProc or
SybExecuteProcEx with SybPrepareNextResults, in the same execution cycle of a stored
procedure. All other functions (for example, SybGetReturnStatus, SybGetError, and so on)
work with either set of functions.
As with SybExecuteProc and SybExecuteProcEx, you are not limited to specifying in
strStatements statements that create or invoke a stored procedure; you can specify any
valid SQL statements in strStatements.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Notes If you do not expect the stored procedure to return result sets, call SqlPrepare instead of
SybPrepareProc.
Example This function is demonstrated in the sample application SQLSYBS3.APP.

SybPrepareNextResults
Prepare the next SELECT statement in a stored procedure.
Syntax bOk=SybPrepareNextResults(hSql, strIntoList, bEndOfResults)
Sql Handle: hSql
String: strIntoList
Boolean: bEndOfResults
Parameters hSql—A handle that identifies a connection context to the database.
strIntoList—The list of INTO variables into which the stored procedure’s result set values
(if any) are returned.
bEndOfResults—Flag that indicates if there are no more result sets.
Description This function prepares the next SELECT statement in the stored procedure; you must then
call SqlExecute with the same SQL handle you specified in SybPrepareNextResults to
actually retrieve the result set. All INTO variable data types are supported.
Separate the variables listed in strIntoList with commas and precede each variable name
with a colon. If the stored procedure returns zero rows, the variables in strIntoList keep
whatever values they had before the call to SybPrepareNextResults.
Return Value bOK is to TRUE if the function succeeds and to FALSE if it fails.
Example This function is demonstrated in the sample application SQLSYBS3.APP.

Retrieving Output Parameter Values


Normally, when you execute a stored procedure that returns values in output parameters by calling
SybPrepareProc followed by SqlExecute, you cannot retrieve those output values, even though you specify
104
bind variables as output parameters when you call the stored procedure. The only way a SQLWindows
application can retrieve data from a stored procedure is in the result sets (if any) that the stored procedure
returns.
You can work around this restriction by executing the stored procedure inside a batch of statements. The
batch of statements retrieves the output parameter from the stored procedure as a “result set” and then
returns that “result set” to your SQLWindows application; for example:
Call SybPrepareProc(hSql,
‘declare @outparm int ‘ ||
‘exec GTIPROC_BASICS ‘ ||
‘@test = 1 ‘ ||
‘@result = @outparm output ‘ ||
‘SELECT @outparm’)
Here is a more complete example to illustrate calling a stored procedure and retrieving an output parameter.
In summary, for the stored procedure created below, you call:
• SybPrepareProc (as shown above).
• SqlExecute to position to the first “result set” (the set of authors’ last names).
• SqlFetchNext to retrieve the rows in the first result set.
• SybPrepareNextResults, SqlExecute, and SqlFetchNext to get the “result set” from the SELECT statement
against table ‘foo’.
• SybPrepareNextResults, SqlExecute, and SqlFetchNext to retrieve the value of outparm. This is the last
result set generated as part of the command batch outside of the stored procedure.
Set strSqlCreate =‘CREATE PROC GTIPROC_BASICS ‘ ||
‘@test int, @result int output ‘ ||
‘AS SELECT au_lname FROM authors ‘ ||
‘SELECT c1 FROM foo where c1 = @test ‘ ||
‘SELECT @result = @test * 10 ‘ ||
‘RETURN -100 /* Success */‘
If TRUE
When SqlError
Return TRUE
Call SqlPrepareAndExecute(hSql,‘drop procedure GTIPROC_BASICS‘)
Call SqlPrepareAndExecute(hSql,strSqlCreate)
Set strSqlExecute = ‘declare @outparm int ‘ ||
‘exec GTIPROC_BASICS ‘ ||
‘@test = :nTest ‘ ||
‘@result = @outparm output ‘ ||
‘SELECT @outparm‘
Set nTest = 2
Call SybPrepareProc(hSql,strSqlExecute, ‘:dfAu_lname‘)
Call SqlExecute (hSql)
While NOT nFetchInd
Call SqlFetchNext(hSql,nFetchInd)
Call SybPrepareNextResults (hSql, ‘:dfFoo‘, bMoreResults)
Call SqlExecute (hSql)
Set nFetchInd = FALSE
While NOT nFetchInd
Call SqlFetchNext(hSql,nFetchInd)
Call SybPrepareNextResults(hSql, ‘:dfOutParm‘,bMoreResults)
Call SqlExecute (hSql)
Set nFetchInd = FALSE
While NOT nFetchInd
Call SqlFetchNext(hSql,nFetchInd)

105
Transactions, Disconnects, and Exits
If autocommit is off in your application, you are responsible for explicitly committing or rolling back
transactions. If autocommit is off, and you disconnect from the database or exit the application (either
normally or abnormally) without committing or rolling back a pending transaction, the database rolls back
the transaction for you.

Writing and Retrieving IMAGE and TEXT Data


This section explains how to retrieve and write IMAGE and TEXT data to a Sybase database.
To retrieve IMAGE or TEXT data you do not intend to update:
Define a normal SQL cursor to fetch the data.
(To scroll backwards and forwards in the result set, turn on result set mode.)
To retrieve IMAGE or TEXT data you want to update:
1. Get a SQL handle (for example, hSql1).
2. Turn off result set mode (specifying the handle hSql1). To preserve the cursor context, turn on CCP
before turning off result set mode.
3. Execute a SELECT…FOR LONG UPDATE statement, specifying hSql1. (The FOR LONG UPDATE clause is an
extension to SQL that SQLWindows uses for Sybase only.)
4. Position to the row you wish to update.

Writing Data
To write IMAGE or TEXT data of up to 64K, you can use the INSERT or UPDATE SQL statements, but you must
then supply the data as a literal or a constant. You can write IMAGE or TEXT data of any size using the
function SybWriteText; assign the data to a variable and pass that variable as an argument to SybWriteText.

Note: If you attempt to execute an INSERT or UPDATE statement using bind variables on an IMAGE or TEXT
column, the current transaction will be rolled back and you will be presented with the error message
returned by the database server. This is because Sybase does not support dynamic parameters for
TEXT and IMAGE data.

To update IMAGE or TEXT data in an existing row:


1. Retrieve a result set with the row you wish to update, as detailed in steps 1-4 above.
2. Get another SQL handle (for example, hSql2).
3. Call SybWriteText (using hSql2) to update the row.
To insert IMAGE or TEXT data:
1. 1. Get a SQL handle (for example, hSql1).
2. Insert a dummy value (such as a space) into the target column by preparing and executing the SQL
INSERT statement and specifying the handle hSql1.

106
3. Commit the insert operation (if autocommit is off) specifying the handle hSql1.
4. Turn off result set mode (specifying the handle hSql1). To preserve the cursor context, turn on CCP
before turning off result set mode.
5. Execute a SELECT…FOR LONG UPDATE statement, specifying hSql1. (The FOR LONG UPDATE clause is an
extension to SQL that SQLWindows uses for Sybase only.)
6. Position to the desired row.
7. Get another SQL handle (for example, hSql2).
8. Call SybWriteText (using hSql2) to update the row.
To delete IMAGE or TEXT data:
1. Turn on result set mode.
2. Execute a SELECT…FOR UPDATE statement.
3. Position to the desired row.
4. Execute the DELETE statement.
When calling SybWriteText, you can choose whether or not to log the update of the IMAGE or TEXT column
by setting the bLogging argument to this function appropriately. If bLogging is FALSE, the database server will
not log the update. Note that you must enable the sp_dboption option (select into/bulkcopy) on the database
server to perform operations without logging.

Retrieving Data and Buffer Size


SQLRouter/Sybase can retrieve IMAGE or TEXT values of any length (given enough memory on your client
machine to store the value) provided the amount of IMAGE or TEXT data does not exceed the buffer size you
specified with the longbuffer keyword in the SQL.INI file (see “longbuffer” on page 94).
SQLRouter/Sybase detects IMAGE and TEXT values during execution and allocates its fetch buffer for the
column according to the size specified for the longbuffer keyword. If there is insufficient memory for this
fetch buffer, you get the following error:
Workspace limit exceeded.
You need to specify a value for the longbuffer keyword that is big enough to hold the largest value you expect
to retrieve. If the amount of long data retrieved is too large to fit into the buffer, SQLRouter/Sybase truncates
the excess data longer without warning. If you use the truncated binary value (for example, a truncated
bitmap) in your application, you will probably get a runtime error.
Client Library pads returned VARCHAR column values with trailing spaces up to the length of the column.
SQLRouter/Sybase strips trailing spaces for returned VARCHAR values.

Note: It is still possible to encounter trailing spaces for VARCHAR values if the row value itself was INSERTed
or UPDATEd with trailing spaces. SQLRouter/Sybase strips only those spaces that lie beyond the
actual stored length. Consequently, SalStrLength indicates the true length of the data value stored in
the database.

SybWriteText
Perform a positioned update of more than 31,997 bytes of IMAGE data or more than 64,000 bytes of TEXT
data on a Sybase database.
107
Syntax bOk=SybWriteText(hSql,strCursorName,nColumnNum,nColumnName,
strUpdateValue,bTimeStamp,bLogging)
Sql Handle: hSql
String: strCursorName
Number: nColumnNum
String: strColumnName
String: strUpdateValue
Boolean: bTimeStamp
Boolean: bLogging
Parameters hSql—A handle that identifies a connection context to the database.
strCursorName—Name of the cursor associated with the SQL handle on which you
executed the SELECT statement that retrieved the row to be updated.
nColumnNum—Ordinal position in the SELECT statement of the column to be updated.
strColumnName—Fully qualified name of the column to be updated.
strUpdateValue—Value to be written to the column.
bTimeStamp—Flag that indicates whether or not this function should avoid updating the
column if it has been updated since you executed.
bLogging—Flag that indicates whether or not the transaction gets logged.
Description This function performs a positioned update on IMAGE or TEXT data in the column named
in strColumnName. The name in strColumnName must be the fully qualified name of a
column in an actual table, not a view.
You first execute a SELECT...FOR LONG UPDATE statement to retrieve the rows and
columns of interest from the table. You then call SqlFetchNext to fetch rows, and
SybWriteText to update the column strColumnName in the current row.
You can SELECT and update any number of columns. The value in nColumnNum is the
ordinal position of the column listed in the SELECT...FOR LONG UPDATE statement. You
vary this number with each call to SybWriteText to update more than one column in the
current row before fetching the next row.
The SQL handle you pass as an argument to SybWriteText must be different from the SQL
handle used to execute the SELECT...FOR UPDATE statement.
This function assumes the column to be updated is either not NULL, or has been set to
NULL using the UPDATE statement (not the INSERT statement).
To set a TEXT or IMAGE column to NULL, execute an UPDATE statement; do not call
SybWriteText.
Notes You must disable result set mode before call SybWriteText because this function does a
positioned update.
Return Value bOk is set to TRUE if the function succeeds and to FALSE if it fails.
Example The following example illustrates the typical operations that are performed when calling
this function:
! Open both cursors.
Call SqlConnect(hSql1)

108
Call SqlConnect(hSql2)
! Result sets must be off for positioned updates.
Call SqlSetResultSet(hSql1,FALSE)
! Assume a bmptbl table and a bitmap column.
Set strSQLstmt = ‘SELECT bitmap FROM bmptbl FOR LONG UPDATE’
Call SqlPrepare(hSql1,strSQLstmt)
Call SqlOpen(hSql1,‘CURSOR1’)
Loop
If NOT SqlFetchNext(hSql1,nRetVal)
Break
! Assume strNewBitMap has the new data for updating.
Call SybWriteText(hSql2,‘CURSOR1’,1,‘bmptbl.bitmap’,
strNewBitMap,bTimeStamp,bLogging)

109
Chapter 7 – Connecting to Databases Using
ODBC
This chapter explains how to connect SQLWindows applications to databases using ODBC. It also describes
the mapping of error codes between SQLWindows and ODBC.
It is recommended that you read all of Chapter 1 – Overview and Chapter 2 – Initializing and Testing Your
Connection before you read this chapter.
You connect SQLWindows applications to ODBC data sources with the Team Developer ODBC router,
SQLRouter/ODBC. This router conforms to the ODBC 3.0 specification up to core levels 1 and 2 (ODBC 2.0
level within the 3.0 specification).

Connecting to Specific Data Sources


A SQLWindows application can access a variety of ODBC data sources, including:
• IBM DB2/400
• Microsoft SQL Server version 6.x
• Microsoft Access
• ORACLE
• Sybase

Note: If you will be connecting to a DB2/4700 data source, make sure to read “Connecting to Access” on
page 7-3. In all other cases, make sure to read the documentation that comes with your ODBC driver.

Your SQLWindows application communicates with SQLRouter/ODBC (supplied with your Team Developer
software); the router talks to the Microsoft ODBC Driver Manager (supplied with Windows); the Driver
Manager talks to the ODBC driver (supplied by a third-party vendor); finally, the ODBC driver talks to the
target database.
ODBC drivers from various vendors are available that work with SQLRouter/ODBC to allow you to connect
your SQLWindows applications to a given data source using ODBC. You need to install and configure an ODBC
driver that has been written to work with the database you want to use. The ODBC driver should also have
been certified to work with SQLWindows applications by the vendor for that driver.
Important: In addition to reading Chapter 2 – Initializing and Testing Your Connection, you must read the
documentation that comes with your ODBC driver to find out what you need to know to connect to your
target database using that driver. The driver documentation should also describe any limitations or special
features of that driver.

Connecting to Access
The Access ODBC driver that we certify does not support isolation levels.

110
Connecting to DB2/400
Your SQLWindows applications connect to DB2/400 just as they would to any other data source. However, you
need to carefully check the documentation that comes with the StarSQL ODBC driver from StarWare, Inc. to
see what kind of support and limitations come with their DB2/400 driver. Topics you should check include
autocommit, cursor context preservation, isolation levels, stored procedures, and transaction processing.
When you update logical files, you must use journaling. Otherwise, you must update using physical files.
See “Cursor Context Preservation” on page 112, “Stored Procedures” on page 121, and “Transactions,
Disconnects, and Exits” on page 121.
For background information on these topics, see “Autocommit” on page 12, “Cursor Context Preservation” on
page 15, “Isolation Levels” on page 19, “Stored Procedures” on page 26, and “Transactions” on page 26.

About the ODBC Driver for SQLBase


Your Team Developer development system includes an ODBC driver for the SQLBase database. With this
driver you can protect your current investment in ODBC-enabled applications (such as Microsoft Excel or
ODBC-enabled Visual Basic applications). You migrate your data as needed to SQLBase and continue to run
those same applications.
You can also use this same ODBC driver (in conjunction with Gupta’s SQLHost for DB/2 product) to access data
on IBM’s DB2/MVS database from any ODBC-enabled application.

Note: Your SQLWindows applications can access a SQLBase database in much the same way they access any
other database. However, you cannot use the SQLBase ODBC driver to access SQLBase. See the
SQLBase documentation to learn how to connect a SQLWindows application to SQLBase.

Brand Information
If your SQLWindows application needs to identify the type of database server it is connected to, you call
SqlGetParameter(hSql, DBP_BRAND, nBrand, strNull) to retrieve the brand into the parameter nBrand.
However, if you are connected to an ODBC data source (which is also the case when you connect to Microsoft
SQL Server 6.x), the value returned in nBrand is always 31 (which denotes “ODBC”).
If your application gets the value 31 in nBrand, you need to make additional function calls to get more specific
information. You use these functions to find out the name of the database and ODBC driver, as well as the
version number of the ODBC driver. These function calls are:
• SqlGetParameterAll (hSql, DBP_DRIVER_NAME, nNum, strDriverName)
• SqlGetParameterAll (hSql, DBP_DRIVER_VER, nNum, strDriverVersion)
• SqlGetParameterAll (hSql, DBP_DBMS_NAME, nNum, strDBMSName)
To get the definitions of the constants DBP_DRIVER_NAME, DBP_DRIVER_VER, and DBP_DBMS_NAME,
include the file SQLNWKCN.APL (located in your installation directory) in your application by selecting
Libraries, File Include from the SQLWindows menu bar.

111
Cursor Context Preservation
When a SQLWindows application connects to an ODBC data source, the default state of cursor context
preservation (CCP)—whether it is on or off—is a function of the database to which you connect.
If you attempt to turn CCP on or off in your application, SQLRouter/ODBC passes this request on to the ODBC
driver. For this request to be honored, both the ODBC driver and the data source must support the setting of
CCP at runtime. Read the documentation supplied with your ODBC driver and your database for more
information.
Important: When connecting to a DB2/400 data source using the StarSQL ODBC driver from StarWare, your
application cannot change the setting for CCP. Since CCP is off by default in SQLWindows, this means that CCP
is always off when you connect to DB2/400 using StarSQL.
If CCP is on, you can continue executing SQL statements you have already prepared even after you commit the
operations you have executed. If CCP is off and you execute a COMMIT, you must re-prepare those SQL
statements if you want to re-execute them. Whether CCP is on or off, you must also prepare them again if you
execute a ROLLBACK.
For more background information on CCP, see “Cursor Context Preservation” on page15 .

Data Types
SQLRouter/ODBC maps between the four data types supported in SQLWindows applications and the SQL data
types defined by the ODBC specification. The mapping supports all ODBC SQL data types (minimum, core, and
extended) except SQL_BINARY and SQL_VARBINARY.

SQLWindows ODBC

STRING SQL_CHAR SQL_VARCHAR


SQL_TIMESTAMP
LONG STRING SQL_LONGVARCHAR
SQL_LONGVARBINARY
NUMBER SQL_DECIMAL SQL_NUMERIC
SQL_SMALLINT SQL_INTEGER
SQL_REAL SQL_FLOAT SQL_DOUBLE
SQL_BIT SQL_TINYINT SQL_BIGINT
DATE/TIME SQL_DATE SQL_TIME

Important: To learn which data types are supported by the data source you are connecting to using your
ODBC driver, read the documentation that comes with the ODBC driver.

Error Processing
SQLWindows has three standard functions to process errors.
• SqlGetError—Returns an error message.

112
If the error number is less than 20,000, the file ERROR.SQL is searched for the error text and that text (if
found) is returned; otherwise, the translated error number and database error message from the
database server are returned
• SqlErrorText—Searches only ERROR.SQL for error text reason and remedy.
If there is a reason and remedy for the given error number in ERROR.SQL, the reason and remedy are
returned; otherwise, the string “message not found” is returned.
• SqlGetErrorText—Returns an error message.
If the text for the given error number is found in ERROR.SQL, that text is returned; otherwise, the
database error message is returned.
Error messages have the following format:
<native_error>[<vendor>][ODBC_component][data_source]<error_text>
The following is an example:
Microsoft SQL Server:208[Microsoft][ODBC SQL Server Driver][SQL Server] Invalid
object name ’tyu’.
The syntactical components of this example are the following:
• native error—Microsoft SQL Server:208
• vendor—Microsoft
• ODBC component—ODBC SQL Server Driver
• data source—SQL Server
• error text—Invalid object name tyu

Error Code Mapping


This section lists the mapping between ODBC alphanumeric error codes and SQLWindows decimal error
numbers. The ODBC entries are listed in increasing lexicographic order (digits preceding letters of the
alphabet).

ODBC Error Code SQLWindows Error Number

01000 20087

01002 20010

01004 20011

01006 20012

01S00 20013

01S01 20090

01S02 20091

113
ODBC Error Code SQLWindows Error Number

01S03 20092

01S04 20093

07001 20014

07006 20015

08001 20016

08002 20017

08003 20018

08004 20019

08007 20020

08S01 20021

21S01 20022

21S02 20023

22001 20024

22003 20025

22005 20026

22008 20027

22012 20028

22026 20029

23000 20030

24000 20031

25000 20032

28000 20033

34000 20034

37000 20035

3C000 20036

114
ODBC Error Code SQLWindows Error Number

40001 20037

42000 20038

70100 20039

IM001 20040

IM002 20041

IM003 20042

IM004 20043

IM005 20044

IM006 20045

IM007 20046

IM008 20047

IM009 20048

IM010 20103

IM011 20104

IM012 20105

IM013 20106

S0001 20049

S0002 20050

S0011 20051

S0012 20052

S0021 20053

S0022 20054

S0023 20107

S1000 20055

S1001 20056

115
ODBC Error Code SQLWindows Error Number

S1002 20057

S1003 20058

S1004 20059

S1008 20060

S1009 20061

S1010 20062

S1011 20108

S1012 20063

S1015 20064

S1090 20065

S1091 20066

S1092 20067

S1093 20068

S1094 20069

S1095 20070

S1096 20071

S1097 20072

S1098 20073

S1099 20074

S1100 20075

S1101 20076

S1102 20077

S1103 20078

S1104 20111

S1105 20112

116
ODBC Error Code SQLWindows Error Number

S1106 20079

S1107 20080

S1108 20081

S1109 20082

S1110 20083

S1111 20113

S1C00 20084

S1DE0 20085

S1T00 20086

Lock Time-Out
You can establish a lock time-out in your SQLWindows application when connecting to a database using
ODBC, provided that both the ODBC driver you are using and the database you are connecting to support
setting a lock time-out from a client application. Read the vendor documentation for your driver and your
database for more information.

SQL.INI Keywords
This section contains the keywords you need or may want to use in the SQL.INI file when connecting a
SQLWindows application to a database using ODBC.
For more information about the SQL.INI file, see “Initializing SQLWindows Applications” on page 28.

buffrow
Use this keyword to control the buffering of data between your SQLWindows application and
SQLRouter/ODBC.
Section [odbcrtr]
Default 0
Syntax buffrow=number_of_rows
Description This keyword controls the buffering of data between your SQLWindows application and
SQLRouter/ODBC. By decreasing the buffrow value, response time can be improved
significantly. Larger values increase data throughput, but lengthen the response time.

117
The values for buffrow can range from 0 through 32767. Assigning a value does not
guarantee that the current INMESSAGE is of sufficient size to fulfill the request. In these
cases, the number of buffered rows may be considerably less.
The default value of zero causes SQLRouter/ODBC to revert to buffering data based on
the number of rows which will fit within the current INMESSAGE buffer size.
Example This statement instructs SQLRouter/ODBC to perform single row fetches into the
INMESSAGE buffer:
buffrow=1
Notes The buffrow keyword has no effect on the manner in which data is buffered and
transported across the network.
You can also configure the buffering of data for individual SQL handles using the
SqlSetParameterAll function. Assuming nBuffrow has already been assigned the value
desired for SQL handle hSql, you can make the following call:
SqlSetParameterAll(hSql,DBP_BUFFROW,nBuffRow,FALSE,TRUE)

comdll
Identify the SQLRouters available between SQLWindows (or a client application created with SQLWindows)
and the database.
Section [win32client.dll]
Syntax comdll=communication_dll_name
Description This keyword identifies the filename of a SQLRouter DLL. You can put more than one
comdll statement in the SQL.INI file. The value of communication_dll_name to use for
SQLRouter/ODBC is sqlodb32.
Notes For related information, read Connect search order on page 1-6.
Example comdll=sqlodb32

enablemultipleconnections
Use this keyword to force the creation of a new connection to the database for each call to SqlConnect.
Section [odbcgtwy]
Default off
Syntax enablemultipleconnections={on|off}
Description If this keyword is set to “on”, you get a new connection to the database each time you
call SqlConnect, even if the SQL handle argument to SqlConnect is associated with the
same user ID, password, and database name as one of the already connected SQL
handles.
If this keyword is set to “off”, and you already called SqlConnect at least once, you may or
may not get a new connection to the database when you call SqlConnect again. You do
not get a new connection if the SQL handle argument in the call is associated with the
same user ID, password, and database name as one of the already connected SQL
handles; otherwise, you do get a new connection.
Example The following example forces the creation of a new connection to the database for each
118
call to SqlConnect:
enablemultipleconnections=on

log
Define the database router activity log and enable logging.
Section [win32client.odb32]
Syntax log=fully_qualified_path_name
Description Specify any valid file pathname after the keyword. If the file does not exist, it is created. If
the file already exists, it is overwritten (unless the /Fa option is specified see below).
Once the log statement has been added to the SQL.INI file, logging is performed
automatically.
You can specify one or more of the options listed below after the fully qualified
pathname to customize the output:
/Bx—exclude bind variable values
/Fa—append to existing file
/FD—display fetch data
/Ld—display LONG data read or written
/Tx—exclude timestamps
Enter the options exactly as shown, keeping upper-case letters in upper case and lower-
case letters in lower case.
Example The following example calls for a log file that does not include timestamps on entries (to
conserve disk space), and displays the input and output of all LONG data (including TEXT
and IMAGE data items):
[win32client.odb32] log=c:\odbc.log /Tx /Ld

longbuffer
Specify the maximum number of bytes of LONG column data to retrieve or send.
Sections [odbcrtr]
Default 32 Kbytes. Setting a value less than the default will cause the value to be reset to the
default.
Syntax longbuffer=number_of_bytes
Description This keyword sets the size of the buffer that holds LONG data. The maximum size is
limited only by operating system constraints. Normally, the optimal size that avoids data
loss is the largest column of LONG data to be sent or retrieved by the SQLWindows
application. A larger buffer uses more memory and increases network traffic; a smaller
buffer uses less memory and reduces network traffic. However, if the buffer is too small
to hold all of the data, the excess is truncated.
If specified, the longbuffer statement applies to all databases connected to using ODBC. It
is recommended that you not specify a value for longbuffer larger than the maximum
LONG column size in any of the tables stored in any of those databases.

119
Example The following statement sets to 64K the maximum number of bytes of LONG column data
to retrieve from or send to any database connected to using ODBC:
longbuffer=65536
Notes You can also set this variable in a SQLWindows program by calling SqlSetParameterAll
with the DBP_LONGBUFFER parameter (defined in SQLNWKCN.APL). This allows you to
tailor the size of the buffer to the application.

odbctrace
Trace all calls to the ODBC API.
Section [odbcrtr]
Default off
Syntax odbctrace={on|off}
Description The ODBC Driver Manager can trace all calls made to the ODBC API. The trace
information is saved in the log file whose pathname is given in the odbtracefile
statement.
Example The following statement enables ODBC API call tracing:
odbctrace=on

odbctracefile
Specify the pathname of the trace file for ODBC API calls.
Section [odbcrtr]
Default client_root_directory\sql.log
Syntax odbctracefile=fully_qualified_filename
Description This keyword specifies the pathname of the trace file for ODBC API calls. This keyword
has no effect unless the odbtrace keyword is set to “on”(see “odbctrace” on page 120).
Example The following statement specifies that the trace file is called odbc.log, and that it is
located in the \logs directory on the client’s c: drive:
odbctracefile=c:\logs\odbc.log

remotedbname
Use this keyword to specify database names and their associated connect strings.
Section [odbcrtr]
Syntax remotedbname=db_name1,connect_string
Description db_name1 specifies the database name as it is known by the SQLWindows application;
the name may not be more than 8 characters long.
connect_string is the exact connect string you will use to connect to the ODBC data
source. Typically, this entry begins with the string "DSN=" to specify the data source
name specified in the ODBC Administrator tool.
The value specified in connect_string can include embedded spaces. Do not put
120
comments on the same line with the "remotedbname" statement. Everything from the
comma following db_name to the end of line is interpreted as the connect string.
Examples: Assume you defined two data sources in ODBC Administrator. One is for Microsoft
SqlServer 7.0 (with a data source name of "MS SqlServer 7.0"), and the other is for a
Visual FoxPro database (with a data source name of "Visual FoxPro DB"). The
remotedbname statements look like:
remotedbname=SS70,DSN=MS SqlServer 7.0
remotedbname=VFP, DSN=Visual FoxPro DB
Your application sets the variable "SqlDatabase" to either "SS70" (to connect to the
SqlServer data source) or to "VFP" (to connect to the FoxPro database), set SqlUser and
SqlPassword appropriately, and issue the SqlConnect() call. SqlRouter/ODBC obtains all
the necessary information from the configuration maintained by the ODBC Administrator
utility.
Additional note: The remotedbname parameter is not necessary for applications built using Team
Developer. It is required if you want to use the SqlTalk utility to connect to the ODBC data
source.
When connecting to any ODBC data source where the data source name was configured
by the ODBC Administrator tool, your application can bypass the remotedbname
parameter altogether, and set the variable SqlDatabase to the actual ODBC data source
name. Using the previous example, if you issue the following statements (assume SqlUser
and SqlPassword are set), your program will connect correctly to the Microsoft SqlServer
7.0 data source without using the remotedbname parameter in SQL.INI:
Set SqlDatabase = "MS SqlServer 7.0" Call SqlConnect (hSql)

Stored Procedures
SQLRouter/ODBC supports calling stored procedures that accept input arguments and return result sets.
However, the router does not support obtaining values from a stored procedure using output arguments.
Syntactically, you can legally call a stored procedure that has output parameters by providing bind variables
where the output arguments are required; however, the value of those variables will be undefined after you
have called the stored procedure, so they will be of no practical use to you.
Important: If your ODBC driver does not support returning values in result sets, such as the StarSQL ODBC
driver for DB2/400, you cannot get information from a stored procedure into your SQLWindows application.
Read your ODBC driver documentation to find out what kind of stored procedure support your driver
provides, and what limitations or restrictions it imposes.
For more information on stored procedures read Stored Procedures in chapter 6.

Transactions, Disconnects, and Exits


If autocommit is off, and your application either disconnects or exits (whether normally or abnormally),
SQLRouter/ODBC executes a ROLLBACK against the pending transaction. This rolls back all pending Data
Manipulation Language statements (DML), such as CREATE, INSERT, and so on.

121
When it comes to Data Definition Language (DDL) statements, such as CREATE TABLE, some databases
commit them as soon as they are executed, regardless of whether autocommit is on or off. For these
databases, the DDL statements cannot be rolled back along with the pending DML statements. Other
databases treat DDL statements just like DML statements. They do not commit them until an explicit COMMIT
is executed. For these databases, the pending DDL statements are rolled back just like the DML statements.

Note: In the case of DB2/400 and the StarSQL driver from StarWare, all the statements in the pending
transaction (both Data Definition Language and Data Manipulation Language statements) are rolled
back.

122
Chapter 8 – Connecting to Multiple
Databases Concurrently
This chapter describes some of the considerations you need to take into account when writing SQLWindows
applications that access multiple databases concurrently.

Overview
SQLWindows is a 32-bit client/server application development environment for Microsoft Windows. Using
SQLWindows and other Team Developer components, teams of programmers can build large, enterprise-wide
client/server applications that connect to, retrieve data from, and manipulate data at database servers from a
variety of vendors.
This chapter describes a sample application that executes transactions against three different database
servers. It tells you how to set up and run the application, it describes some of the major design issues, and it
provides code excerpts to illustrate how different parts of the application were implemented.
Make sure to read “Chapter 1 – Overview” for general information about database server and SQLWindows
features that affect how you write your applications.

Two Approaches
When writing a multi-database SQLWindows application, you have two basic approaches to choose from.
One approach (the one used in the sample application) is to take the SAL language and certain SQLWindows
features and create as much common code as possible for accessing the different databases from different
vendors. These features include dynamic table windows and automatic column variables. The rest of the
application has code specific to the database server being accessed. This approach is easier to understand
conceptually, gives a standard example of a multi-database application, and results in more understandable
code.
The other approach is more object-oriented—use SQLWindows functional classes to handle accessing
heterogeneous databases and maximize the reuse of code. For information on writing multi-database
applications that create and use functional classes, read the white paper Designing Multi-Database
Applications with Gupta Object Classes. You can access this paper by going to the Gupta Technologies home
page at http://www.guptatechnologies.com.

About the Application


The multi-database application MULTIDB.APP is a single SQLWindows application that a user runs to generate
an invoice for a customer order. The application connects to three different database servers to retrieve,
insert, update, and delete information from six interrelated tables.
The application is also supplied in text form in the file MULTIDB.APT. The application includes code that:
• Connects to each database.
123
• Creates database tables.
• Copies data from one database to another.
• Controls the transaction environment.
• Controls concurrency.
• Retrieves data from multiple databases.
• Manipulates data in multiple databases.
The tables are spread across SQLBase , Microsoft SQL Server , and Oracle. You use a local connection to
connect to SQLBase, the SQLRouter/Oracle to connect (natively) to Oracle, and the SQLRouter/Microsoft SQL
Server to connect (natively) to Microsoft SQL Server.

Schema and Location of Tables


The application uses the tables COMPANY, EMPLOYEE, INVOICE, INVOICE_ITEM, PRODUCT, and PAYMENT
(from the sample ISLAND database supplied with SQLBase). The application has been written to let you make
another copy of the COMPANY table on SQLBase, and to copy the INVOICE, INVOICE_ITEM, and PRODUCT
tables from SQLBase to Oracle, and the EMPLOYEE and PAYMENT tables from SQLBase to Microsoft SQL
Server.
The following table summarizes the original location of the tables in the SQLBase ISLAND database, and
where the tables will reside (in bold) after you select the Populate option. The tables must exist on the
correct database server for the application to run successfully.
The SQLBase tables propagated to Microsoft SQL Server, Oracle, and SQLBase have their names prefixed with
MDB_ to reduce the possibility of a name conflict with existing tables.

Table Location Oracle Microsoft SQL Server

MDB_COMPANY MDB_INVOICE MDB_EMPLOYEE


MDB_INVOICE_ITEM MDB_PAYMENT
MDB_PRODUCT

Running the Application


This section tells you how to run the multi-database application.

Preparing to Run the Application


Before you run the sample multi-database application, you must install the sample applications so that you
include the sample ISLAND database on SQLBase. You must then create a database on your Oracle server and
on Microsoft SQL Server to hold the tables that the sample application expects to find there. You also need to
define the database on Microsoft SQL Server as an ODBC data source by using the ODBC Administrator. (Read
the Microsoft Windows documentation for information about the ODBC Administrator.)
Make sure your database administrator gives you the appropriate permissions to create those tables.

124
Configuring the Application
You must configure the application before connecting to any of the three databases by modifying the SQL.INI
file.
For SQLBase the installation program modifies the SQL.INI for you.
For Microsoft SQL Server modify the SQL.INI file in the section [win32client.dll] by setting the comdll keyword
to sqlodb32 and in the section [odbcrtr] by setting the longbuffer keyword to 10000.
For Oracle modify the SQL.INI file in the section [win32client.dll] by setting the comdll keyword to sqlora32,
and in the section [oragtwy] by defining the remotedbname keyword for your database with the correct
connect string information, setting the longbuffer keyword equal to 200000, and setting the fetchrow
keyword to 20.
For more information about configuring connections, see Chapter 2 – Initializing and Testing Your Connection,
Chapter 4 – Connecting to Microsoft SQL Server, and Chapter 5 – Connecting to Oracle.
The following is an example of the SQL.INI keywords you define to connect to SQLBase, Microsoft SQL Server,
and Oracle:
[dbntsrv] dbdir=c:\gupta
servername=server1,sqlapipe dbname=island,sqlapipe

[dbntsrv.dll] comdll=sqlapipe

[win32client] clientname=client1

[win32client.dll] comdll=sqlapipe comdll=sqlodb32 comdll=sqlora32

[odbcrtr] longbuffer=10000

[oragtwy] REMOTEDBNAME=oratcp,@T:orasrvr.oracle.com:1 longbuffer=200000


fetchrow=20

Note: Refer to the sample SQL.INI file supplied with the multi-database application for help in editing your
own SQL.INI file.

Connecting to the Servers


To connect to the database servers, run the sample application and then log in to each of the servers.

Starting the sample application


5. Start SQLWindows.
6. Select File > Open from the menu bar and then open the sample application MULTIDB.APP. This file is
located in the SAMPLES subdirectory of the Team Developer installation directory.
7. Select Project > Execute from the menu bar to execute the application.

Logging in to the database servers


8. Log in to each of three database servers (SQLBase, Microsoft SQL Server, and Oracle) by selecting File >
Connect from the menu bar.
9. In the dialog box, enter the name of the database you want to connect to.

125
10. Enter a valid user name.
11. Enter a valid password.
12. Choose the appropriate database server.
13. Click Connect.
The default value displayed for Password is ******, but the actual value of the password is SYSADM.
Once you successfully connect to a server, the application removes the name of that server from the
server selection combo box.
14. When you have connected to all three servers, which you must connect do to run the application
successfully, click Exit.

Populating the Tables


After successfully connecting to all three databases, you must populate the tables on Oracle, Microsoft SQL
Server, and SQLBase with data. (You must do this the first time you run the application, and any time the
tables are dropped for any reason on Oracle and Microsoft SQL Server). To populate the tables, select File >
Populate Data from the menu bar and then select either SQLBase, Oracle, or Microsoft SQL Server. The
application copies the appropriate tables from the SQLBase ISLAND database to the target database you
selected. The application also creates the indexes associated with those tables.

Deleting the Tables


To delete the tables and indexes from SQLBase, Microsoft SQL Server or Oracle, select File > Cleanup Data
from the menu bar and then select either SQLBase, Oracle, or Microsoft SQL Server.

Displaying Table Data


To display data in a table window from the COMPANY table (SQLBase), the EMPLOYEE table (Microsoft SQL
Server), the INVOICE table (Oracle), or the PRODUCT table (Oracle), select Table from the menu bar and then
select the appropriate table name.
The displayed form has the following push buttons:
• Refresh—Redisplays all the current data from the database for the selected table window. Click this
button to see any data that has been added to the table or changed in the table.
• New—Positions you to the next available empty row in the table window. Click this button to enter data
for a new row in the table.
• Insert—Inserts new data into the database. (You first click New to enter the new data, then you click
Insert to put the data into the table.)
• Update—Updates the database with the changed data you entered (You select the row to update, you
enter the changes in that row, then you click Update).
• Delete—Delete the selected row from the database. You first select the row by clicking the row header
(the leftmost box displayed in that row), then clicking Delete.
• Details— Displays a details form. From this details form you can add, update, or remove invoice items for
the selected invoice. You can also update certain columns in the PAYMENT table. (This button appears
only on the INVOICE table.)

126
• Apply & Close— Commits all pending changes to each database (assuming autocommit is disabled) and
returns to the main form.
• Discard & Exit—Rolls back all pending changes to each database (assuming autocommit is disabled) and
returns to the main form.

Note: To display the bitmap picture for a given row in the PRODUCT table, click the row header (the
leftmost box) in that row.

About the Details Form


If you select a row in the INVOICE table and click the Details button, you see a details form with three table
windows showing the following information for the selected invoice:
• Invoice items
• Payment record
• PRODUCT table information
The form contains two radio buttons: Invoice Item and Payment. If you select Invoice Item (the default), you
see PRODUCT table information and the focus goes to the invoice items table window. If you select Payment,
you see no PRODUCT table information and the focus goes to the payment record table window.
The details form has the following buttons:
• Refresh—Redisplays all the current data from the database for the table window that has the focus. Click
this button to see any data that has been added to the table or changed in the table.
• New—Positions you to the next available empty row in the table window that has the focus. Click this
button to enter data for a new row in the table. You can click this button only if Invoice Item is selected.
• Insert—Inserts new data into the database. (You first click New to enter the new data, then you click
Insert to put the data into the table.) You can click this button only if Invoice Item is selected.
• Update—Updates the database with the changed data you entered (You select the row to update, you
enter the changes in that row, then you click Update).
• Delete—Delete the selected row from the database. You first select the row by clicking the row header
(the leftmost box displayed in that row), then clicking Delete.
• Apply & Close— Commits all pending changes to each database (assuming autocommit is disabled) and
returns to the form with the INVOICE table.
• Discard & Exit—Rolls back all pending changes to each database (assuming autocommit is disabled) and
returns to the form containing the INVOICE table.

Modifying Autocommit and Isolation Level Settings


To enable or disable autocommit or to modify the isolation levels, select File > Options from the menu bar.
The dialog box displayed shows the current autocommit setting for each database server and the current
isolation level setting for SQLBase and Microsoft SQL Server.
To enable autocommit, check the checkbox. To disable autocommit, clear the checkbox.

127
To set (or change) the isolation level, enter one of RO, RL, CS, or RR in the text field. To find out what these
isolation levels mean for Microsoft SQL Server, see “Isolation Levels” on page 4-5. To find out what these
isolation levels mean for SQLBase, see the SQLBase documentation.
You cannot set an isolation level with Oracle.

Disconnecting and Exiting


To disconnect from all the database servers and exit the application, select File > Exit from the menu bar.

Note: The sample application may not exit or close windows properly if you select Close from the Windows
system menu. Select File > Exit from the menu bar instead.

Design Issues
This section describes some of the design issues raised in the development of the sample application.

Connecting to the Databases


The application begins by logging in and connecting to SQLBase, Microsoft SQL Server, and Oracle. You specify
the name of a valid database, a user name, and password. If you need a user name and password for these
databases, contact your database administrator. You should successfully connect to each of the databases
before you try doing anything else with the application.
The application defines a separate SQL handle variable for each database server. The handle uniquely
identifies a connection between the client application and a database. A handle is specified for every SQL
statement executed.
The following code excerpt shows the use of the login form and the combo box from which the user selects
the database to connect to. If the user succeeds in connecting to the database, the application sets initial
values for the isolation level (RR) and disables autocommit.
Form Window: frmLogin
...
Message Actions
On SAM_Create
Call SalListAdd(cmbServer,COMBO_SQLSERVER)
Call SalListAdd(cmbServer,COMBO_ORACLE)
Call SalListAdd(cmbServer,COMBO_SQLBASE)
Call SalListSetSelect(cmbServer,2)
...
Pushbutton: pbConnect
...
Message Actions
On SAM_Click
Set SqlUser=dfUserName
Set SqlPassword=dfPassword
Set SqlDatabase=dfDatabase
Set nCmbIndex=SalListQuerySelection(cmbServer)
Set strServer=SalListQueryTextX(cmbServer,nCmbIndex)
If strServer=COMBO_SQLSERVER
Set bSQLServerConnect=SqlConnect(hMSSQLServer)
If bSQLServerConnect
128
Set strDBSQLServer=dfDatabase
Set nCmbEntries=SalListDelete(cmbServer,nCmbIndex)
Call SalListSetSelect(cmbServer,nCmbEntries - 1)

! Make isolation level Repeatable Read (RR)


Set strMSSQLSrvrIsoLvl='RR'
Call SqlSetIsolationLevel(hMSSQLServer, strMSSQLSrvrIsoLvl)

! Disable autocommit Set


bMSSQLServerAC=FALSE
Call SqlSetParameter(hMSSQLServer,DBP_AUTOCOMMIT,
bMSSQLServerAC, '')
Else If strServer=COMBO_ORACLE
Set bOracleConnect=SqlConnect(hOracle)
If bOracleConnect
Set strDBOracle=dfDatabase
Set nCmbEntries=SalListDelete(cmbServer,nCmbIndex)
Call SalListSetSelect(cmbServer,nCmbEntries - 1)

! Oracle OCI does not allow setting an isolation


! level, so we don’t in the case of Oracle.

! Disable autocommit
Set bOracleAC=FALSE
Call SqlSetParameter(hOracle,DBP_AUTOCOMMIT, bOracleAC,‘’)
Set dfDatabase=’’
Else If strServer=COMBO_SQLBASE
Set bSQLBaseConnect=SqlConnect(hSQLBase)
If bSQLBaseConnect
Set strDBSQLBase=dfDatabase
Set nCmbEntries=SalListDelete(cmbServer,nCmbIndex)
Call SalListSetSelect(cmbServer,nCmbEntries - 1)

! Make isolation level is Repeatable Read (RR)


Set strSQLBaseIsoLvl=’RR’
Call SqlSetIsolationLevel(hSQLBase,strSQLBaseIsoLvl)

! Disable autocommit
bSQLBaseAC=FALSE
Call SqlSetParameter(hSQLBase,DBP_AUTOCOMMIT, bSQLBaseAC,’’)
Call SqlSetParameter(hSQLBasePop,DBP_AUTOCOMMIT, bSQLBaseAC,’’)
Set dfDatabase=’’ If nCmbEntries=0
Call SalListClear(cmbServer)
Call SalDisableWindow(pbConnect)
...
Message Actions
On SAM_Create
Set dfDatabase='island'
Set dfUserName='sysadm'
Set dfPassword='sysadm'
...

Setting autocommit
In your SQLWindows application you can either specify transaction boundaries explicitly in your application
code, or you can have the database server process each SQL statement in your application as its own
transaction. To specify transaction boundaries explicitly, you first disable autocommit; you then execute
COMMIT or ROLLBACK statements in your application code at the end of every transaction. To have the

129
database server process each SQL statement as its own transaction, enable autocommit. (For more general
information about autocommit, see “Autocommit” on page 12. For information about autocommit and a
given database server, see the chapter devoted to that server.)
In the multi-database application you can enable or disable autocommit independently for each database.
When you write a multi-database application, you need to know what each of the databases you will connect
to does if you disable autocommit, then either disconnect from that database or end the application without
executing a COMMIT or ROLLBACK statement.
For example, if you connect to SQLBase or Oracle and disable autocommit, the database server commits your
last transaction for you if you either disconnect from the database or end your application without executing
a COMMIT or ROLLBACK statement. Under the same circumstances, however, Microsoft SQL Server rolls the
transaction back.
The application initially disables autocommit for all three databases by calling SqlSetParameter with the two
arguments DBP_AUTOCOMMIT and FALSE. With autocommit disabled, the application can ensure that
operations on the various databases are committed only after all portions of a transaction have been
completed successfully.
The code in the application that disables autocommit on SQLBase is the following (hSQLBase is the SQL
handle for the connection to SQLBase):
Call SqlSetParameter(hSQLBase,DBP_AUTOCOMMIT,FALSE,’’)

Note: The sample application controls transactions separately on each of the databases. It does not
perform distributed transactions.

Concurrency and Consistency


When multiple users access the same database, it is very important to allow as many of those users as
possible to get to their data at the same time. The more users can access data at the same time, the more
concurrency there is.
It also is important for multiple users to get consistent data. Ideally, each user should have the impression
that he or she is the only user of the database. The closer the user is brought to this ideal, the more data
consistency there is.
You influence how much concurrency and data consistency there is while accessing your databases through
the use of data locks and application code.

Locking
Intelligent locking improves both concurrency and consistency. Different databases support different locking
strategies and implement different ways for users to set locks. For example, both SQLBase and Microsoft SQL
Server lock entire pages of data. Oracle, on the other hand, locks individual rows (when you execute a
SELECT...FOR UPDATE statement). However, you cannot choose the kind of lock Oracle places on that row.

Note: To set a particular kind of lock in SQLBase and Microsoft SQL Server, you set a certain isolation level
(see “Isolation Levels”, which follows).

Locking strategies are either pessimistic or optimistic. A pessimistic locking strategy assumes that another
user will attempt to update the same row you might be looking at. If you choose this strategy, the database
server locks the row first before you get to see the data, thus ensuring a high degree of data consistency. An
optimistic locking strategy assumes the opposite: no other user will attempt to update the row you are
130
looking at. If you choose this strategy, the database server does not lock the row, thus ensuring a higher
degree of concurrency.
If you adopt an optimistic locking strategy, you must put checks into your code to verify for yourself that
another user of the database has not updated any of the data that the user of your application is viewing.

Isolation levels
SQLBase and Microsoft SQL Server allow you to set isolation levels. An isolation level defines the degree to
which transactions are prevented from interfering with each other. You set the isolation level in your
SQLWindows application by calling the SAL function SqlSetIsolationLevel.
Most databases support several isolation levels. However, the isolation levels are not defined in the same way
on all the databases. The ANSI/SQL committee has defined a standard set of isolation levels with standard
semantics, but most databases do not implement these isolation levels with precisely those semantics.
Some of the names commonly used by database vendors for isolation levels are: Dirty Read, Uncommitted
Read, Cursor Stability, Repeatable Read, and Serializable. Other names you will see include Browse, Read
Only, Release Locks, and Hold Lock.
Warning: Some databases use the same names as those specified by the ANSI/SQL committee for isolation
levels, but they do not implement the ANSI/SQL semantics for those isolation levels. Do not assume you know
what an isolation level means just from its name. You must read the database vendor documentation to find
out what the behavior is for each of the isolation levels defined by that vendor.
Most relational databases allow users to set isolation levels (either at the statement level, the transaction
level, or both) at run-time. These databases also define a default isolation level, but the default varies from
database to database.

Note: Oracle does not allow your application to set an isolation level. This is because SQLWindows
applications use dynamic SQL, and Oracle does not support the setting of isolation levels with
dynamic SQL

For example, the default isolation level in SQLBase is called Repeatable Read (RR). SQLBase implements this
isolation level by maintaining a read lock on the requested row until the transaction has been committed.
Another example comes from Microsoft SQL Server, where the default isolation level is called Read
Uncommitted. With this isolation level, the database may send a client requesting a row the most recently
changed data in that row even if those changes have not yet been committed.
Even though these isolation levels vary in syntax and semantics for each database, SQLWindows allows for up
to four isolation levels. The semantics of these isolation levels depends on the database your application
connects to. To learn which isolation levels are supported for a given database, see the chapter in this
document devoted to that database.
For example, to set the isolation level on Microsoft SQL Server to SQL_TXN_SERIALIZABLE, make the call:
Call SqlSetIsolationLevel(hMSSQLServer,‘RR’)
The application chooses an initial value for the isolation level to guarantee that the data remain consistent
until the application executes a COMMIT or ROLLBACK statement. With this isolation level other database
users are prevented from changing locked data until the application has completed the transaction.
The application executes all SQL statements as Dynamic SQL statements.

131
Note: Because the Oracle OCI does not support the setting of isolation levels for Dynamic SQL statements,
the application does not set an isolation level with Oracle.

Database-oriented client/server applications fall into two broad categories: transaction-based applications
and decision support applications.
Transaction-based applications generally affect one or more tables. All work related to a single transaction
should be committed only if all the operations in that transaction completed successfully. If the application
detects the violation of an integrity constraint, it might choose to roll back the transaction.
Decision support applications typically do a lot of queries and only occasionally change the database. Because
decision support queries usually take a long time to execute, you normally do not want to acquire any locks
along the way. If you do choose to acquire locks during the query, you normally release those locks as soon as
the query is done.
Transaction-based applications need to support a high degree of both concurrency and consistency among
users because these applications are typically run by many users at the same time. To support a high degree
of concurrency these applications typically select the most permissive isolation levels available. These
isolation levels direct the database either to not lock data (allowing dirty reads) or to release locks very
quickly, such as releasing a lock as soon as a row is read. This isolation level is often called “cursor stability”.
Decision support applications do not require high degrees of concurrency; they can use much more restrictive
locking to maintain more data consistency. However, if you write a decision support application knowing it
will access the same database as a transaction-based application, you should design it to use (at least nearly)
the same isolation level as the transaction-based application to support the high degree of concurrency
needed by the transaction-based application.
One way applications can get both a high degree of concurrency and a high degree of data consistency is to
choose a permissive isolation level (one that releases locks quickly), enable cursor context preservation (CCP),
then commit transactions quickly (either by enabling autocommit or executing the COMMIT statement
frequently).

Note: Generally, if both decision support and transaction-based applications access the same database, the
decision support applications should use an optimistic locking strategy to reduce their impact on the
transaction-based applications.

The sample multi-database application combines elements of both a decision support application and a
transaction-based application. The portion that is like a decision support application processes the COMPANY,
PRODUCT, and EMPLOYEE tables; the portion that is transaction-based handles the creating of invoices and
the accessing of the INVOICE_ITEM, PAYMENT, and PRODUCT tables. Because the application is a hybrid, it
needs a high degree of both consistency and concurrency. This application sets the isolation level to
Repeatable Read and disables autocommit. In your own application you must make appropriate design
decisions (such as choosing an isolation level) that let you strike a suitable balance between concurrency and
data consistency.

Replicating Table Schemas and Data


The application creates the schema of the INVOICE, INVOICE_ITEM, and PRODUCT tables in the SQLBase
ISLAND database on the Oracle database server with the CREATE TABLE statement. The application copies the
data from SQLBase to Oracle using SELECT... FROM and INSERT... INTO statements.

132
There are some data type differences among SQLBase, Oracle, and Microsoft SQL Server. You see these kinds
of differences whenever you write a single application that accesses heterogeneous databases. Some data
types are not supported on all the databases your application must access.
For example, the picture column in the PRODUCT table on SQLBase is a LONG column. When the application
moves this data to Oracle, it redefines this column as LONG RAW data because the column contains binary
data.
The application copies table data from SQLBase to Oracle, Microsoft SQL Server, or SQLBase itself by
executing a SELECT from SQLBase, then executing an INSERT statement to place the data into the target
database. The application also creates table indexes on the target database.
When creating tables on one database server to receive data from tables on another database server, you
must specify suitable column type definitions in the CREATE TABLE statement. Because different database
servers do not all support the same set of data types, you must decide which data types to use when you
copy data from one database server to another.
For example, most database servers support a data type called VARCHAR. However, the length bounds for this
data type vary from one database server to another.
Depending on the size of the actual VARCHAR data you are copying, you may have to specify a column type
other than VARCHAR (such as LONG, for example) on the database server to which you are copying the data.
Depending on the databases you are accessing in your own applications, you may want to use a different
technique whenever you need to copy data from one database to another, especially if you are moving large
amounts (megabytes) of data. Read the documentation on the databases your applications access for
information that may help you decide how to perform such an operation efficiently.
In the following excerpt, the application creates the EMPLOYEE table on Microsoft SQL Server and the
INVOICE table on Oracle; it then copies the data from the corresponding SQLBase tables to the target
database using the TransferData internal function.
Popup Menu: &Populate Data
...
Menu Item: Microsoft SQL Server
...
Menu Settings
Enabled when: hMSSQLServer
Checked when:
Menu Actions
Set strSQL = ‘CREATE TABLE ‘|| MDB_EMPLOYEE ||
‘ (EMPLOYEE_ID CHAR (8),’ ||
‘LAST_NAME VARCHAR (30),’ ||
‘FIRST_NAME VARCHAR (30),’ ||
‘MI CHAR (1),’ ||
‘EXTENSION VARCHAR (4),’ ||
‘OFFICE_NO VARCHAR (5),’ ||
‘DEPT_ID CHAR (5),’ ||
‘DATE_HIRED DATETIME,’ ||
‘CURRENT_SALARY FLOAT (8))’
If NOT SqlPrepareAndExecute(hMSSQLServer,strSQL)
Set strMsgPrompt=MSGBOX_MSGPART1 || MDB_EMPLOYEE || MSGBOX_MSGPART2
Set nMsgResult=SalMessageBox(strMsgPrompt,MSGBOX_TITLE,MB_OkCancel)
If nMsgResult=IDCANCEL
Return -1
Else
Call TransferData(MDB_EMPLOYEE)
Call SalMessageBox('Create Index for ' || MDB_EMPLOYEE,
'Create Index',MB_Ok)
Set strSQL='CREATE UNIQUE INDEX ' || MDB_EMPLOYEE_IDX ||
133
' ON ' || MDB_EMPLOYEE || ' (EMPLOYEE_ID)'
If NOT SqlPrepareAndExecute(hMSSQLServer,strSQL)
Call SalMessageBox('SQL Error Creating Index ' ||
MDB_EMPLOYEE_IDX,'Error!',MB_Ok)
...

Menu Item: Oracle


...
Menu Settings
Enabled when: hOracle
Checked when:
Menu Actions
Set strSQL=’CREATE TABLE ‘ || MDB_INVOICE ||
‘ (INVOICE_NO DECIMAL(8, 0) NOT NULL,’ ||
‘COMPANY_ID SMALLINT NOT NULL,’ ||
‘COMPANY_NAME VARCHAR(30),’ ||
‘INVOICE_DATE DATE,’ ||
‘DATE_PAID DATE,’ ||
‘STATUS VARCHAR(18),’ ||
‘AMOUNT_PAID DECIMAL(8,2),’ ||
‘EMPLOYEE_ID CHAR(8))’

If NOT SqlPrepareAndExecute(hOracle,strSQL)
Set strMsgPrompt = MSGBOX_MSGPART1 || MDB_INVOICE || MSGBOX_MSGPART2
Set nMsgResult=SalMessageBox(strMsgPrompt,MSGBOX_TITLE, MB_OkCancel)
If nMsgResult=IDCANCEL Return -1
Else
Call TransferData(MDB_INVOICE)
Call SalMessageBox('Create Index for ' || MDB_INVOICE,
'Create Index',MB_Ok)
Set strSQL='CREATE INDEX ' || MDB_INVOICE_IDX ||
' ON ' || MDB_INVOICE || ' (INVOICE_NO)'
If NOT SqlPrepareAndExecute (hOracle,strSQL)
Call SalMessageBox ('SQL Error Creating Index ' ||
MDB_INVOICE_IDX,'Error!',MB_Ok)
...
The TransferData function accepts the table name as input, then transfers data from SQLBase to the target
database. If the input table name is MDB_COMPANY, the target database is SQLBase. If MDB_EMPLOYEE or
MDB_PAYMENT, the target database is Microsoft SQL Server. Otherwise, the target database is Oracle.
Function: TransferData
Returns
Boolean: bOk
Parameters
String: strTableName
...
Actions
When SqlError
Call SalMessageBox ('SQL Error in Transfer Data function',
'Transfer Data Error!',MB_Ok)
Return FALSE
If strTableName=MDB_EMPLOYEE
! Read from SQLBase employee table into MS SQL Server
Set strTempSQL=’INSERT INTO ‘|| MDB_EMPLOYEE ||
' VALUES (:dfCol1,:dfCol2,:dfCol3,:dfCol4,:dfCol5,
:dfCol6,:dfCol7,:dfCol8,:dfCol9)'
Call SqlPrepare(hMSSQLServer,strTempSQL)
Set strSQL='SELECT * FROM EMPLOYEE INTO ‘ ||
‘:dfCol1,:dfCol2,:dfCol3,:dfCol4,:dfCol5,’ ||
‘:dfCol6,:dfCol7,:dfCol8,:dfCol9’
134
If SqlPrepareAndExecute(hSQLBasePop,strSQL)
Loop TData1
If SqlFetchNext(hSQLBasePop,nRetval)
Call SqlExecute(hMSSQLServer)
Else
Break TData1

! If autocommit is off for Microsoft SQL Server,


! commit the changes.
If NOT bMSSQLServerAC
Call SqlCommit(hMSSQLServer)
Return TRUE
...
If strTableName=MDB_INVOICE
! Read the SQLBase invoice table into Oracle
Set strTempSQL=’INSERT INTO ‘ || MDB_INVOICE ||
‘VALUES(:nInvNum,:nCompId,:strCompName,:dInvDate,’ ||
‘:dDatePaid,:strStatus,:nAmtPaid,:strEmpId)’
Call SqlPrepare(hOracle,strTempSQL)
Set strSQL = ‘SELECT * FROM INVOICE INTO ‘ ||
‘:nInvNum,:nCompId,:strCompName,:dInvDate,’ ||
‘:dDatePaid,:strStatus,:nAmtPaid,:strEmpId’
If SqlPrepareAndExecute(hSQLBasePop,strSQL)
Loop TData3
If SqlFetchNext(hSQLBasePop,nRetval)
Call SqlExecute(hOracle)
Else
Break TData3

! If autocommit is off for Oracle, commit the changes.


If NOT bOracleAC
Call SqlCommit(hOracle)
Return TRUE
...

Displaying Table Data


The application displays table data in dynamic table windows (by calling the SAL function SalTblPopulate). A
dynamic table window uses automatic columns, which you can use to display the results of any query
whatsoever. Automatic columns are of type STRING, but can display data of any type (except the LONG data
types). For more information about SalTblPopulate, see the SQLWindows Function Reference.
In the following code excerpt, the application retrieves data from the COMPANY table (in SQLBASE) into a
dynamic table window.
Menu Item: &Company
...
Menu Settings
Enabled when: hSQLBase
Checked when:
Menu Actions
Call SalShowWindow(tblInfo)
Call SalBringWindowToTop(tblInfo)
...
Set strSQL = 'SELECT * FROM COMPANY'
Call SalTblPopulate(tblInfo,hSQLBase,strSQL,TBL_FillNormal)
...

135
In the following code excerpt, the application retrieves data from the EMPLOYEE table (in Microsoft SQL
Server) into a dynamic table window.
Menu Item: &Employee
...
Menu Settings
Enabled when: hMSSQLServer
Checked when:
Menu Actions
Call SalShowWindow(tblInfo)
Call SalBringWindowToTop(tblInfo)
...
Set strSQL=’SELECT * FROM ‘|| MDB_EMPLOYEE
Call SalTblPopulate(tblInfo,hMSSQLServer,strSQL, TBL_FillNormal)
In the following code excerpt, the application retrieves data from the PRODUCT table (in Oracle) into a
dynamic table window.
Menu Item: &ProductInvoice
...
Menu Settings
Enabled when: hOracle
Checked when:
Menu Actions
Call SalShowWindow(tblInfo)
Call SalBringWindowToTop(tblInfo)
Call SalShowWindow(picPicture)
...
Set strSQL='SELECT STYLE_ID,STYLE,DESCRIPTION,PRICE FROM ' || MDB_PRODUCT
Call SalTblPopulate(tblInfo,hOracle,strSQL,TBL_FillNormal)
...

Key Columns
The key columns for the tables used in the multi-database application are the following:
• company_id in the COMPANY table
• employee_id in the EMPLOYEE table
• invoice_no in the INVOICE table
• style_id in the PRODUCT table
You cannot update a key column.

Transaction Processing
The multi-database Team Developer application centers around four transactions.
Three of these transactions involve just a single SQL table located on a specific database server. None of these
tables has dependencies on any other table.
The three transactions are the following:
• Retrieve, insert, delete, and update the COMPANY table on SQLBase.
• Retrieve, insert, delete, and update the PRODUCT table on Oracle.
• Retrieve, insert, delete, and update the EMPLOYEE table on Microsoft SQL Server.

136
The more complex transaction is the INVOICE transaction. It involves multiple tables: the INVOICE and
INVOICE_ITEM tables on Oracle and the PAYMENT table on Microsoft SQL Server. When the user clicks the
Details button for a specified invoice (and the user has also selected the Invoice Item radio button), the
application displays a form showing all invoice items, a single payment entry, and a PRODUCT table listing.
The sample application manages two independent transactions: one on Oracle and the other on Microsoft
SQL Server; it does not process this transaction as a distributed transaction between Oracle and Microsoft
SQL Server.
A distributed transaction is one where the transaction is shared by two or more (possibly heterogeneous)
database servers. When a client executes the COMMIT statement, all the servers participating in the
transaction must coordinate to make a collective yet single decision whether to register all the changes in the
transaction or to undo all the changes.
A commit coordinator (sync-point manager) manages this decision-making process. The coordinator knows
about all the participants, issues a prepare for commit, and receives acknowledgments. When the
coordinator receives all the acknowledgments, it issues a commit to all participants. Once the participants
have all acknowledged a successful commit, the coordinator writes a commit marker. The commit coordinator
also keeps track if a database server goes down during the second portion of the commit phase. If that
happens, the coordinator helps to resolve the transaction.
If a user adds a new invoice item for a selected invoice number, the style_id and item_price columns in the
INVOICE_ITEM table are set from the chosen product selected from the PRODUCT table. Once a quantity is
specified, the amount column in the PAYMENT table is adjusted to reflect the new product quantity and price
by summing all invoice item quantities times their unit price for the specified invoice number.
If the user removes an invoice item for a selected invoice number, the amount column in the PAYMENT table
is adjusted to reflect the removal of a product quantity and price by summing all invoice item quantities times
their unit price for the specified invoice number.
If the user changes the quantity associated with a selected invoice item, the amount column in the PAYMENT
table is adjusted to reflect the modifying of a product quantity and price by summing all invoice item
quantities times their unit price for the specified invoice number.

Note: One useful way to enhance the sample application is to make sure that another application cannot
change a row that the sample application has read until it executes a COMMIT or ROLLBACK. You can
do this on Oracle by retrieving data with the SELECT...FOR UPDATE statement.

The application locks the PAYMENT data because it uses an isolation level of Repeatable Read on Microsoft
SQL Server.
Since the sample application is predominantly transaction-based, we disable autocommit; this lets the
application take control of the transaction boundaries. When the user creates or updates an invoice item, the
application needs to guarantee that all changes are committed only when changes have been successfully
made to all tables. If even one of the updates fails, the application rolls back all the changes that make up the
transaction.
The application must also guarantee the integrity of each row in the tables affected by the transaction. No
client should be allowed to update a row that another client has either read or updated. To do this the
application selects the isolation level Repeatable Read. This puts a lock on every row a user fetches and
maintains that lock until the user commits (or rolls back) the transaction—other users cannot write to any of
the locked rows.
For example, when a user creates a new invoice item, the PAYMENT row needs to be locked while the
application updates the amount column, summing all invoice item quantities and their unit prices. The

137
PRODUCT table row for this invoice item needs to locked so that information cannot be changed and that
PRODUCT entry itself cannot be removed. All other invoice items for the selected invoice number need to be
locked since the application uses those quantities and unit prices to calculate a new amount in the PAYMENT
table. Also, none of the invoice items should be removed while the application does the calculation.
To manage transactions explicitly an application must disable autocommit.
If you disable autocommit and fail to commit or roll back the last transaction before disconnecting or ending
the application, some databases commit the last transaction while others roll it back. Read your database
vendor documentation to find out what your database does in that situation.

Processing the INVOICE Transaction


The application allows a user to display INVOICE_ITEM entries and PAYMENT information for a selected
invoice. A user can insert, update, or delete new invoice items. The application adjusts the associated
PAYMENT amount for the selected invoice accordingly. The application allows the user to update a PAYMENT
entry but not delete it. Because there is only one PAYMENT record for each invoice number, a PAYMENT
record must exist as long as an invoice exists. A user cannot add a new PAYMENT entry for a selected invoice
number because there is only one PAYMENT record for each invoice number. A user also cannot update
selected fields in either the INVOICE_ITEM or PAYMENT table.
For example, item_price in the INVOICE_ITEM table is set from the PRODUCT table price column; item_no
and invoice_no are key fields. The amount column in the PAYMENT table is calculated by summing all invoice
items (quantity column times item_price column); invoice_no is the key field.

Dynamic Binding
Dynamic binding in SQLBase and any database you access using ODBC allows you to use column variables of
type STRING as bind variables. Because you cannot use dynamic binding with Oracle, you must define each
column variable with the correct data type.
In the following code excerpt, the application uses automatic column variables as bind variables to update the
COMPANY table on SQLBase and the EMPLOYEE table on Microsoft SQL Server.
Pushbutton: pbUpdate
...
Message Actions
On SAM_Click
Set nNumCols=SalSendMsg(tblInfo, SAM_GETCOLCOUNT, 0, 0)
Set strSQL='UPDATE '
If nTableIndicator=SQLBASE_TABLE
Set strSQL=strSQL || MDB_COMPANY || ' SET '
Set hLocalSql=hSQLBase
Else If nTableIndicator = MSSQLSERVER_TABLE
Set strSQL=strSQL || MDB_EMPLOYEE || ' SET '
Set hLocalSql=hMSSQLServer
Set nIndex = 2
Loop Binding2
Call SalTblGetColumnTitle(
SalTblGetColumnWindow(tblInfo,nIndex,COL_GetPos),
strColName,MAXCOLNAME_LENGTH)
If nIndex < nNumCols
Set strSQL=strSQL || strColName || ‘= :’ ||
‘tblInfo#’ || SalNumberToStrX (nIndex,0) || ‘, ‘

138
Else If nIndex = nNumCols
Set strSQL=strSQL || strColName || ‘= :’ ||
‘tblInfo#’ || SalNumberToStrX(nIndex,0)
Break Binding2
Set nIndex=nIndex + 1
Call SalTblGetColumnTitle(
SalTblGetColumnWindow(tblInfo,1,COL_GetPos),
strColName,MAXCOLNAME_LENGTH)
Set strSQL=strSQL || ‘ WHERE ‘ || strColName ||
‘ = :tblInfo#1’
Call SqlPrepare(hLocalSql,strSQL)
Call SalTblDoUpdates(tblInfo,hLocalSql,TRUE)
...
The following is an example of similar code required to update the INVOICE table on Oracle where we cannot
take advantage of STRING automatic column variables as bind variables:
Pushbutton: pbUpdate
...
Message Actions
On SAM_Click
Set strSQL='UPDATE '
If nTableIndicator=ORACLE_TABLE1
Set strSQL=strSQL || MDB_INVOICE || ' SET '
Set hLocalSql=hOracle
Set strSQL = strSQL ||
‘COMPANY_ID=:nCompId,’ ||
‘COMPANY_NAME=:strCompName,’ ||
‘INVOICE_DATE=:dInvDate,’ ||
‘DATE_PAID=:dDatePaid,’ ||
‘STATUS=:strStatus,’ ||
‘AMOUNT_PAID=:nAmtPaid,’ ||
‘EMPLOYEE_ID=:strEmpId ‘ ||
‘WHERE INVOICE_NO=:nInvNum’
Set nInvNum=SalStrToNumber(tblInfo#1)
Set nCompId=SalStrToNumber(tblInfo#2)
Set strCompName=tblInfo#3
Set dInvDate=SalStrToDate(tblInfo#4)
Set dDatePaid=SalStrToDate(tblInfo#5)
Set strStatus=tblInfo#6
Set nAmtPaid=SalStrToNumber(tblInfo#7
Set strEmpId=tblInfo#8
Call SqlPrepare(hLocalSql,strSQL)
Call SalTblDoUpdates(tblInfo,hLocalSql,TRUE)
...
Dynamic binding only works if the statement string is in single quotes, example: ‘Hello’ or a string variable is
defined.
The sample application assumes the first column is the key field.

Implementation Details
This section describes some of the implementation details involved in updating table data, dynamic table
windows management, dynamic data type binding, isolation levels, and autocommit.

139
Updating Table Data
The sample application assumes that the first column of a table is a key column; the application uses this key
for update or delete operations. This assumption allows the application to make use of common code when
building the WHERE clause of a SELECT, UPDATE, or DELETE statement.
The following is an example of how the application builds the WHERE clause of a SELECT statement:
Call SalTblGetColumnTitle(
SalTblGetColumnWindow (tblInfo,1,COL_GetPos),
strColName,MAXCOLNAME_LENGTH)
Set strSQL=strSQL || ' WHERE ' || strColName ||
' = :' || 'tblInfo#1'

Dynamic Table Windows Management


Dynamic table windows let the developer use table windows without defining as much about them at design
time. You use dynamic table windows to display data from different user queries. These queries can specify
differing numbers of columns and even different data types. To display data in a dynamic table window, call
SalTblPopulate.
The application specifies TBL_FillNormal when calling SalTblPopulate; this populates only the visible portion
of the table window. New rows are fetched only when they are brought into view. Since the tables have only a
small number of rows, specifying TBL_FillNormal does not improve performance significantly. However, for
tables that have hundreds or perhaps thousands of rows, specifying TBL_FillNormal can greatly improve
performance.

Microsoft SQL Server and SQLBase


For the Populate menu option, the TransferData function fetches row data from SQLBase into generic column
variables of type STRING—for example, dfCol1, dfCol2, and so on. When the application inserts a row data
into the Microsoft SQL Server database or the SQLBase database, it uses these same generic column variables
as the program variables.
The Insert, Update, Delete, and Refresh buttons use the automatic column variables created by
SalTblPopulate as the bind variable names on the associated INSERT, UPDATE, DELETE, or SELECT statement.
The automatic column variables are of type STRING and have the following syntax:
table_name#column_number
For example, the first column variable for dynamic table tblInfo is tblInfo#1.
If the user selects the Table menu option, the application calls SalTblPopulate to build a dynamic table
window for the selected table: the COMPANY table on SQLBase and the EMPLOYEE table on Microsoft
SQLServer.

Oracle
The PICTURE column from the PRODUCT table is not displayed as a column in the dynamic table window
because the data is a bit map. There is a separate area on the form where the product image is displayed if
the user clicks on the row header of a given PRODUCT table entry.
If the user selects the Table menu option, the application calls SalTblPopulate to build a dynamic table
window for the INVOICE and PRODUCT tables on Oracle.

140
Dynamic Data Type Binding
Because Oracle does not support dynamic data type binding, the application does not use the automatic
column variables as bind variables for data types other than STRING in the INSERT, UPDATE, or DELETE
statements. For the NUMBER and DATE data types, the application calls SalStrToNumber and SalStrToDate to
convert the automatic variable string data to the correct number or date format—the application uses a
specific bind variable of that data type.

Oracle
For the Populate menu option, the TransferData function fetches row data from SQLBase into specific data
type column variables (for example, nCompId of type NUMBER, dDatePaid of type DATE/TIME, and so forth).
When the application inserts a row of data into the Oracle database, it uses these data-type-specific variables
as the program variables.
When the user clicks the Insert, Update, Delete, or Refresh buttons, the application calls an appropriate SAL
function to convert the data associated with each dynamic column variable created by SalTblPopulate into a
NUMBER or DATE/TIME specific column variable. The application uses these data-type-specific column
variables as bind variable names on the associated INSERT, UPDATE, DELETE, or SELECT statement.
To copy data from SQLBase to Oracle, the application must map the SQLBase column data types to the Oracle
data types. Because SAL has no functions for obtaining the data type of each column in a table, the
application must hard-code the appropriate values of the CREATE TABLE statement that it executes on the
target database (the one to which it copies the data). For example, the picture column is a bit map stored on
SQLBase in a LONG column. To store this data on Oracle, the application must use a LONG RAW column.

Microsoft SQL Server


To copy data from SQLBase to Microsoft SQL Server, the application must map the SQLBase column data
types to the Microsoft SQL Server data types. Because SAL has no functions for obtaining the data type of
each column in a table, the application must hard-code the appropriate values of the CREATE TABLE
statement that it executes on the target database (the one to which it copies the data).

Isolation Levels and Oracle


Oracle does not support the setting of isolation levels using dynamic SQL. Because of this, you have two ways
of locking data in a table on Oracle:
• Lock the entire table with the LOCK TABLE statement.
• Lock certain rows with the SELECT...FOR UPDATE statement.
Locking an entire table is very heavy-handed and risky. No one other than the user who has locked the table
can do any operations on the table until it is unlocked. Even if the application locks the table for only a short
period of time, the penalty paid by other users is usually unacceptably high.
Locking only certain rows during a transaction is much more selective and allows other users to access all
data in the database except for the locked rows. However, if the user who has the lock takes a long time to
complete the transaction, all other users who need to access that data are forced to wait.
The sample application favors a high degree of concurrent access to the database over a high degree of data
consistency by not locking any of the Oracle table data that it accesses.

141
Autocommit
The sample application turns off autocommit for SQLBase, Microsoft SQL Server, and Oracle. The application
is responsible for committing (or rolling back) the operations that make up a transaction.
You may find it instructive to modify the sample application by enabling autocommit and observe how the
application runs differently.
While the application does manage its own transactions, it does not manage the operations on both Oracle
and Microsoft SQL Server as a single distributed transaction. Instead, the application commits changes to
INVOICE and INVOICE_ITEM tables (on Oracle) separately from the PAYMENT table (on Microsoft SQL Server).
For example, if the commit fails for the PAYMENT table, the application rolls back not only the changes in the
PAYMENT table on Microsoft SQL Server, but also the changes in the INVOICE and INVOICE_ITEM tables on
Oracle. If the commit succeeds for the PAYMENT table, the application commits the changes to the INVOICE
and INVOICE_ITEM tables on Oracle also. If the commit for those tables fails, you can roll back the changes in
those tables, but at this point you cannot go back to Microsoft SQL Server and roll back the PAYMENT table
changes already committed.
This code excerpt shows how the application coordinates commit (and rollback, if required) between
Microsoft SQL Server and Oracle:
On SAM_Click

! Ok, commit all the changes.


If NOT bMSSQLServerAC

! If commit on MS SQL Server fails and autocommit is off,


! roll back Microsoft SQL Server and Oracle.
If NOT SqlCommit(hMSSQLServer)
If SqlPrepareAndExecute(hMSSQLServer,’rollback’)
Call SalMessageBox(‘Microsoft SQL Server changes ‘ ||
‘were successfully rolled back’, ‘Rollback Status’,MB_Ok)
If NOT bOracleAC
If SqlPrepareAndExecute(hOracle,'rollback')
Call SalMessageBox(‘Oracle changes were ‘ ||
‘successfully rolled back’,’Rollback Status’,MB_Ok)
Return -1
Else
Call SalMessageBox(‘Microsoft SQL Server changes were ‘ ||
‘successfully committed’,‘Commit Status’,MB_Ok)

! If the commit was successful on Microsft SQL Server, perform


! a commit on Oracle. If the commit fails, perform a rollback
! on Oracle. NOTE: You cannot go back to Microsoft SQL Server
! and roll back the updates if the commit on Oracle failed.

If NOT bOracleAC
If NOT SqlCommit(hOracle)
If SqlPrepareAndExecute(hOracle,'rollback')
Call SalMessageBox(‘Oracle changes were successfully’ ||
‘ rolled back’,’Rollback Status’,MB_Ok)
Return -1
Else
Call SalMessageBox( 'Oracle changes were successfully’ || ‘
committed’,’Commit Status’,MB_Ok)
Important: If you successfully commit on Microsoft SQL Server, try to commit on Oracle. If the commit on
Oracle fails, you can do a rollback there, but you cannot roll back the changes you committed on Microsoft
SQL Server.
142
Getting More Information
For more information about transactions, you can read Transaction Processing: Concepts and Techniques by
Jim Gray and Andreas Reuter. You might also want to find out if your database servers can handle distributed
transactions using industry standard protocols such as X/Open XA+. If they do, you might want to adopt those
protocols using the External Call Functions feature of SQLWindows.

143
Glossary
bind variable—A variable that associates data to an SQL statement at runtime. You can use bind variables in
the VALUES clause of an INSERT statement, in a WHERE clause, or in the SET clause of an UPDATE statement.
commit—The act of making permanent pending changes against a database.
cursor—A work space in memory that is used for processing an SQL statement. This work space contains the
return code, number of rows, error position, number of select list items, number of program variables,
rollback flag, and the command result. A cursor is part of a SQL handle. Alternatively, a pointer to a row in a
result set.
DCC—Database Connectivity Configuration.
DDL—SQL statements that define and alter database structures, such as tables, views, and indexes. Example
statements are CREATE TABLE and DROP INDEX.
DML—SQL statements that add, delete, change, or retrieve data from the database, such as INSERT, DELETE,
UPDATE, or SELECT.
Data Definition Language—see DDL.
Data Manipulation Language—see DML.
dirty read—A read of a value that was never committed to the database. For example, transaction A changes
one or more column values in a row. Transaction B reads the same row, after which transaction A rolls back its
changes. Transaction B has now performed a dirty read, because it read values that (from a transaction-
oriented point of view) never existed.
nonrepeatable read—The reading of data from a given row such that the values read are not guaranteed to
be always the same. For example, transaction A reads a row, then transaction B either updates or deletes the
row. Transaction A cannot now repeat the read of that row and get the same results; either the values are
different or the row no longer exists.
phantom row—A row that appears in between two executions of the same SELECT statement. For example,
transaction A performs a SELECT on a table. Based on the selection criteria in the SELECT statement, the
database server returns a set of rows to A. Transaction B inserts one or more new rows into the same table
with values that happen to match the selection criteria of the SELECT statement used by A. If transaction A re-
executes the same SELECT statement, it obtains a different set of rows than it did the first time; the new rows
are phantom rows.
rollback—The act of undoing pending changes against a database.
SAL—See Scalable Application Language.
Scalable Application Language—An object-oriented language for writing SQLWindows/32 applications.
SQL handle—A program variable that identifies a connection context to a database. Multiple SQL handles can
share the same connection to a database. It is often true that an action performed on any one of those
handles actually affects all the handles sharing the same connection. See also cursor.
SQLRouter—A software library that maps between the actions and data types of a SQLWindows/32
application and the actions and data types of a target database or data source.
transaction—A logical unit of work whose elements (changes either to database data or to database data
structures) can be potentially applied to a database. The elements of a transaction are either all applied to
the database or else all discarded; the elements of a transaction constitute an indivisible unit.

144
145

You might also like