a firstworks project
SQL Relay
About Documentation Download Licensing Support News

Programming with SQL Relay using ODBC

The SQL Relay distribution provides an ODBC driver, enabling ODBC applications to access a database through SQL Relay.

Configuring a DSN

Whether you're developing your own ODBC application, or you want an existing ODBC application to be able to talk to SQL Relay, you'll have to configure a DSN (Data Source Name) for the application to use. The DSN provides the paraemters that are necessary for connecting to an instance of SQL Relay such as host, port, socket, etc.

Unix/Linux

On Unix or Linux platforms, using unixODBC or iODBC, before creating a DSN, you must first create an instance for the SQL Relay driver by adding an entry like the following to your ODBC instances file (usually /etc/odbcinst.ini)

[SQLRelay]
Description=ODBC for SQL Relay
Driver=/usr/local/firstworks/lib/libsqlrodbc.so
FileUsage=0

Note that the Driver attribute must be set to the full pathname of the libsqlrodbc.so driver. Note also that on Mac OS X you'll need to replace the .so suffix with .dylib

Once the instance is defined you can add a DSN for SQL Relay by adding an entry like the following to your ODBC INI file (usually /etc/odbc.ini)

[sqlrexample]
Description=Connection to SQL Relay
Driver=SQLRelay
Server=sqlrserver
Port=9000
Socket=/tmp/example.socket
User=exampleuser
Password=examplepass

Here the Driver attribute refers to the instance in odbcinst.ini.

Alternatively, you could create a combined instance/DSN by adding an entry like the following to your ODBC INI file (usually /etc/odbc.ini)

[sqlrexample]
Description=Connection to SQL Relay
Driver=/usr/local/firstworks/lib/libsqlrodbc.so
FileUsage=0
Server=sqlrserver
Port=9000
Socket=/tmp/example.socket
User=exampleuser
Password=examplepass

Windows

On Windows, just use the ODBC control panel to create a DSN.

Attributes

The ODBC driver for SQL Relay supports the following DSN attributes:

The following attributes can be used to establish Kerberos or Active Directory encryption and authentication with the server:

See the SQL Relay Configuration Guide for more information about Kerberos and Active Directory configurations. In particular, User and Password are not typically used when using Kerberos/AD.

The following attributes can be used to establish TLS/SSL encryption and authentication with the server:

See the SQL Relay Configuration Guide for more information about TLS/SSL configurations.

Note that the supported Tlscert and Tlsca file formats may vary between platforms. A variety of file formats are generally supported on Linux/Unix platfoms (.pem, .pfx, etc.) but only the .pfx format is currently supported on Windows.

Sample Session

After creating a DSN, on Linux/Unix, you can use a command line utility like isql that comes with iODBC or unixODBC, to access the database through SQL Relay as follows:

isql sqlrexample exampleuser examplepass

NOTE: The user and password are stored in the DSN, but most versions of isql require that they also be passed in on the command line.

Here is a sample session:

[dmuse@fedora ~]$ isql sqlrexample exampleuser examplepass
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL> create table exampletable (col1 int, col2 varchar2(20))
SQLRowCount returns 0
SQL> insert into exampletable values (1,'hello')
SQLRowCount returns 1
SQL> insert into exampletable values (2,'goodbye')
SQLRowCount returns 1
SQL> select * from exampletable
+-----+--------+
| COL1| COL2   |
+-----+--------+
| 1   | hello  |
| 2   | goodbye|
+-----+--------+
SQLRowCount returns 0
2 rows fetched
SQL> update exampletable set col2='bye' where col1=2
SQLRowCount returns 1
SQL> select * from exampletable
+-----+------+
| COL1| COL2 |
+-----+------+
| 1   | hello|
| 2   | bye  |
+-----+------+
SQLRowCount returns 0
2 rows fetched
SQL> delete from exampletable
SQLRowCount returns 2
SQL> select * from exampletable
+-----+-----+
| COL1| COL2|
+-----+-----+
+-----+-----+
SQLRowCount returns 0
SQL> drop table exampletable
SQLRowCount returns 0
SQL> quit
[dmuse@fedora ~]$ 

If you get an error like ISQL ERROR: Could not SQLConnect when you run isql then it's possible that it can't find your ODBC INI file. Some versions of unixODBC, when compiled and installed with non-standard prefixes, still look for odbc.ini in /etc rather than under their prefix. For example, I discovered that on Mac OS X, unixODBC 2.2.12 installed under /sw should look for /sw/etc/odbc.ini but instead it looks for /etc/odbc.ini, even though odbcinst -j looks under /sw.

You can work around this by setting environment variables to override whatever default search path unixODBC uses. For example, to force it to look under /sw/etc, use:

export ODBCINST=/sw/etc/odbcinst.ini
export ODBCINI=/sw/etc/odbc.ini

Other Applications

Any application that uses ODBC can be configured to use SQL Relay via ODBC by setting its DSN to the SQL Relay DSN that you defined.

You can also use SQL Relay with any language that supports ODBC directly or has a database abstraction layer that supports ODBC. For example, lets say you have an existing PHP app that uses MDB2 to connect to Oracle and you want to use SQL Relay with it. There is no PHP MDB2 driver for SQL Relay, but since MDB2 suppots ODBC, you could use:

PHP -> MDB2 -> ODBC -> SQL Relay -> Oracle

Compiling an ODBC Program

When writing an ODBC application, you need to include the sql.h and sqlext.h header files.

#include <sql.h>
#include <sqlext.h>

You'll also need to link against the ODBC library (-lodbc on most Unix/Linux platforms using unixODBC, or -liodbc if using iODBC).

On most Unix/Linux platforms, the header files are found in /usr/include and the libraries are found in /usr/lib or /usr/lib64. If unixODBC or iODBC was installed under a non-standard prefix, you may need to specify the prefix in your compiler and linker flags.

The command to compile your .c file to object code will look something like this (assuming you're using GCC on Linux or Unix):

gcc -c myprogram.c

The command to compile your .o file to an executable will look something like this:

gcc -o myprogram myprogram.o -lodbc

Establishing a Session

To use ODBC, you must allocate an environment handle, a connection handle, and then connect using the DSN that you configured.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);

        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);

        ... execute some queries ...

        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

SQLAllocHandle() allocates a handle of the specified type. SQL_HANDLE_ENV allocates an environment handle and SQL_HANDLE_DBC allocates a connection handle. SQLSetEnvAttr() is used to set the ODBC version to ODBC 3. SQLConnect() connects to the DSN, passing the username and password.

For the duration of the session, the client occupies one of the database connections, so care should be taken to minimize the length of a session.

Encryption and authentication options (Kerberos/Active Directory and TLS/SSL) are configured in the DSN, as described above.

Executing Queries

Allocate a statement handle, then call SQLExecDirect() to execute a query. The same statement handle may be reused after calling SQLFreeStmt() with SQL_CLOSE to close the cursor.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLExecDirect(stmt,
                (SQLCHAR *)"select * from my_table",SQL_NTS);

        ... do some stuff that takes a short time ...

        SQLFreeStmt(stmt,SQL_CLOSE);
        SQLExecDirect(stmt,
                (SQLCHAR *)"select * from my_other_table",SQL_NTS);

        ... process the result set ...

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Commits and Rollbacks

If you need to execute a commit or rollback, you should use SQLEndTran() with SQL_COMMIT or SQL_ROLLBACK rather than sending a "commit" or "rollback" query.

You can also turn Autocommit on or off using SQLSetConnectAttr() with the SQL_ATTR_AUTOCOMMIT attribute. When Autocommit is on, the database performs a commit after each successful DML or DDL query. When Autocommit is off, the database commits when the client instructs it to, or (by default) when a client disconnects.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);

        // turn off autocommit
        SQLSetConnectAttr(dbc,SQL_ATTR_AUTOCOMMIT,
                                (SQLPOINTER)SQL_AUTOCOMMIT_OFF,0);

        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLExecDirect(stmt,
                (SQLCHAR *)"insert into my_table values (1,2,3)",
                SQL_NTS);

        SQLEndTran(SQL_HANDLE_DBC,dbc,SQL_COMMIT);

        SQLFreeStmt(stmt,SQL_CLOSE);
        SQLExecDirect(stmt,
                (SQLCHAR *)"insert into my_table values (4,5,6)",
                SQL_NTS);

        SQLEndTran(SQL_HANDLE_DBC,dbc,SQL_ROLLBACK);

        // turn autocommit back on
        SQLSetConnectAttr(dbc,SQL_ATTR_AUTOCOMMIT,
                                (SQLPOINTER)SQL_AUTOCOMMIT_ON,0);

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Temporary Tables

Some databases support temporary tables. That is, tables which are automatically dropped or truncated when an application closes its connection to the database or when a transaction is committed or rolled back.

For databases which drop or truncate tables when a transaction is committed or rolled back, temporary tables work naturally.

However, for databases which drop or truncate tables when an application closes its connection to the database, there is an issue. Since SQL Relay maintains persistent database connections, when an application disconnects from SQL Relay, the connection between SQL Relay and the database remains, so the database does not know to drop or truncate the table. To remedy this situation, SQL Relay parses each query to see if it created a temporary table, keeps a list of temporary tables and drops (or truncates them) when the application disconnects from SQL Relay. Since each database has slightly different syntax for creating a temporary table, SQL Relay parses each query according to the rules for that database.

In effect, temporary tables should work when an application connects to SQL Relay in the same manner that they would work if the application connected directly to the database.

Catching Errors

If your call to SQLExecDirect() or SQLExecute() returns something other than SQL_SUCCESS, you can find out why by calling SQLGetDiagRec().

#include <sql.h>
#include <sqlext.h>
#include <stdio.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        if (SQLExecDirect(stmt,
                (SQLCHAR *)"select * from my_nonexistant_table",
                SQL_NTS)!=SQL_SUCCESS) {

                SQLCHAR         state[6];
                SQLINTEGER      nativeerror;
                SQLCHAR         message[SQL_MAX_MESSAGE_LENGTH];
                SQLSMALLINT     messagelen;

                SQLGetDiagRec(SQL_HANDLE_STMT,stmt,1,
                                state,&nativeerror,
                                message,sizeof(message),
                                &messagelen);

                printf("Error %s: %s\n",state,message);
        }

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Bind Variables

Programs rarely execute fixed queries. More often than not, some part of the query is dynamically generated. The ODBC API provides means for using bind variables (also known as parameters) in those queries.

For a detailed discussion of binds, see this document.

ODBC provides SQLBindParameter() for binding values to parameter markers (?) in queries. You call SQLPrepare() to prepare the query, SQLBindParameter() to bind each parameter, and SQLExecute() to execute the query.

When passing a floating point number as a bind variable, ODBC handles the precision and scale through the SQL type specification. See this page for a discussion of precision and scale.

Note that ODBC requires question marks (?) as parameter markers, but most databases use other formats natively (eg. :name for Oracle, @name for Sybase/MSSQL, $1 for PostgreSQL). For ODBC bind variables to work against those databases, SQL Relay must be configured to translate them by setting translatebindvariables="yes" in the sqlrelay.conf file for the instance you're using. See the SQL Relay Configuration Reference for more information on this parameter.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLCHAR         stringval[]="true";
        SQLINTEGER      integerval=10;
        SQLDOUBLE       floatval=1.1;
        SQLLEN          stringvallen=SQL_NTS;
        SQLLEN          integervallen=0;
        SQLLEN          floatvallen=0;

        SQLPrepare(stmt,
                (SQLCHAR *)"select * from mytable "
                        "where stringcol=? "
                        "and integercol>? "
                        "and floatcol>?",
                SQL_NTS);
        SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                        SQL_C_CHAR,SQL_VARCHAR,
                        4,0,stringval,sizeof(stringval),
                        &stringvallen);
        SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                        SQL_C_SLONG,SQL_INTEGER,
                        0,0,&integerval,0,
                        &integervallen);
        SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                        SQL_C_DOUBLE,SQL_DOUBLE,
                        0,0,&floatval,0,
                        &floatvallen);
        SQLExecute(stmt);

        ... process the result set ...

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

ODBC also supports output bind variables via SQLBindParameter() with the SQL_PARAM_OUTPUT direction. This is useful for retrieving data from stored procedure calls.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLINTEGER      integer1=10;
        SQLINTEGER      integer2=20;
        SQLDOUBLE       float1=1.1;
        SQLDOUBLE       float2=2.2;
        SQLINTEGER      integer3=30;
        SQLINTEGER      result1;
        SQLDOUBLE       result2;
        SQLCHAR         result3[101];
        SQLLEN          integer1len=0;
        SQLLEN          integer2len=0;
        SQLLEN          float1len=0;
        SQLLEN          float2len=0;
        SQLLEN          integer3len=0;
        SQLLEN          result1len=0;
        SQLLEN          result2len=0;
        SQLLEN          result3len=0;

        SQLPrepare(stmt,
                (SQLCHAR *)"{call addAndConvert(?,?,?,?,?,?,?,?)}",
                SQL_NTS);
        SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                        SQL_C_SLONG,SQL_INTEGER,
                        0,0,&integer1,0,&integer1len);
        SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                        SQL_C_SLONG,SQL_INTEGER,
                        0,0,&integer2,0,&integer2len);
        SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                        SQL_C_DOUBLE,SQL_DOUBLE,
                        0,0,&float1,0,&float1len);
        SQLBindParameter(stmt,4,SQL_PARAM_INPUT,
                        SQL_C_DOUBLE,SQL_DOUBLE,
                        0,0,&float2,0,&float2len);
        SQLBindParameter(stmt,5,SQL_PARAM_INPUT,
                        SQL_C_SLONG,SQL_INTEGER,
                        0,0,&integer3,0,&integer3len);
        SQLBindParameter(stmt,6,SQL_PARAM_OUTPUT,
                        SQL_C_SLONG,SQL_INTEGER,
                        0,0,&result1,0,&result1len);
        SQLBindParameter(stmt,7,SQL_PARAM_OUTPUT,
                        SQL_C_DOUBLE,SQL_DOUBLE,
                        0,0,&result2,0,&result2len);
        SQLBindParameter(stmt,8,SQL_PARAM_OUTPUT,
                        SQL_C_CHAR,SQL_VARCHAR,
                        100,0,result3,sizeof(result3),
                        &result3len);
        SQLExecute(stmt);

        ... do something with result1, result2, result3 ...

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

The SQLGetData() method returns a NULL value as an empty string by default. To receive NULLs, check whether the length/indicator variable is set to SQL_NULL_DATA after calling SQLGetData().

You can insert data into BLOB and CLOB columns using SQLBindParameter() with SQL_C_BINARY/SQL_LONGVARBINARY types for BLOBs and SQL_C_CHAR/SQL_LONGVARCHAR types for CLOBs.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLExecDirect(stmt,
                (SQLCHAR *)"create table images "
                        "(image blob, description clob)",
                SQL_NTS);
        SQLFreeStmt(stmt,SQL_CLOSE);

        unsigned char   imagedata[40000];
        SQLLEN          imagelength;

        ... read an image from a file into imagedata and the length of the
                file into imagelength ...

        SQLCHAR         description[40000];
        SQLLEN          desclength;

        ... read a description from a file into description and the length of
                the file into desclength ...

        SQLPrepare(stmt,
                (SQLCHAR *)"insert into images values (?,?)",
                SQL_NTS);
        SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                        SQL_C_BINARY,SQL_LONGVARBINARY,
                        imagelength,0,imagedata,imagelength,
                        &imagelength);
        SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                        SQL_C_CHAR,SQL_LONGVARCHAR,
                        desclength,0,description,desclength,
                        &desclength);
        SQLExecute(stmt);

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Likewise, you can retrieve BLOB or CLOB data using SQLGetData() with the appropriate type.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLExecDirect(stmt,
                (SQLCHAR *)"select image, description from images",
                SQL_NTS);

        unsigned char   image[40000];
        SQLLEN          imagelength;
        SQLCHAR         desc[40000];
        SQLLEN          desclength;

        while (SQLFetch(stmt)==SQL_SUCCESS) {
                SQLGetData(stmt,1,SQL_C_BINARY,
                                image,sizeof(image),
                                &imagelength);
                SQLGetData(stmt,2,SQL_C_CHAR,
                                desc,sizeof(desc),
                                &desclength);

                ... do something with image and desc ...
        }

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Re-Binding and Re-Executing

Another feature of the prepare/bind/execute paradigm is the ability to prepare and bind a query once, then re-execute the query over and over with different values without re-preparing it. Since the bind variables point to application buffers, you can simply update the buffer values and call SQLExecute() again. If your back-end database natively supports this paradigm, you can reap a substantial performance improvement.

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLINTEGER      value;
        SQLLEN          valuelen=0;

        SQLPrepare(stmt,
                (SQLCHAR *)"select * from mytable where mycolumn>?",
                SQL_NTS);
        SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                        SQL_C_SLONG,SQL_INTEGER,
                        0,0,&value,0,&valuelen);

        value=1;
        SQLExecute(stmt);

        ... process the result set ...

        SQLFreeStmt(stmt,SQL_CLOSE);
        value=5;
        SQLExecute(stmt);

        ... process the result set ...

        SQLFreeStmt(stmt,SQL_CLOSE);
        value=10;
        SQLExecute(stmt);

        ... process the result set ...

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Accessing Fields in the Result Set

SQLNumResultCols(), SQLFetch() and SQLGetData() are useful for processing result sets.

#include <sql.h>
#include <sqlext.h>
#include <stdio.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLExecDirect(stmt,
                (SQLCHAR *)"select * from my_table",SQL_NTS);

        SQLSMALLINT     cols;
        SQLNumResultCols(stmt,&cols);

        while (SQLFetch(stmt)==SQL_SUCCESS) {
                SQLCHAR field[256];
                SQLLEN  fieldlen;
                for (int col=1; col<=cols; col++) {
                        SQLGetData(stmt,col,SQL_C_CHAR,
                                        field,sizeof(field),
                                        &fieldlen);
                        printf("%s,",field);
                }
                printf("\n");
        }

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Alternatively, you can use SQLBindCol() to bind columns to application buffers before fetching. Each call to SQLFetch() will then populate the bound buffers automatically.

#include <sql.h>
#include <sqlext.h>
#include <stdio.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLExecDirect(stmt,
                (SQLCHAR *)"select * from my_table",SQL_NTS);

        SQLCHAR col1[256];
        SQLCHAR col2[256];
        SQLCHAR col3[256];
        SQLLEN  col1len;
        SQLLEN  col2len;
        SQLLEN  col3len;

        SQLBindCol(stmt,1,SQL_C_CHAR,col1,sizeof(col1),&col1len);
        SQLBindCol(stmt,2,SQL_C_CHAR,col2,sizeof(col2),&col2len);
        SQLBindCol(stmt,3,SQL_C_CHAR,col3,sizeof(col3),&col3len);

        while (SQLFetch(stmt)==SQL_SUCCESS) {
                printf("%s,%s,%s\n",col1,col2,col3);
        }

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Cursors

Cursors make it possible to execute queries while processing the result set of another query. In ODBC, each statement handle (HSTMT) acts as a cursor. You can allocate multiple statement handles on the same connection, allowing you to select rows from one table in one statement, iterate through its result set, and insert rows into another table using a second statement, all using a single database connection.

For example:

#include <sql.h>
#include <sqlext.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt1;
        SQLHSTMT stmt2;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt1);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt2);

        SQLExecDirect(stmt1,
                (SQLCHAR *)"select * from my_huge_table",SQL_NTS);

        SQLCHAR col1[256];
        SQLCHAR col2[256];
        SQLCHAR col3[256];
        SQLLEN  col1len;
        SQLLEN  col2len;
        SQLLEN  col3len;
        SQLLEN  p1len=SQL_NTS;
        SQLLEN  p2len=SQL_NTS;
        SQLLEN  p3len=SQL_NTS;

        SQLBindCol(stmt1,1,SQL_C_CHAR,
                        col1,sizeof(col1),&col1len);
        SQLBindCol(stmt1,2,SQL_C_CHAR,
                        col2,sizeof(col2),&col2len);
        SQLBindCol(stmt1,3,SQL_C_CHAR,
                        col3,sizeof(col3),&col3len);

        SQLPrepare(stmt2,
                (SQLCHAR *)"insert into my_other_table "
                        "values (?,?,?)",
                SQL_NTS);
        SQLBindParameter(stmt2,1,SQL_PARAM_INPUT,
                        SQL_C_CHAR,SQL_VARCHAR,
                        255,0,col1,sizeof(col1),&p1len);
        SQLBindParameter(stmt2,2,SQL_PARAM_INPUT,
                        SQL_C_CHAR,SQL_VARCHAR,
                        255,0,col2,sizeof(col2),&p2len);
        SQLBindParameter(stmt2,3,SQL_PARAM_INPUT,
                        SQL_C_CHAR,SQL_VARCHAR,
                        255,0,col3,sizeof(col3),&p3len);

        while (SQLFetch(stmt1)==SQL_SUCCESS) {
                SQLExecute(stmt2);
        }

        SQLFreeHandle(SQL_HANDLE_STMT,stmt2);
        SQLFreeHandle(SQL_HANDLE_STMT,stmt1);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Getting Column Information

For each column, the ODBC API supports getting the name, type, size, decimal digits, and nullability via SQLDescribeCol(). Additional attributes such as display size, whether the column is auto-incrementing, and whether the column is unsigned can be retrieved using SQLColAttribute().

#include <sql.h>
#include <sqlext.h>
#include <stdio.h>

main() {

        SQLHENV env;
        SQLHDBC dbc;
        SQLHSTMT stmt;

        SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
        SQLSetEnvAttr(env,SQL_ATTR_ODBC_VERSION,
                                (SQLPOINTER)SQL_OV_ODBC3,0);
        SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
        SQLConnect(dbc,(SQLCHAR *)"sqlrexample",SQL_NTS,
                        (SQLCHAR *)"user",SQL_NTS,
                        (SQLCHAR *)"password",SQL_NTS);
        SQLAllocHandle(SQL_HANDLE_STMT,dbc,&stmt);

        SQLExecDirect(stmt,
                (SQLCHAR *)"select * from my_table",SQL_NTS);

        SQLSMALLINT     cols;
        SQLNumResultCols(stmt,&cols);

        for (int i=1; i<=cols; i++) {

                SQLCHAR         name[256];
                SQLSMALLINT     namelen;
                SQLSMALLINT     datatype;
                SQLULEN         columnsize;
                SQLSMALLINT     decimaldigits;
                SQLSMALLINT     nullable;

                SQLDescribeCol(stmt,i,
                                name,sizeof(name),&namelen,
                                &datatype,&columnsize,
                                &decimaldigits,&nullable);

                SQLLEN  displaysize;
                SQLColAttribute(stmt,i,
                                SQL_DESC_DISPLAY_SIZE,
                                NULL,0,NULL,
                                &displaysize);

                SQLLEN  autoincrement;
                SQLColAttribute(stmt,i,
                                SQL_DESC_AUTO_UNIQUE_VALUE,
                                NULL,0,NULL,
                                &autoincrement);

                SQLLEN  isunsigned;
                SQLColAttribute(stmt,i,
                                SQL_DESC_UNSIGNED,
                                NULL,0,NULL,
                                &isunsigned);

                printf("Name:           %s\n",name);
                printf("Type:           %d\n",datatype);
                printf("Column Size:    %ld\n",(long)columnsize);
                printf("Decimal Digits: %d\n",decimaldigits);
                printf("Nullable:       %d\n",nullable);
                printf("Display Size:   %ld\n",(long)displaysize);
                printf("Auto Increment: %ld\n",(long)autoincrement);
                printf("Unsigned:       %ld\n",(long)isunsigned);
                printf("\n");
        }

        SQLFreeHandle(SQL_HANDLE_STMT,stmt);
        SQLDisconnect(dbc);
        SQLFreeHandle(SQL_HANDLE_DBC,dbc);
        SQLFreeHandle(SQL_HANDLE_ENV,env);
}

Stored Procedures

Many databases support stored procedures. Stored procedures are sets of queries and procedural code that are executed inside of the database itself. For example, a stored procedure may select rows from one table, iterate through the result set and, based on the values in each row, insert, update or delete rows in other tables. A client program could do this as well, but a stored procedure is generally more efficient because queries and result sets don't have to be sent back and forth between the client and database. Also, stored procedures are generally stored in the database in a compiled state, while queries may have to be re-parsed and re-compiled each time they are sent.

While many databases support stored procedures. The syntax for creating and executing stored procedures varies greatly between databases.

Stored procedures typically take input paramters from client programs through input bind variables and return values back to client programs either through bind variables or result sets. Stored procedures can be broken down into several categories, based on the values that they return. Some stored procedures don't return any values, some return a single value, some return multiple values and some return entire result sets.

No Values

Some stored procedures don't return any values. Below are examples, illustrating how to create, execute and drop this kind of stored procedure for each database that SQL Relay supports.

Oracle

To create the stored procedure, run a query like the following.

create procedure exampleproc(in1 in number, in2 in number, in3 in varchar2) is
begin
        insert into mytable values (in1,in2,in3);
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,(SQLCHAR *)"begin exampleproc(?,?,?); end;",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Sybase and Microsoft SQL Server

To create the stored procedure, run a query like the following.

create procedure exampleproc @in1 int, @in2 float, @in3 varchar(20) as
        insert into mytable values (@in1,@in2,@in3)

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Firebird

To create the stored procedure, run a query like the following.

create procedure exampleproc(in1 integer, in2 float, in3 varchar(20)) as
begin
        insert into mytable values (in1,in2,in3);
        suspend;
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,
        (SQLCHAR *)"execute procedure exampleproc ?,?,?",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

DB2

To create the stored procedure, run a query like the following.

create procedure exampleproc(in in1 int, in in2 double, in in3 varchar(20)) language sql
begin
        insert into mytable values (in1,in2,in3);
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Postgresql

To create the stored procedure, run a query like the following.

create function examplefunc(int,float,varchar(20)) returns void as '
begin
        insert into mytable values ($1,$2,$3);
        return;
end;' language plpgsql

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,(SQLCHAR *)"select examplefunc(?,?,?)",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

To drop the stored procedure, run a query like the following.

drop function examplefunc(int,float,varchar(20))

MySQL/MariaDB

To create the stored procedure, run a query like the following.

create procedure exampleproc(in in1 int, in in2 float, in in3 varchar(20))
begin
        insert into mytable values (in1,in2,in3);
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Single Values

Some stored procedures return single values. Below are examples, illustrating how to create, execute and drop this kind of stored procedure for each database that SQL Relay supports.

Oracle

In Oracle, stored procedures can return values through output parameters or as return values of the procedure itself.

Here is an example where the procedure itself returns a value. Note that Oracle calls these functions.

To create the stored procedure, run a query like the following.

create function exampleproc(in1 in number, in2 in number, in3 in varchar2) returns number is
begin
        return in1;
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,
        (SQLCHAR *)"select exampleproc(?,?,?) from dual",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

SQLCHAR result[256];
SQLLEN  resultlen;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,result,sizeof(result),&resultlen);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Here is an example where the value is returned through an output parameter.

To create the stored procedure, run a query like the following.

create procedure exampleproc(in1 in number, in2 in number, in3 in varchar2, out1 out number) as
begin
        out1:=in1;
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLINTEGER      out1;
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;
SQLLEN          out1len=0;

SQLPrepare(stmt,(SQLCHAR *)"begin exampleproc(?,?,?,?); end;",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLBindParameter(stmt,4,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLExecute(stmt);
// out1 now contains the result

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Sybase and Microsoft SQL Server

In Sybase and Microsoft SQL Server, stored procedures return values through output parameters rather than as return values of the procedure itself.

To create the stored procedure, run a query like the following.

create procedure exampleproc @in1 int, @in2 float, @in3 varchar(20), @out1 int output as
        select @out1=convert(varchar(20),@in1)

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLINTEGER      out1;
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;
SQLLEN          out1len=0;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLBindParameter(stmt,4,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLExecute(stmt);
// out1 now contains the result

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Firebird

To create the stored procedure, run a query like the following.

create procedure exampleproc(in1 integer, in2 float, in3 varchar(20)) returns (out1 integer) as
begin
        out1=in1;
        suspend;
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,
        (SQLCHAR *)"select * from exampleproc(?,?,?)",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

SQLCHAR result[256];
SQLLEN  resultlen;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,result,sizeof(result),&resultlen);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

DB2

In DB2, stored procedures return values through output parameters rather than as return values of the procedure itself.

To create the stored procedure, run a query like the following.

create procedure exampleproc(in in1 int, in in2 double, in in3 varchar(20), out out1 int) language sql
begin
        set out1 = in1;
end

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLINTEGER      out1;
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;
SQLLEN          out1len=0;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLBindParameter(stmt,4,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLExecute(stmt);
// out1 now contains the result

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Postgresql

To create the stored procedure, run a query like the following.

create function examplefunc(int,float,char(20)) returns int as '
declare
        in1 int;
        in2 float;
        in3 char(20);
begin
        in1:=$1;
        return;
end;
' language plpgsql

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,
        (SQLCHAR *)"select * from examplefunc(?,?,?)",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

SQLCHAR result[256];
SQLLEN  resultlen;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,result,sizeof(result),&resultlen);

To drop the stored procedure, run a query like the following.

drop function examplefunc(int,float,char(20))

MySQL/MariaDB

A single value can be returned from a MySQL/MariaDB function.

To create the function, run a query like the following.

create function examplefunc(in in1 int, in in2 float, in in3 varchar(20)) returns int return in1;

To execute the function from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,
        (SQLCHAR *)"select examplefunc(?,?,?)",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

SQLCHAR result[256];
SQLLEN  resultlen;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,result,sizeof(result),&resultlen);

To drop the function, run a query like the following.

drop procedure exampleproc

A single value can be returned in the result set of a MySQL/MariaDB procedure.

To create the procedure, run a query like the following.

create procedure exampleproc() begin select 1; end;

To execute the procedure from an SQL Relay program, use code like the following.

SQLExecDirect(stmt,(SQLCHAR *)"{call exampleproc}",SQL_NTS);

SQLCHAR result[256];
SQLLEN  resultlen;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,result,sizeof(result),&resultlen);

To drop the procedure, run a query like the following.

drop procedure exampleproc

A single value can be returned using the output variable of a MySQL/MariaDB procedure.

To create the procedure, run a query like the following.

create procedure exampleproc(out out1 int) begin select 1 into out1; end;

To execute the procedure from an SQL Relay program, use code like the following.

SQLINTEGER      out1;
SQLLEN          out1len=0;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLExecute(stmt);
// out1 now contains the result

To drop the procedure, run a query like the following.

drop procedure exampleproc

Multiple Values

Some stored procedures return multiple values. Below are examples, illustrating how to create, execute and drop this kind of stored procedure for each database that SQL Relay supports.

Oracle

In Oracle, stored procedures can return values through output parameters or as return values of the procedure itself. If a procedure needs to return multiple values, it can return one of them as the return value of the procedure itself, but the rest must be returned through output parameters.

To create the stored procedure, run a query like the following.

create procedure exampleproc(in1 in number, in2 in number, in3 in varchar2, out1 out number, out2 out number, out3 out varchar2) is
begin
        out1:=in1;
        out2:=in2;
        out3:=in3;
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLINTEGER      out1;
SQLDOUBLE       out2;
SQLCHAR         out3[21];
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;
SQLLEN          out1len=0;
SQLLEN          out2len=0;
SQLLEN          out3len=0;

SQLPrepare(stmt,
        (SQLCHAR *)"begin exampleproc(?,?,?,?,?,?); end;",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLBindParameter(stmt,4,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLBindParameter(stmt,5,SQL_PARAM_OUTPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&out2,0,&out2len);
SQLBindParameter(stmt,6,SQL_PARAM_OUTPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,out3,sizeof(out3),&out3len);
SQLExecute(stmt);
// out1,out2,out3 now contain the results

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Sybase and Microsoft SQL Server

To create the stored procedure, run a query like the following.

create procedure exampleproc @in1 int, @in2 float, @in3 varchar(20), @out1 int output, @out2 int output, @out3 int output as
        select @out1=convert(varchar(20),@in1),
                @out2=convert(varchar(20),@in2),
                @out2=convert(varchar(20),@in2)

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLINTEGER      out1;
SQLDOUBLE       out2;
SQLCHAR         out3[21];
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;
SQLLEN          out1len=0;
SQLLEN          out2len=0;
SQLLEN          out3len=0;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?,?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLBindParameter(stmt,4,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLBindParameter(stmt,5,SQL_PARAM_OUTPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&out2,0,&out2len);
SQLBindParameter(stmt,6,SQL_PARAM_OUTPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,out3,sizeof(out3),&out3len);
SQLExecute(stmt);
// out1,out2,out3 now contain the results

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Firebird

To create the stored procedure, run a query like the following.

create procedure exampleproc(in1 integer, in2 float, in3 varchar(20)) returns (out1 integer, out2 float, out3 varchar(20)) as
begin
        out1=in1;
        out2=in2;
        out3=in3;
        suspend;
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,
        (SQLCHAR *)"select * from exampleproc(?,?,?)",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

SQLCHAR out1[256];
SQLCHAR out2[256];
SQLCHAR out3[256];
SQLLEN  out1len;
SQLLEN  out2len;
SQLLEN  out3len;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,out1,sizeof(out1),&out1len);
SQLGetData(stmt,2,SQL_C_CHAR,out2,sizeof(out2),&out2len);
SQLGetData(stmt,3,SQL_C_CHAR,out3,sizeof(out3),&out3len);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

DB2

To create the stored procedure, run a query like the following.

create procedure exampleproc(in in1 int, in in2 double, in in3 varchar(20), out out1 int, out out2 double, out out3 varchar(20)) language sql
begin
        set out1 = in1;
        set out2 = in2;
        set out3 = in3;
end

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLINTEGER      out1;
SQLDOUBLE       out2;
SQLCHAR         out3[21];
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;
SQLLEN          out1len=0;
SQLLEN          out2len=0;
SQLLEN          out3len=0;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?,?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLBindParameter(stmt,4,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLBindParameter(stmt,5,SQL_PARAM_OUTPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&out2,0,&out2len);
SQLBindParameter(stmt,6,SQL_PARAM_OUTPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,out3,sizeof(out3),&out3len);
SQLExecute(stmt);
// out1,out2,out3 now contain the results

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Postgresql

To create the stored procedure, run a query like the following.

create function examplefunc(int,float,char(20)) returns record as '
declare
        output record;
begin
        select $1,$2,$3 into output;
        return output;
end;
' language plpgsql

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,
        (SQLCHAR *)"select * from examplefunc(?,?,?) "
                "as (col1 int, col2 float, col3 char(20))",
        SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

SQLCHAR out1[256];
SQLCHAR out2[256];
SQLCHAR out3[256];
SQLLEN  out1len;
SQLLEN  out2len;
SQLLEN  out3len;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,out1,sizeof(out1),&out1len);
SQLGetData(stmt,2,SQL_C_CHAR,out2,sizeof(out2),&out2len);
SQLGetData(stmt,3,SQL_C_CHAR,out3,sizeof(out3),&out3len);

To drop the stored procedure, run a query like the following.

drop function examplefunc(int,float,char(20))

MySQL/MariaDB

Here's how you can get multiple values from the result set of a MySQL/MariaDB procedure.

To create the stored procedure, run a query like the following.

create procedure exampleproc(in in1 int, in in2 float, in in3 varchar(20)) begin select in1, in2, in3; end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      in1=1;
SQLDOUBLE       in2=1.1;
SQLCHAR         in3[]="hello";
SQLLEN          in1len=0;
SQLLEN          in2len=0;
SQLLEN          in3len=SQL_NTS;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_INPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&in1,0,&in1len);
SQLBindParameter(stmt,2,SQL_PARAM_INPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&in2,0,&in2len);
SQLBindParameter(stmt,3,SQL_PARAM_INPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,in3,sizeof(in3),&in3len);
SQLExecute(stmt);

SQLCHAR out1[256];
SQLCHAR out2[256];
SQLCHAR out3[256];
SQLLEN  out1len;
SQLLEN  out2len;
SQLLEN  out3len;
SQLFetch(stmt);
SQLGetData(stmt,1,SQL_C_CHAR,out1,sizeof(out1),&out1len);
SQLGetData(stmt,2,SQL_C_CHAR,out2,sizeof(out2),&out2len);
SQLGetData(stmt,3,SQL_C_CHAR,out3,sizeof(out3),&out3len);

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Here's how you can get multiple values from the output variables of a MySQL/MariaDB procedure.

To create the stored procedure, run a query like the following.

create procedure exampleproc(out out1 int, out out2 float, out out3 varchar(20)) begin select 1,1.1,'hello' into out1, out2, out3; end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLINTEGER      out1;
SQLDOUBLE       out2;
SQLCHAR         out3[21];
SQLLEN          out1len=0;
SQLLEN          out2len=0;
SQLLEN          out3len=0;

SQLPrepare(stmt,(SQLCHAR *)"{call exampleproc(?,?,?)}",SQL_NTS);
SQLBindParameter(stmt,1,SQL_PARAM_OUTPUT,
                SQL_C_SLONG,SQL_INTEGER,0,0,&out1,0,&out1len);
SQLBindParameter(stmt,2,SQL_PARAM_OUTPUT,
                SQL_C_DOUBLE,SQL_DOUBLE,0,0,&out2,0,&out2len);
SQLBindParameter(stmt,3,SQL_PARAM_OUTPUT,
                SQL_C_CHAR,SQL_VARCHAR,20,0,out3,sizeof(out3),&out3len);
SQLExecute(stmt);
// out1,out2,out3 now contain the results

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Result Sets

Some stored procedures return entire result sets. Below are examples, illustrating how to create, execute and drop this kind of stored procedure for each database that SQL Relay supports.

Oracle

To create the stored procedure, run a query like the following.

create or replace package types as
        type cursorType is ref cursor;
end;

create function exampleproc return types.cursortype is
        l_cursor    types.cursorType;
begin
        open l_cursor for select * from mytable;
        return l_cursor;
end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLExecDirect(stmt,
        (SQLCHAR *)"begin open :curs for "
                "select * from mytable; end;",
        SQL_NTS);

SQLCHAR col1[256];
SQLCHAR col2[256];
SQLCHAR col3[256];
SQLLEN  col1len;
SQLLEN  col2len;
SQLLEN  col3len;

while (SQLFetch(stmt)==SQL_SUCCESS) {
        SQLGetData(stmt,1,SQL_C_CHAR,col1,sizeof(col1),&col1len);
        SQLGetData(stmt,2,SQL_C_CHAR,col2,sizeof(col2),&col2len);
        SQLGetData(stmt,3,SQL_C_CHAR,col3,sizeof(col3),&col3len);
        ... process col1, col2, col3 ...
}

To drop the stored procedure, run a query like the following.

drop function exampleproc
drop package types

Sybase and Microsoft SQL Server

To create the stored procedure, run a query like the following.

create procedure exampleproc as select * from exampletable

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLExecDirect(stmt,(SQLCHAR *)"{call exampleproc}",SQL_NTS);

SQLCHAR col1[256];
SQLCHAR col2[256];
SQLCHAR col3[256];
SQLLEN  col1len;
SQLLEN  col2len;
SQLLEN  col3len;

while (SQLFetch(stmt)==SQL_SUCCESS) {
        SQLGetData(stmt,1,SQL_C_CHAR,col1,sizeof(col1),&col1len);
        SQLGetData(stmt,2,SQL_C_CHAR,col2,sizeof(col2),&col2len);
        SQLGetData(stmt,3,SQL_C_CHAR,col3,sizeof(col3),&col3len);
        ... process col1, col2, col3 ...
}

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Firebird

Stored procedures in Firebird can return a result set if a select query in the procedure selects values into the output parameters and then issues a suspend command, however SQL Relay doesn't currently support stored procedures that return result sets.

DB2

Stored procedures in DB2 can return a result set if the procedure is declared to return one, however SQL Relay doesn't currently support stored procedures that return result sets.

Postgresql

To create the stored procedure, run a query like the following.

create function examplefunc() returns setof record as '
        declare output record;
begin
        for output in select * from mytable loop
                return next output;
        end loop;
        return;
end;
' language plpgsql

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLExecDirect(stmt,
        (SQLCHAR *)"select * from examplefunc() "
                "as (col1 int, col2 float, col3 char(40))",
        SQL_NTS);

SQLCHAR col1[256];
SQLCHAR col2[256];
SQLCHAR col3[256];
SQLLEN  col1len;
SQLLEN  col2len;
SQLLEN  col3len;

while (SQLFetch(stmt)==SQL_SUCCESS) {
        SQLGetData(stmt,1,SQL_C_CHAR,col1,sizeof(col1),&col1len);
        SQLGetData(stmt,2,SQL_C_CHAR,col2,sizeof(col2),&col2len);
        SQLGetData(stmt,3,SQL_C_CHAR,col3,sizeof(col3),&col3len);
        ... process col1, col2, col3 ...
}

To drop the stored procedure, run a query like the following.

drop function examplefunc

MySQL/MariaDB

The result sets of all select statements called within MySQL/MariaDB stored procedures (that aren't selected into variables) are returned from the procedure call. Though MySQL/MariaDB stored procedures can return multiple result sets, currently SQL Relay can only fetch the first result set.

To create the stored procedure which returns a result set, run a query like the following.

create procedure exampleproc() begin select * from mytable; end;

To execute the stored procedure from an SQL Relay program, use code like the following.

SQLExecDirect(stmt,(SQLCHAR *)"{call exampleproc}",SQL_NTS);

SQLCHAR col1[256];
SQLCHAR col2[256];
SQLCHAR col3[256];
SQLLEN  col1len;
SQLLEN  col2len;
SQLLEN  col3len;

while (SQLFetch(stmt)==SQL_SUCCESS) {
        SQLGetData(stmt,1,SQL_C_CHAR,col1,sizeof(col1),&col1len);
        SQLGetData(stmt,2,SQL_C_CHAR,col2,sizeof(col2),&col2len);
        SQLGetData(stmt,3,SQL_C_CHAR,col3,sizeof(col3),&col3len);
        ... process col1, col2, col3 ...
}

To drop the stored procedure, run a query like the following.

drop procedure exampleproc

Copyright 2024 - David Muse - Contact