![]() | ![]() |
Home |
|
|
Access Service User's Guide DirectConnect for AS/400 |
|
| Chapter 6 Issuing SQL statements |
|
| SQL Transformation modes |
|
| Types of commands |
As a developer, you can issue the following types of commands:
Language commands
Dynamic commands
Cursor commands
RPC requests
The first three topics are presented in this chapter. RPC requests are described in Chapter 8, "Issuing Remote Procedure Calls"
Language commandsLanguage commands are the simplest way to issue a request, but there are drawbacks to using them:
You cannot control incoming datatype conversion.
You must issue a request and finish processing it before you can issue another request.
With certain commands, performance is slower than with dynamic and cursor commands.
Long character-string substitution is not done for select statements.
For information about how to issue a language command, see the appropriate Sybase documentation.
Dynamic commandsDynamic commands are available only with Open Client CT-Library System 10 or greater. This section describes how DirectConnect processes dynamic commands through the AS/400.
Dynamic commands allow an application to execute SQL statements, such as insert, update, and delete, that contain variables the values of which are determined at run-time, in the following manner:
The SQL statement is bound to a SQL package on the AS/400, making it a prepared statement.
The statement executes, and the client application passes any argument data in the SQL descriptor area (SQLDA) of the execution instruction.
The AS/400 substitutes any argument data for the parameter markers in the prepared SQL statement.
Following is an example of a dynamic SQL command:
insert into table2 (col1, col2) values (SALES1, SALES2)
Following is the same command using parameter markers:
insert into table2 (col1, col2) values (?, ?)
The access service prepares the preceding statement. Then, the statement executes and substitutes the values SALES1 and SALES2 into the parameter markers.
You can prepare and execute dynamic SQL statements by mapping these commands to AS/400 capabilities. For example:
You can prepare any non-select statement and execute the prepared statement as many times as you want.
You can execute non-select statements through the dynamic command execute immediate capability.
For more information about how to prepare and execute statements with dynamic SQL, see the Sybase Open Client Client-Library/C Reference Manual and the IBM AS/400 DB2/400 SQL Reference Manual.
Following is a description of dynamic command capabilities that DirectConnect supports, as well as the limitations that apply.
Only CT-Library client applications using Open Client System 10 or greater can use dynamic commands.
You can only prepare use procedure, transfer, and most non-select SQL statements, such as insert, update, and delete statements. For a current list of SQL statements, see the IBM AS/400 DB2/400 SQL Reference manual.
The access service does not support the from clause in an update statement.
The access service supports a maximum of 50 simultaneously prepared dynamic statements per client connection.
When the client application issues a commit or rollback, the AS/400 releases all resources associated with the prepared statement. The client application must also free its connection at this point so that CT-Library synchronizes with the actual state of both the AS/400 and the access service.
The access service can support both short and long transactions in either allocate on request or allocate on connect mode.
The first time an application issues a prepare (or declares a cursor), the access service forces TransactionMode for that connection to long. The Allocate property remains unchanged.
The access service supports the execute immediate capability of a dynamic command, but the statement must not return data. (This is an Open Server restriction, not an access service limitation.) The access service allows a use procedure statement (as long as the stored procedure does not return data) but not a transfer statement.
Parameter markers for dynamic commands are supported as follows:
In Sybase mode, parameter markers within the text can use Sybase conventions, such as @paramname. When necessary, the access service transforms parameters to the native syntax.
In Passthrough mode, parameter marker syntax is database-specific, so you must use question marks (?) as parameter markers.
The access service also supports long character and binary parameters. Client applications must describe such properties as datatype CS_LONGCHAR or CS_LONGBINARY.
Consider the following restriction: The AS/400 limits the SQL statement for any request (such as a prepare) to a total of 32,704 bytes. The maximum size of both data and null indicators in a row is 32,740 bytes.
For general information about dynamic SQL and dynamic commands, see the Sybase Open Client Embedded SQL Programmer's Guide and the IBM AS/400 DB2/400 SQL Reference manual.
The access service does not support Embedded SQL (ESQL). However, OmniConnect (ASE/CIS) does support Embedded SQL. You may configure OmniConnect (ASE/CIS) in front of DirectConnect to support this functionality.
When you write an application using dynamic commands, consider the following:
The SQL statement being prepared must not be a select statement. Although Open Server allows a select statement to be prepared and a cursor to be opened on the prepared statement, the access service does not support this capability.
Use AS/400 syntax for the statement you are preparing when you are in Passthrough transformation mode.
Following is an example of a code fragment that prepares and executes an insert statement.
/*
** This sample code fragment illustrates the CT-Library calls
** used to prepare and execute a SQL statement. Error handling
** and other details are omitted for the sake of brevity.
** NOTE: Ellipses in the following represent code that you must supply.
*/
/*
** Prepare the statement. The statement we will prepare is:
** INSERT INTO TEST VALUES (?, ?, ?, ?).
** We will name the dynamic statement DYN1.
*/
retcode = ct_dynamic( cmd, CS_PREPARE, "DYN1", CS_NULLTERM,
"INSERT INTO TEST VALUES (?, ?, ?, ?)", CS_NULLTERM );
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );
/*
** Now execute the prepared statement with a set of parameter
** values. Allocate a CS_DATAFMT structure for each parameter.
*/
dfmt = malloc( 4 * sizeof(struct CS_DATAFMT) );
/*
** Fill in the structure and set the datalength, null indicator,
** and data value for each parameter.
*/
dfmt[0].datatype = CS_CHAR_TYPE;
dfmt[0].status = CS_INPUTVAL;
dfmt[0].maxlength = strlen( "col1val" );
dataptr[0] = "col1val";
datalen[0] = strlen( "col1val" );
nullind[0] = 0;
...
/*
** Execute the statement.
*/
retcode = ct_dynamic( cmd, CS_EXECUTE, "DYN1", CS_NULLTERM,
NULL, CS_UNUSED );
/*
** Describe and send the parameters.
*/
for (i=0; i<4; i++)
{
retcode = ct_param( cmd, &dfmt[i], dataptr[0], datalen[i],
nullind[i] );
}
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );For more information about SQL processing, see the IBM AS/400 DB2/400 SQL Reference manual.
Special error handling is not required.
Cursor commandsCursor commands, which are available only with Open Client CT-Library System 10 or greater, give an application the power to retrieve and change data in a flexible way. For example, you can use cursor commands to process multiple result sets that are simultaneously available to the application, instead of one at a time using the language command.
Cursor commands require you to specify a SQL select statement that goes through SQL transformation, as follows:
The client application declares and opens the cursor command through the CT-Library routine ct_cursor.
When the cursor opens, a set of rows on the target is qualified. At that point, the select, update, or delete statement can initiate the following SQL statements to operate on specific rows in the set:
fetch
update
delete
The access service does not accept language event-based cursor commands. You must use ct_cursor commands.
DB-Library also supports a form of cursors but somewhat differently. The following table shows the differences between DB-Library cursors and CT-Library cursors.
Characteristic | DB-Library-based Cursor | CT-Library-based Cursor |
Relationship to Adaptive Server cursor | Called an "emulated" or " client-side" cursor, does not correspond to an Adaptive Server cursor. | Called a "native" or "server-side" cursor, corresponds to an actual Adaptive Server cursor. |
Cursor row position | Defined by the client. | Defined by the server. |
Fetch capability | Can fetch backward . | Can fetch forward only. |
Memory requirements | More memory is required if querying large row sizes, unless a smaller number of rows in the fetch buffer is specified. | No additional memory is required, regardless of the row sizes. |
Access to Open Server application | Not available, unless required DB-Library stored procedures are installed. | Any System 10 or greater Open Server application is coded to support cursors. |
The access service supports CT-Library-based cursors by mapping the corresponding Open Server commands to AS/400 capabilities. Because AS/400 cursors do not provide all the capabilities of Open Server cursors, some limitations apply and are included in the following section.
For more information about AS/400 capabilities related to commit and rollback statements, see the IBM AS/400 DB2/400 SQL Reference manual.
Following is a description of cursor capabilities that DirectConnect supports, as well as the limitations that apply:
Only CT-Library client applications built on Open Client System 10 or greater can use cursor capabilities.
The access service supports a maximum of 50 simultaneously declared cursors per client connection.
In both Sybase and Passthrough modes, the access service does not send text with the cursor delete command; instead, it formulates the DBMS-specific equivalent of:
delete from <tablename> where current of
cursor <cursorname>where <cursorname> is based on the current Open Server cursor ID.
The access service does not support the from clause in an update statement.
When an Open Client application deletes the current cursor row, it does not supply a SQL statement, so no translation occurs. The access service generates the commands that delete the current cursor row.
When the client application issues a rollback, the AS/400 closes and frees all cursors. The client application must also close and free its cursors at this point, so that CT-Library synchronizes with the actual state of the cursors.
The client application can select the behavior of cursors after a commit; in other words, the cursor is either closed, or it is not closed and retains its position:
If the client executes a set CloseOnEndtran on command before any cursors were declared, then all cursors are closed--but not freed--after the commit. The client application closes its cursors so that CT-Library synchronizes with the actual state of the cursors. If the application does not execute the close on endtran statement, on is the default, so the statement executes anyway.
If the client executes a set CloseOnEndtran off command before any cursors are declared, then all cursors remain open and retain their current position after a commit occurs. This corresponds to the with hold clause in the AS/400.
A parameter marker can vary depending on the transformation mode:
In Passthrough mode, the marker is a question mark (?).
In Sybase mode, the marker is an "at" (@) sign.
The access service supports long character and binary parameters. Client applications must describe such parameters as CS_LONGCHAR or CS_LONGBINARY.
The AS/400 limits the SQL statement for any request (such as a cursor declare or update) to 32,767 bytes. The access service does not allow you to declare a cursor in a dynamically prepared select statement.
When you write an application using read-only cursors, consider the following:
The access service supports forward positioning (next) only. It does not support any other fetch option, such as previous, first, last, absolute, or relative.
You must specify the cursor as read only when declaring it.
Read-only cursors have two fetch modes, called blocking and nonblocking:
Blocking occurs when the for fetch only clause is specified in the cursor's select statement. DirectConnect then uses the fetch count number that you set to buffer the number of rows. You can see this number in the @@RowCount global variable.
Nonblocking does not use the for fetch only clause. Therefore, DirectConnect ignores the fetch count and fetches only one row at a time.
The following code fragment shows the sequence of CT-Library calls used to declare, open, fetch, close, and free a read-only cursor.
/*
** This sample code fragment illustrates the CT-Library calls that are
** used to declare, open, fetch, close and deallocate a read-only
** cursor. Error handling and other details are omitted for the sake of
** brevity.
** NOTE: Ellipses in the following represent code that you must supply.
*/
/*
** Initialize CT-Lib, establish a connection to DirectConnect.
*/
...
/*
** Set cursor behavior on COMMIT so that cursors will maintain
** their position and not be closed.
*/
retcode = ct_cmd_alloc( connection, &cmd );
strcpy( lang, "SET CLOSEONENDTRAN OFF" );
retcode = ct_command( cmd, CS_LANG_CMD, lang, CS_NULLTERM,
CS_UNUSED );
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );
/*
** We are going to declare the cursor on the statement:
** SELECT * FROM AUTHORS WHERE STATE = ?
**
** Allocate a CS_DATAFORMAT structure for the parameter and fill
** in the relevant fields. In this case, the parameter is char(2).
*/
dfmt = malloc( sizeof(CS_DATAFMT) );
memset( dfmt, 0, sizeof(CS_DATAFMT) );
dfmt->status = CS_INPUTVALUE;
dfmt->format = CS_UNUSED;
dfmt->datatype = CS_CHAR_TYPE;
/*
** Declare the cursor, using cursor name "CURS1".
*/
retcode = ct_cursor( cmd, CS_CURSOR_DECLARE, "CURS1", CS_NULLTERM,
"SELECT * FROM AUTHORS WHERE STATE = ?", CS_NULLTERM,
CS_READ_ONLY );
/*
** Describe the parameter.
*/
retcode = ct_param( cmd, &dfmt, NULL, CS_UNUSED, 0 );
/*
** Set the fetch count to 20.
*/
retcode = ct_cursor( cmd, CS_CURSOR_ROWS, NULL, CS_UNUSED,
NULL, CS_UNUSED, 20 );
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );
/*
** Now open the cursor with host variable value = 'CA'
*/
strcpy( parmval, "CA" );
datlen = 2;
nullind = 0;
retcode = ct_cursor( cmd, CS_CURSOR_OPEN, NULL, CS_UNUSED, NULL,
CS_UNUSED, CS_UNUSED );
/*
** Send the parameter.
*/
retcode = ct_param( cmd, &dfmt, parmval, datlen, nullind );
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );
/*
** Describe and bind the result set.
** Find out how many columns there are in this result set.
** Make sure that there is at least 1 column.
*/
retcode = ct_res_info( cmd, CS_NUMDATA, &ncols, CS_UNUSED, NULL );
/*
** Allocate memory for each column's CS_DATAFMT, a data pointer,
** data length, and null indicator.
*/
...
/*
** Loop through the columns getting a description of each one and
** binding each one to a program variable.
*/
for (i = 0; i < ncols; i++)
{
/*
** Get the column description. ct_describe() fills the
** datafmt parameter with a description of the column.
*/
retcode = ct_describe( cmd, (i + 1), &(coldata[i].dfmt) );
/*
** Update the datafmt structure to indicate the form in
** which we want to get the data. Set the datatype,
** format, maximum length, etc.
*/
...
/*
** Allocate memory for the actual column data.
*/
...
/*
** Now bind.
*/
retcode = ct_bind( cmd, (i + 1), &(coldata[i].dfmt ),
coldata[i].data, &( coldata[i].datlen ),&( coldata
[i].nullind) );
}
/*
** Fetch 1 row.
*/
retcode = ct_fetch( cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED,
&rows_read );
/*
** Check the results of the fetch.
*/
if (retcode == CS_ROW_FAIL)
{
/*
** Fetch failed.
*/
...
}
else if (retcode != CS_SUCCEED)
{
if (retcode == CS_END_DATA)
{
/*
** End of data has been reached.
*/
retcode = handleresults( cmd );
goto CLOSECURS;
}
else
{
/*
** Fetch failed.
*/
...
}
}
/*
** We have a row of data.
*/
...
CLOSECURS:
/*
** Close and free the cursor.
*/
retcode = ct_cursor( cmd, CS_CURSOR_CLOSE, NULL, CS_UNUSED, NULL,
CS_UNUSED, CS_DEALLOC );
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );When you write an application using updatable cursors, consider the following:
The access service supports only forward positioning with the fetch next option and with a fetch count of 1. It does not support any other fetch option (previous, absolute, relative). Therefore, it deletes or updates only the most recently fetched row.
The SQL statement used for the cursor declaration must contain a for update of <column_list> clause.
You must specify the cursor as for_update when you declare it.
Use AS/400 syntax for the statement being declared, which is determined by the transformation mode. When in Sybase mode, the access service performs translation on the statement. Parameter markers can represent values that the application describes and sends to the access service. The access service then uses the parameter descriptions and values to create the final form of the update statement. Parameter markers are as follows:
In Sybase mode, the marker is the at (@) sign.
In Passthrough mode, the marker is a question mark (?).
In both modes, the access service appends the where current of <cursor_name> clause to the update statement.
Following is a code fragment that shows the sequence of CT-Library calls used to delete and update a row on an updatable cursor.
/*
** This sample code fragment illustrates the CT-Library calls
** used to update and delete a row on an updatable cursor.
**
** Error handling and other details are omitted for the sake of brevity.
** NOTE: Ellipses in the following represent code that you must supply.
*/
/*
** See the previous example for the cursor declare, open and
** fetch. The differences are:
** 1. The statement to declare is
** SELECT * FROM AUTHORS WHERE STATE = ? FOR UPDATE OF PHONE
** 2. The declare must specify CS_FOR_UPDATE instead of
CS_READ_ONLY.
** 3. The setting of the fetch count is eliminated.
*/
/*
** Assume that you have fetched rows (one at a time) until you see
** a row you want to delete. This code deletes the most recently
** fetched row.
*/
retcode = ct_cursor( cmd, CS_CURSOR_DELETE, "AUTHORS",
CS_NULLTERM, NULL, CS_UNUSED, CS_UNUSED );
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );
/*
** Fetch more rows until we see a row that we want to update.
*/
...
/*
** Update the most recently-fetched row. For this example, we
** will update the PHONE column in the AUTHORS table, and we
** will use a parameter in the update statement for the value of
** PHONE. Fill in the CS_DATAFMT structure for the parameter and
** the parmval, atlen, and nullind values.
*/
dfmt.status = CS_INPUTVALUE;
dfmt.datatype = CS_CHAR_TYPE;
strcpy( parmval, "303-443-2706" );
datlen = strlen( "303-443-2706" );
nullind = 0;
/*
** Send the cursor update.
*/
retcode = ct_cursor( cmd, CS_CURSOR_UPDATE, "AUTHORS",
CS_NULLTERM,"UPDATE AUTHORS SET PHONE = ?", CS_NULLTERM,
CS_UNUSED );
/*
** Send the parameter description and value.
*/
retcode = ct_param( cmd, &dfmt, parmval, datlen, nullind );
/*
** Send the batch and check results.
*/
retcode = ct_send( cmd );
retcode = handleresults( cmd );Error handling
for cursor declare and open commandsIn general, the client application handles errors the same as any other CT-Library application. However, handling errors from a cursor declare or cursor open requires special consideration, as described next.
If a cursor declare fails because, for example, a table does not exist or the user does not have sufficient privilege to access a table, the application should issue a ct_cancel and drop the command. This cleans up the necessary structures in Open Client/Server and synchronizes the client with both the access service and the AS/400.
If a cursor open fails because, for example, the number of parameters described in the open does not match the number of parameter markers in the declare, then the client application should close and free the cursor, and then drop the command. The client application should not issue a ct_cancel in this case.
Warning!
If a cursor declare contains parameter markers but the cursor open does not describe any parameters, DirectConnect goes into an error state. As a result, the client's connection to the database is dropped when the next command is executed. To recover, the client application must close the connection to the access service and open a new one.
For more information about error handling, see the Sybase Open Client Client-Library/C Reference Manual.
|
|