Open Database Connectivity (ODBC) is one of the most popular ways today to code a database application. Let's begin our examination of ODBC programming for Apache Derby by comparing ODBC applications with embedded SQL applications.
Embedded SQL applications contain SQL statements embedded into the source code, which is written using a high-level language, a common example being C. ODBC applications, in contrast, use a standard set of APIs to communicate with the database. The SQL statements are sent using these standard API methods to the database manager for processing.
Some of the main differences between embedded SQL and ODBC applications are:
- Embedded SQL applications tend to lack interoperatiliby. Applications might have to modified to work with other RDBMS.
- Cursors are automatically generated in ODBC applications, if needed, whenever the SQLExecute()/SQLExecDirect() functions are executed. In contrast, embedded SQL requires a DECLARE CURSOR statement.
- ODBC applications do not require the explicit declaration and use of host variables.
- ODBC functions manage environment, connection, and SQL information using handles; hence there is no need for complex product-specific data structures (such as the SQLCA and SQLDA, used for DB2 embedded SQL apps).
- Typically in a manual transaction, ODBC/CLI applications rely on the standard SQLEndTran() function to terminate active transactions. Embedded SQL applications depend on the way SQL statements are prepared dynamically, which may differ between database vendors.
Since ODBC applications pass all the SQL statements to the data source for execution, applications portability is easier to achieve. This could be an issue with embedded SQL applications.
The ODBC support for Apache Derby or IBM Cloudscape is made possible with the DB2 Runtime Client. Distributed relation database architecture (DRDA) is the common protocol used for communication between the client and the server, and is provided by the Runtime Client. Please refer to the instructions available in the developerWorks article Cloudscape and ODBC for more installation instructions and information on the platform availablity. Using Cloudscape and ODBC is currently in beta status.
Set up an Apache Derby database as an ODBC data source
Follow these steps to create an ODBC data source for an Apache Derby database:
- Start the Apache Derby Network Server. Please refer to the Derby documentation for infomation related to setting up the CLASSPATH and the various options for starting the Derby Network Server.
-
For example:
java org.apache.derby.drda.NetworkServerControl start
The command above starts the Derby Network Server with the default options: host is 'localhost', port is '1527' - Connect to the Derby Network Server using the 'ij' tool to create the database
-
For example:
ij> connect 'jdbc:derby:net://localhost:1527/SAMPLE;create=true:user=app;password=app;';
The command above connects to the Derby Network Server and creates a database called SAMPLE. - Catalog the Derby Network Server with the DB2 Runtime client using the CLP
db2 catalog tcpip node DERBYNET remote localhost server 1527- Catalog the database with the DB2 Runtime client using the Command Line Processor (CLP).
db2 catalog db SAMPLE at node DERBYNET authentication server- Verify connection to the database using DB2 CLP:
db2 connect to SAMPLE user abc using abc- Create the ODBC data source:
- On Windows® 2000, create the ODBC data source by clicking on Start -> Settings -> Control Panel -> Administrative Tools -> Data Sources -> System DSN.
- Another approach for Windows is to use DB2 CLP to perform the ODBC data source creation.
db2 catalog system odbc data source SAMPLE- To list the current set of ODBC data sources:
db2 list system odbc data sources- For Linux, it will depend on the type of ODBC Driver Manager installed; please refer its documentation on setting up the ODBC data source.
Structure of an ODBC application
ODBC and the DB2 Call Level Interface (CLI) have many of the method calls in common; however some extra driver manager related method calls are available in ODBC. For more information on ODBC, please refer to http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcabout_this_manual.asp.
The main difference between an ODBC application and a CLI application is in the way the database driver is loaded. In an ODBC environment, the Driver Manager provides the interface to the application. It also dynamically loads the necessary driver for the database server that the application connects to. Each ODBC function call the application makes to the ODBC DriverManager is forwarded to the appropriate data source driver for processing. In a CLI environment, the application does the loading by itself and hence is bound to a single data source driver. Figure 1 illustrates this difference.
Figure 1. Difference between the CLI and ODBC environments

For more information on the DB2 CLI, please refer to the DB2 CLI Guide and Reference, Volume 1 and Volume 2.
An ODBC application mainly consists of the following three parts:
- Initialization
- Transaction processing
- Termination
All the resources needed to create connections, execute SQL and perform transactions are allocated in this step, shown in Figure 2.
Figure 2. Initialization in an ODBC environment

In ODBC, all these resources are simple pointers (data storage areas) to data objects controlled by the ODBC Driver Manager or the DB2 CLI, and are called 'handles'. The are four main types of handles:
| Handle type | Purpose |
|---|---|
| Environment | Global storage that has ODBC-specific information |
| Connection | Storage for data source connection |
| Statement | Storage for a single SQL statement |
| Descriptor |
Storage for metadata, which has information about application variables used in binding to parameter markers or columns of a result set |
Allocating an environment handle is usually the first step in an ODBC application. One environment handle must be present to allocate other types of handles in an application. An environment handle is allocated by calling the SQLAllocHandle() function with the SQL_HANDLE_ENV option specified.
SQLHANDLE EnvHandle = 0; SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &EnvHandle); |
To make a connection to a data source for an ODBC application, you first create first a connection handle. A connection handle is allocated by calling the SQLAllocHandle() function with the SQL_HANDLE_DBC option specified.
SQLHANDLE ConnHandle = 0; SQLAllocHandle(SQL_HANDLE_DBC, EnvHandle, &ConnHandle); |
To establish a connection to a data source, use one of the following methods:
Table 2. Methods to connect to a data source
| Function call | Usage |
|---|---|
| SQLConnect() |
This is the simplest of the connect functions and only needs a data source name, user, and password. All the other required information needs to be in the db2cli.ini or the ODBC.INI file. |
| SQLDriverConnect() |
Using this function, an application can send connection information to a data source driver using a connection string with key-value pairs separted by semicolons. No information needs to be stored in the db2cli.ini or the ODBC.INI file. For example: DSN=SAMPLE;UID=app;PWD=app; |
| SQLBrowseConnect() |
This function is similar to the SQLDriverConnect() function, and uses a connection string to send connection information to a driver. But with the SQLBrowseConnect()function one can be used to construct a complete connection string at application run time, using the connection information given by the user, through the application's dialog box, for example. |
The statement handle is the most important, as it is used to process
the SQL statements in the application. For executing each SQL statement
there needs to be a statement handle associated with it, hence an
application can have multiple statement handles. A statement handle
is allocated by calling the SQLAllocHandle() function with the
SQL_HANDLE_STMT option specified.
SQLHANDLE StmtHandle = 0; SQLAllocHandle(SQL_HANDLE_STMT, ConHandle, &StmtHandle); |
A statement handle can be used to:
- Bind application variables to parameter markers used in a prepared statement
- Prepare and submit a statement for execution
- Obtain metadata about any result data set(s) produced
- Bind application variables to columns found in any result data set(s) produced
- Retrieve (fetch) data from any result data set(s) produced
- Obtain diagnostic information when a statement fails.
For each statement handle allocated, four descriptor handles are automatically allocated and associated with the handle as part of the allocation process. These descriptor handles remain associated with the corresponding statement handle until it is destroyed. The ODBC application can perform most of the operations using these implicitly defined descriptor handles.
One or more descriptor handles can be explicitly allocated by calling the SQLAllocHandle() function with the SQL_HANDLE_DESC option and a valid connection and statement handle.
ODBC applications can connect to various data sources and can use the SQLGetInfo(), SQLGetFunctions(), and SQLGetTypeInfo() functions to get more information about the underlying data source driver in use. In addition some of the 'driver attributes' can be altered to change the behaviour of the driver. The following are the types of driver attributes that are available:
- Environment attributes
- Connection attributes
- SQL statement attributes
Please note the attributes mentioned below under each type of driver attributes are the most common ones found in ODBC applications. For a complete list and description of the attributes for each type, please refer to to the DB2 CLI Guide and Reference, Volume 2. At the time of writing this paper, the list of ODBC attributes supported by Apache Derby have not been finalized. Once they are finalized a complete list will be made available on developerWorks.
These attributes affect the way ODBC functions behave under a specific environment. The environment attributes can only be changed as long as no connection handles have been allocated using the environment handle, once the connection handles are allocated, these attributes can only retrieved and not changed. The functions used to access these attributes are SQLSetEnvAttr()/SQLGetEnvAttr().
Table 3. Environment attributes
| SQL_ATTR_ODBCh_VERSION | Set the ODBC version to be used |
|---|---|
| SQL_ATTR_OUTPUT_NTS | Whether or not the driver needs to append the null terminator to string data |
These attributes affect the behaviour of the connections to data sources. Depending on each of these connection attributes, they can be set a different times: before/after allocating the connection handle, or before/after establishing a connection to the data source. The functions used to access these attributes are SQLSetConnectAttr()/SQLGetConnectAttr().
Table 4. Connection attributes
| SQL_ATTR_AUhTOCOMMIT | Automatic commit on/off |
|---|---|
| SQL_ATTR_MAXCONN | Max concurrent connections that can be open for the application |
| SQL_ATTR_TXN_ISOLATION |
Set isolation level for the current connection SQL_TXN_SERIALIZABLE for repeatable read, SQL_TXN_REPEATABLE_READ for read stability, SQL_TXN_READ_COMMITTED for cursor stability, and SQL_TXN_READ_UNCOMMITTED for uncommitted read |
These attributes affect the behavior of the statement level functions. Again depending on each of these statement attributes, they can be set at different times: before/after executing the SQL statement or at any time. The functions used to access these attributes are SQLSetStmtAttr()/SQLGetStmtAttr().
| SQL_ATTR_CURSOR_SENhSITIVITY | Make changes made by other cursors visible or not |
|---|---|
| SQL_ATTR_CURSOR_TYPE | Type of cursor (forward-only, static, keyset-driven, or dynamic). |
| SQL_ATTR_CONCURRENCY |
Specifies the cursor concurrency level to use (read-only, low-level locking, or value-comparison locking). |
| SQL_ATTR_RETRIEVE_DATA |
Specifies whether or not the SQLFetch() and SQLFetchScroll() functions are to automatically retrieve data after they position the cursor. |
After the initialization of all the required handles, this section in the ODBC application is where all transaction processing occurs, where the SQL statements are sent to the data sources, and the results are processed. As you can see in Figure 3, the main steps involved here are:
- Allocate one or more statement handles (covered in the section above on the structure of an ODBC application).
- Execute SQL statements
- Process results
- Manage transactions
Figure 3. Transaction processing in an ODBC application

There are two ways to execute SQL Statements: Prepare and Execute, and Execute Immediate.
This is used when statements have to be executed repeatedly. The statement can have multiple parameter markers ('?'). The ODBC functions SQLPrepare() and SQLExecute() are used to process the SQL statements.
SQLPrepare() function prepares the statement:
strcpy((char *) SQLStmt, "INSERT INTO EMPLOYEE VALUES (?,?,?,?, CURRENT DATE)" ); RetCode = SQLPrepare(StmtHandle, SQLStmt, SQL_NTS); |
The '?' indicates the position where values of application variables can be substituted. Application variables are bound and associated with the parameter markers using the SQLBindParameter() function. Once an application variable is bound to a parameter marker, the association remains until it is overridden or until the corresponding statement handle is freed. Data from the bound variable is retrieved only during the execution of the SQL statement.
SQLExecute() function executes the statement:
RetCode = SQLBindParameter(StmtHandle, 1,SQL_PARAM_INPUT, SQL_C_LONG, \ SQL_INTEGER,sizeof(EmpId), 0, &EmpId ,sizeof(EmpId), NULL); RetCode = SQLExecute(StmtHandle); |
This is typically used when statements have to be executed only once. The ODBC function SQLExecDirect() is used to process the SQL statements.
SQLExecDirect() method performs the execution:
strcpy((char *) SQLStmt, "Create table Employee(empno int, firstname char(15), \ lastname char(25), jobtype varchar(10), date date) " ); RetCode = SQLExecDirect(StmtHandle, SQLStmt, SQL_NTS); |
The results generated from the SQL statements have to be retrieved and processed. They are stored in storage areas that you can reference using the connection and statement handles used during execution. For statements that do not generate any results, you can use the function return code to verify a successful execution of the statement. To retrieve rows from the result set generated by the SQL statements, the following steps are involved:
- For executed SQL statements, obtain the structure of the result set in terms of the columns types, data lengths number of columns, and so on, by using the SQLNumResultCols(), SQLDescribeCol() and SQLColAttributes() functions.
- Bind application variables to the columns from the result set using the SQLBindCol() function. These variables are used as output arguments and the values are retrieved and assigned to them during the fetch operation.
SQLBindCol(StmtHandle, 1, SQL_C_CHAR, (SQLPOINTER)EmpNo, sizeof(EmpNo), NULL); |
- Retrieve the data stored in a result data set using any combination of the SQLFetch() and SQLGetData() methods. You can fetch values from the result set into the bound application variables by calling the SQLFetch() repeatedly until there is no data available. If no variables were bound, the values can be obtained using the SQLGetData() function.
A transaction is a unit of work and consists of a set of SQL queries which are either committed or rolled back on completion. There are two commit modes for transaction processing:
- auto commit
- manual commit
By default, for ODBC applications the commit mode is 'auto commit', where each individual SQL statement is treated as a complete transaction, and is automatically committed after its successful execution. For 'manual commit', the auto commit is turned off using the following statement:
SQLSetConnectAttr(ConHandle, SQL_ATTR_AUTOCOMMIT,SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER); |
With manual commit mode, the transactions starts when the first statement is executed, and are explicitly ended when the SQLEndTran() function is called. To commit a transaction, the SQLEndTran() function with the SQL_COMMIT option is called as follows:
SQLEndTran(SQL_HANDLE_DBC, ConHandle,SQL_COMMIT); |
To roll back a transaction, the SQLEndTran() function with the SQL_ROLLBACK option is called:
SQLEndTran(SQL_HANDLE_DBC, ConHandle,SQL_ROLLBACK); |
Once all SQL statements in the application are executed and there are none left, the storage area/memory allocated during the transaction has to be freed up. You do this using the SQLFreeHandle() function, during which all the parameter and column bound application variable are unbound, all open cursors are closed, and the memory associated with the statement and description handles is released.
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle); |
Similarly, if the application is about to terminate, all the connections needs to be terminated and all the resources allocated for the connection and environment handles have to be released. To disconnect an established connection, the SQLDisconnect() function is used as follows:
SQLDisconnect(ConHandle); |
All the storage area allocated for the connection and environment handles is freed using the SQLFreeHandle() function with the SQL_HANDLE_DBC and the SQL_HANDLE_ENV options respectively.
SQLFreeHandle(SQL_HANDLE_DBC, ConHandle); //free the connection handle SQLFreeHandle( SQL_HANDLE_ENV, EnvHandle); //free the environment handle |
Error handling and diagnostics
Return codes and SQLSTATE messages aid in diagnosing error conditions during the execution of an ODBC application.
For each ODBC function invoked, a special value known as a return code is returned to the calling application. It indicates whether the function executed as expected. Table 6 lists the possible return codes that can be returned by any ODBC function:
| SQL_SUCCESS_WhITH_INFO | Function completed successfully with warning/non-fatal error |
|---|---|
| SQL_NO_DATA or SQL_NO_DATA_FOUND | Function completed successfully without any data |
| SQL_NEED_DATA | Function failed, missing data (connection information or parameter data) |
| SQL_INVALID_HANDLE | Function failed with invalid handle |
| SQL_STILL_EXECUTING | An asynchronous function still in progress |
| SQL_ERROR | Function failed |
| SQL_SUCCESS | Function completed successfully |
SQLSTATE messages give specific information about what caused an error or warning condition, and are very useful for resolving errors. Almost all of the database products use SQLSTATEs to provide such information. SQLSTATEs are alphanumeric strings of five characters (bytes) in length and have the format 'ccSSS', where 'cc' indicates the error message class, as follows:
| 01 | Warning |
|---|---|
| HY | DB2 Call Level Interface error |
| IM | ODBC Driver Manager error |
'SSS' indicates the error message subclass.
Please note that the Apache Derby SQLSTATEs may be different from the DB2 SQLSTATEs.
Getting diagnostic information
You can obtain the SQLSTATE, diagnostic messages, and native error codes information by calling the SQLGetDiagRec() function, the SQLGetDiagField() function, or both.
These functions take environment, connection, statement, or descriptor handles as input and return the diagnostic information related to the last function call made using that handle. For multiple diagnostic records that get generated, the SQLGetDiagField() function call with record number 0 and the SQL_DIAG_NUMBER option return the total number of records. For each diagnostic record, a SQLGetDiagRec() function call retrieves SQLSTATE values, diagnostic messages, and native error codes.
The sections above described the various modules within an ODBC application. Listing 1 shows a simple ODBC application, written in the C programming language for Windows, that makes a connection to an ODBC data source called 'SAMPLE', inserts a row into the EMPLOYEE table, and retrieves the employee id and last names for all employees that have the job title as 'ENGINEER'.
The method print_error() aids in diagnosing error conditions during the execution and shows the usage of the SQLGetDiagRec() and the SQLGetDiagField() functions.
The sample ODBC program must be compiled by an appropriate C programming language compiler, such as Visual C++.
Listing 1. Example ODBC Application
#include <stdio.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
void print_error( SQLSMALLINT htype, /* A handle type identifier */
SQLHANDLE hndl, /* A handle */
SQLRETURN frc, /* Return code to be included with error msg */
int line, /* Used for output message, indcate where */
char * file /* the error was reported from */
) {
SQLCHAR buffer[SQL_MAX_MESSAGE_LENGTH + 1] ;
SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1] ;
SQLINTEGER sqlcode ;
SQLSMALLINT length, i ;
SQLINTEGER NumRecords;
printf( ">--- ERROR -- RC = %d Reported from %s, line %d ------------\n",
frc,
file,
line
) ;
SQLGetDiagField(htype, hndl, 0,SQL_DIAG_NUMBER, &NumRecords, SQL_IS_INTEGER,NULL);
printf("Total Number of diagnostic records: %d\n",NumRecords);
i = 1 ;
while ( SQLGetDiagRec( htype,
hndl,
i,
sqlstate,
&sqlcode,
buffer,
SQL_MAX_MESSAGE_LENGTH + 1,
&length
) == SQL_SUCCESS ) {
printf( " SQLSTATE: %s\n", sqlstate ) ;
printf( "Native Error Code: %ld\n", sqlcode ) ;
printf( "%s \n", buffer ) ;
i++ ;
}
printf( ">--------------------------------------------------\n" ) ;
}
int main()
{
// Declare The Local Memory Variables
SQLHANDLE EnvHandle = 0;
SQLHANDLE ConHandle = 0;
SQLHANDLE StmtHandle = 0;
SQLRETURN RetCode = SQL_SUCCESS;
SQLCHAR SQLStmt[255];
SQLCHAR JobType[10];
SQLCHAR EmpNo[10];
SQLCHAR LastName[25];
SQLCHAR FirstName[15];
int EmpId=1002;
/**
* INITIALIZATION
**/
// Allocate An Environment Handle
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE,&EnvHandle);
// Set The ODBC Application Version To 3.x
printf("Setting the ODBC version... \n");
if (EnvHandle != 0)
SQLSetEnvAttr(EnvHandle, SQL_ATTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3,
SQL_IS_UINTEGER);
// Allocate A Connection Handle
printf("Creating Connection handle... \n");
if (EnvHandle != 0)
SQLAllocHandle(SQL_HANDLE_DBC, EnvHandle,&ConHandle);
// Connect To The Appropriate Data Source
if (ConHandle != 0)
{
RetCode = SQLConnect(ConHandle, (SQLCHAR *) "SAMPLE",SQL_NTS,
(SQLCHAR *) "app",SQL_NTS,
(SQLCHAR *) "app", SQL_NTS);
printf("Got the connection... \n");
}
/*
* TRANSACTION PROCESSING
**/
// Allocate An SQL Statement Handle
if (ConHandle != 0 && RetCode == SQL_SUCCESS)
SQLAllocHandle(SQL_HANDLE_STMT, ConHandle,&StmtHandle);
else{
printf("Error getting connection:\n");
print_error((SQLSMALLINT)SQL_HANDLE_DBC,ConHandle,
RetCode,__LINE__,__FILE__ );
return(0);
}
// Define A SELECT SQL Statement That Uses A Parameter
strcpy((char *) SQLStmt, "INSERT INTO EMPLOYEE VALUES (?,?,?,?, \
CURRENT DATE)");
RetCode = SQLPrepare(StmtHandle, SQLStmt, SQL_NTS);
if(RetCode!=SQL_SUCCESS)
{
printf("Error preparing the insert statement:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 1,SQL_PARAM_INPUT, SQL_C_LONG,
SQL_INTEGER,sizeof(EmpId),
0, &EmpId ,sizeof(EmpId), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the first param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 2,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(FirstName),
0, FirstName,sizeof(FirstName), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the second param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 3,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(LastName),
0, LastName,sizeof(LastName), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the third param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 4,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(JobType),
0, JobType,sizeof(JobType), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the fourth param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
strcpy((char *) FirstName, "Robert");
strcpy((char *) LastName, "Evans");
strcpy((char *) JobType, "ENGINEER");
RetCode = SQLExecute(StmtHandle);
if (RetCode == SQL_SUCCESS)
{
printf("Successfully executed the insert statement...\n");
}
else
{
printf("Error executing insert statement:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// A SELECT Statement
strcpy((char *) SQLStmt, "SELECT EMPNO, LASTNAME FROM ");
strcat((char *) SQLStmt, "EMPLOYEE WHERE JOBTYPE = ?");
// Prepare The SQL Statement
RetCode = SQLPrepare(StmtHandle, SQLStmt, SQL_NTS);
if(RetCode!=SQL_SUCCESS)
{
printf("Error preparing:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// Bind The Parameter Marker Used In The SQL Statement To
// An Application Variable
RetCode = SQLBindParameter(StmtHandle, 1,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(JobType),
0, JobType,sizeof(JobType), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// Populate The "Bound" Application Variable
strcpy((char *) JobType, "ENGINEER");
// Execute The SQL Statement
RetCode = SQLExecute(StmtHandle);
// If The SQL Statement Executed Successfully, Retrieve
// The Results
if (RetCode == SQL_SUCCESS)
{
printf("Successfully executed the select statement...\n");
// Bind The Columns In The Result Data Set Returned
// To Application Variables
SQLBindCol(StmtHandle, 1, SQL_C_CHAR, (SQLPOINTER)EmpNo,
sizeof(EmpNo), NULL);
SQLBindCol(StmtHandle, 2, SQL_C_CHAR, (SQLPOINTER)
LastName, sizeof(LastName), NULL);
// While There Are Records In The Result Data Set
// Produced, Retrieve And Display Them
while (RetCode != SQL_NO_DATA)
{
RetCode = SQLFetch(StmtHandle);
if (RetCode != SQL_NO_DATA)
printf("%-8s %s\n", EmpNo, LastName);
}
}else
{
printf("Error executing select statement:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// Commit The Transaction
RetCode = SQLEndTran(SQL_HANDLE_DBC, ConHandle,SQL_COMMIT);
/**
* TERMINATION
**/
// Free The SQL Statement Handle
if (StmtHandle != 0)
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
// Terminate The Data Source Connection
if (ConHandle != 0)
RetCode = SQLDisconnect(ConHandle);
// Free The Connection Handle
if (ConHandle != 0)
SQLFreeHandle(SQL_HANDLE_DBC, ConHandle);
// Free The Environment Handle
if (EnvHandle != 0)
SQLFreeHandle(SQL_HANDLE_ENV, EnvHandle);
// Return Control To The OS
return(0);
}
|
- Participate in the discussion forum.
- Documentation about using and developing applications with Apache Derby
- DB2 CLI Guide and Reference Volume 1
- DB2 CLI Guide and Reference Volume 2
- DB2 Admin Guide Implementation
- DB2 Admin API Reference
- Technical resources relating to DB2 can be found in the DB2 area at developerWorks
- unixODBC, an ODBC Driver Manager for Linux can be downloaded at http://www.unixodbc.org
Comments (Undergoing maintenance)





