Skip to main content

ODBC programming using Apache Derby

Rajesh Kartha (kartha@us.ibm.com), Software Engineer, IBM Cloudscape Group
Rajesh Kartha works as a software engineer in the Cloudscape group of IBM Data Management.

Summary:  This article introduces you to ODBC programming with Apache Derby or IBM® Cloudscape™ as the backend data source. You'll learn how ODBC applications differ from embedded SQL applications, how to set up Apache Derby as an ODBC data source, and how an ODBC application is structured. In addition, the author covers error handling and includes an example.

Date:  23 Sep 2004
Level:  Introductory
Activity:  2415 views

Embedded SQL vs. ODBC

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.


DB2 Runtime Client

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:

  1. 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'
  2. 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.
  3. Catalog the Derby Network Server with the DB2 Runtime client using the CLP
    • db2 catalog tcpip node DERBYNET remote localhost server 1527
  4. Catalog the database with the DB2 Runtime client using the Command Line Processor (CLP).
    • db2 catalog db SAMPLE at node DERBYNET authentication server
  5. Verify connection to the database using DB2 CLP:
    • db2 connect to SAMPLE user abc using abc
  6. 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
ODBC and CLI 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:

  1. Initialization
  2. Transaction processing
  3. Termination

1) Initialization

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
Initialization in ODBC

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:

Table 1. Handle types in ODBC

Handle typePurpose
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

Environment handle

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);

Connection handle

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 callUsage
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.

Statement handle

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.

Descriptor handle

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.

Attributes

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.

Environment attributes

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

Connection attributes

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

Statement attributes

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().

Table 5. Statement attributes

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.

2) Transaction processing

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
Transaction Processing

Executing SQL statements

There are two ways to execute SQL Statements: Prepare and Execute, and Execute Immediate.

Prepare and Execute

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);

Execute Immediate

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);

Process results

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.

Transaction management

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);

3) Termination

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.

Return codes

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:

Table 6. ODBC Return Codes

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

SQL states

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.


An example

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);
}


Resources

About the author

Rajesh Kartha works as a software engineer in the Cloudscape group of IBM Data Management.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management, Open source
ArticleID=16293
ArticleTitle=ODBC programming using Apache Derby
publish-date=09232004
author1-email=kartha@us.ibm.com
author1-email-cc=clines@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers