Contents IndexChapter 36.  ODBC Programming ODBC programming for the Macintosh

User's Guide
   Part V. The SQL Anywhere Programming Interfaces
     Chapter 36. ODBC Programming
      ODBC C language programming

The ODBC interface is defined by a set of function calls, called the ODBC API (Application Programming Interface).

To write ODBC applications for SQL Anywhere, you need:

Top of page


Fundamentals

Although the interface to ODBC is through function calls, database access is specified using SQL statements passed as strings to ODBC functions.

The following fundamental objects are used for every ODBC program. Each object is referenced by a handle.

All access to these objects is through function calls; the application cannot directly access any information about the object from its handle. In the Windows and Windows NT environments, all the function calls are described in full detail in the ODBC API help file (ODBCAPI.HLP) provided with the Windows and Windows NT installations of SQL Anywhere.

Top of page


Compiling and linking an ODBC application

Every C source file using ODBC functions must include one of the following lines:

These files all include the main ODBC include file ODBC.H, which defines all of the functions, data types and constant definitions required to write an ODBC program. The file ODBC.H and the environment specific include files are installed in the H subdirectory of the SQL Anywhere installation directory (usually C:\SQLANY50).

Once your program has been compiled, you must link with the appropriate library file to have access to the ODBC functions:

Top of page


A first example

The following is a simple ODBC program:

     {
         HENV    env;
         HDBC    dbc;
         HSTMT    stmt;
         
         SQLAllocEnv( &env );
         SQLAllocConnect( env, &dbc );
         SQLConnect( dbc, "sademo", SQL_NTS,
                         "dba", SQL_NTS, "sql", SQL_NTS );
         SQLSetConnectOption( dbc, SQL_AUTOCOMMIT, FALSE );
         SQLAllocStmt( dbc, &stmt );
         
         /* Delete all the order items for order 2015 */
         SQLExecDirect( stmt,
             "delete from sales_order_items where id=2015",
              SQL_NTS );
         
         /* Use rollback to undo the delete */
         SQLTransact( env, dbc, SQL_ROLLBACK );
         SQLFreeStmt( stmt, SQL_DROP );
         SQLDisconnect( dbc );
         SQLFreeConnect( dbc );
         SQLFreeEnv( env );
     }

Notes

SQL_NTS Every string passed to ODBC has a corresponding length. If the length is unknown, you can pass SQL_NTS indicating that it is a 'Null Terminated String' whose end is marked by the null character ('\0').

SQLExecDirect Executes the SQL statement specified in the parameter.

SQLTransact Used to perform COMMIT and ROLLBACK statements marking the end of a transaction. You should not use SQLExecDirect to perform COMMIT or ROLLBACK.

Top of page


Error checking

The previous example did not check for any errors. Errors in ODBC are reported using the return value from each of the ODBC API function calls and the SQLError function.

Every ODBC API function returns a RETCODE which is one of the following status codes:

Every environment, connection and statement handle can have one or more errors or warnings associated with it. Each call to SQLError returns the information for one error and removes the information for that error. If you do not call SQLError to remove all errors, the errors are removed on the next function call which passes the same handle as a parameter.

The following program fragment uses SQLError and return codes:

     HDBC dbc;
     HSTMT stmt;
     RETCODE retcode;
     UCHAR errmsg[100];
     
     . . .
     
     retcode = SQLAllocStmt( dbc, &stmt );
     if( retcode == SQL_ERROR ) {
         SQLError( env, dbc, SQL_NULL_HSTMT, NULL, NULL,
                     errmsg, sizeof(errmsg), NULL );
     
         /* Assume that print_error is defined */
         print_error( "Failed SQLAllocStmt", errmsg );
         return;
     }
     
     /* Delete items for order 2015 */
     retcode = SQLExecDirect( stmt,
             "delete from sales_order_items
             where id=2015", SQL_NTS );
     if( retcode == SQL_ERROR ) {
         SQLError( env, dbc, stmt, NULL, NULL,
                     errmsg, sizeof(errmsg), NULL );
         /* Assume that print_error is defined */
         print_error( "Failed to delete items", errmsg );
         return;
     }
     . . .

Note that each call to SQLError passes in three handles for an environment, connection and statement. The first call uses SQL_NULL_HSTMT to get the error associated with a connection. Similarly, a call with both SQL_NULL_DBC and SQL_NULL_HSTMT will get any error associated with the environment handle.

The return value from SQLError may seem confusing. It returns SQL_SUCCESS if there is an error to report (not SQL_ERROR), and SQL_NO_DATA_FOUND if there are no more errors to report.

The examples pass the null pointer for some of the parameters to SQLError. The help file contains a full description of SQLError and all its parameters.

Top of page


Cursors in ODBC

ODBC cursors are similar to cursors in Embedded SQL (see "Cursors in Embedded SQL"). A cursor is opened using SQLExecute or SQLExecDirect, rows are fetched using SQLFetch or SQLExtendedFetch and the cursor is closed using SQLDropStmt.

To get values from a cursor, the application can use either SQLBindCol before a fetch or SQLGetData after a fetch.

The following code fragment opens and reads a cursor. Error checking has been omitted to make the example easier to read.

     . . .
     HDBC dbc;
     HSTMT stmt;
     RETCODE retcode;
     long emp_id;
     char emp_lname[20];
     
     SQLAllocStmt( dbc, &stmt );
     SQLExecDirect( stmt,
                     "select emp_id,emp_lname
                     from employee", SQL_NTS );
     SQLBindCol( stmt, 1, SQL_C_LONG, &emp_id,
                     sizeof(emp_id), NULL );
     SQLBindCol( stmt, 2, SQL_C_CHAR, &emp_lname,
                     sizeof(emp_lame), NULL );
     
     for(;;) {
         retcode = SQLFetch( stmt );
         if( retcode == SQL_NO_DATA_FOUND ) break;
         print_employee( emp_id, emp_lname);
     }
     
     /* Using SQL_CLOSE closes the cursor
         but does not free the statement */
     SQLFreeStmt( stmt, SQL_CLOSE );
     . . .

Top of page


Procedures and triggers in ODBC

Procedures can be both created and called using ODBC. Triggers can also be created. Refer to "Using Procedures, Triggers, and Batches" for a full description of stored procedures and triggers.

There are two types of procedures in SQL Anywhere; those that return result sets and those that do not. Use SQLNumResultCols to tell the difference. The number of result columns will be zero if the procedure does not return a result set. If there is a result set, you can fetch the values using SQLFetch or SQLExtendedFetch just like any other cursor.

Parameters to procedures should be passed using parameter markers (question marks). Use SQLSetParam to assign a storage area for each parameter marker whether it is an INPUT, OUTPUT or INOUT parameter.

The following example creates and calls a procedure. The procedure takes one INOUT parameter, and increments its value. In the example, the variable "num_col" will have the value zero, since the procedure does not return a result set. Error checking has been omitted to make the example easier to read.

     HDBC dbc;
     HSTMT stmt;
     long i;
     SWORD num_col;
     
     /* Create a procedure */
     SQLAllocStmt( dbc, &stmt );
     SQLExecDirect( stmt,
             "CREATE PROCEDURE Increment( INOUT a INT )" \
             " BEGIN" \
                 " SET a = a + 1" \
             " END", SQL_NTS );
     
     /* Call the procedure to increment 'i' */
     i = 1;
     SQLSetParam( stmt, 1, SQL_C_LONG, SQL_INTEGER, 0,
                     0, &i, NULL );
     SQLExecDirect( stmt, "CALL Increment( ? )",
                     SQL_NTS );
     SQLNumResultCols( stmt, &num_col );
     do_something( i );

The following example calls a procedure that returns a result set. In the example, the variable "num_col" will have the value two, since the procedure returns a result set with two columns. Again, error checking has been omitted to make the example easier to read.

     HDBC dbc;
     HSTMT stmt;
     SWORD num_col;
     RETCODE retcode;
     char emp_id[ 10 ];
     char emp_lame[ 20 ];
     
     /* Create the procedure */
     SQLExecDirect( stmt,
             "CREATE PROCEDURE employees()" \
             " RESULT( emp_id CHAR(10), emp_lname CHAR(20))"\
             " BEGIN" \
             " SELECT emp_id, emp_lame FROM employee" \
             " END", SQL_NTS );
     
     /* Call the procedure - print the results */
     SQLExecDirect( stmt, "CALL employees()", SQL_NTS );
     SQLNumResultCols( stmt, &num_col );
     SQLBindCol( stmt, 1, SQL_C_CHAR, &emp_id,
                     sizeof(emp_id), NULL );
     SQLBindCol( stmt, 2, SQL_C_CHAR, &emp_lname,
                     sizeof(emp_lname), NULL );
     
     for( ;; ) {
         retcode = SQLFetch( stmt );
         if( retcode == SQL_NO_DATA_FOUND ) {
         retcode = SQLMoreResults( stmt );
         if( retcode == SQL_NO_DATA_FOUND ) break;
     } else {
         do_something( emp_id, emp_lname );
     }
     }

Top of page


ODBC conformance

The SQL Anywhere ODBC driver supports all of the ODBC Version 2.5 API functions (Core, Level 1 and Level 2) under Windows 95 and Windows NT. ODBC Version 2.1 support is provided for Windows 3.x. However, the driver cannot handle the following ODBC features:

Top of page


The sample program

A sample ODBC program, ODBC.C, is supplied in the CXMP subdirectory of the SQL Anywhere installation directory (usually C:\SQLANY50). The program performs the same actions as the embedded SQL dynamic cursor example program. See "Database examples" for a description of the associated embedded SQL program.

Building the sample program

Along with the sample program is a batch file, MAKEALL.BAT, that can be used to compile the sample program for the various environments and compilers supported by SQL Anywhere. For OS/2 the command is MAKEALL.CMD. For QNX, use the shell script makeall.. The format of the command is as follows:

     makeall odbc {Platform} {Compiler} [DOS Extender]

The first parameter is "odbc", meaning compile the ODBC example. The same batch file also compiles the embedded SQL programs.

The second parameter is the platform in which the program will be run. The platform can be one of:

Building the sample program as an NT service

The example program ODBC., when compiled for Windows NT, runs optionally as a service.

The two files containing the example code for NT services are the source file NTSVC.C and the header file NTSVC.H. The code allows the linked executable to be run as either a regular executable or as an NT service.

To run the compiled example as an NT service:

  1. Start the SQL Anywhere Service Manager.
  2. Click New. The Service Type window is displayed.
  3. Click Sample Application, and click OK. The Edit SQL Anywhere Service Configuration window is displayed.
  4. Enter a service name.
  5. Click Path, and select the sample program (ODBCNT.EXE) from the CXMP subdirectory of the installation directory.
  6. Click OK to install the service.
  7. Click Start on the main window to start the service.

When run as a service, the program displays the normal user interface if possible. It also writes the output to the Application Event Log. If it is not possible to start the user interface, the program prints one page of data to the Application Event Log and stops.

This example has been tested with the Watcom C/C++ 10.5 compiler and the Microsoft Visual C++ 2.0 compiler.

Top of page


Contents IndexChapter 36.  ODBC Programming ODBC programming for the Macintosh