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
- Compiling an ODBC Program
- Establishing a Session
- Executing Queries
- Commits and Rollbacks
- Temporary Tables
- Catching Errors
- Bind Variables
- Re-Binding and Re-Executing
- Accessing Fields in the Result Set
- Cursors
- Getting Column Information
- Stored Procedures
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:
- Description - A user-friendly description of this configuration.
- Driver
- In the odbc.ini file - The name of the section of the odbcinst.ini file that specifies which driver library to use. If an odbc.ini file is being used but no odbcinst.ini file is being used, then this is the file name of the driver library to use.
- In the odbcinst.ini file - The file name of the driver library to use.
- This parameter is not available via the Windows control panel because the driver name is configured during installation and the parameter is hardcoded to the correct value for each instance.
- FileUsage - Always set to 0. 0 indicates that the driver is an RDBMS driver. 1 means that each file is a table (eg. Xbase). 2 means that each file is an entire database (eg. Access). This parameter is not available via the Windows control panel because it is configured during installation.
- Server - The SQL Relay server to connect to. If Server is a comma-separated list of hosts, then an attempt will be made to connect to each until the attempt succeeds, or there are no more hosts left to try.
- Port - The port that the SQL Relay server is listening on. Either port or socket must be specified. If both are specified, socket will be preferred to port.
- Socket - The socket that the SQL Relay server is listening on. Either port or socket must be specified. If both are specified, socket will be preferred to port.
- User - The username to use when logging into SQL Relay.
- Password - The password to use when logging into SQL Relay.
- Tries - If a connection fails, it will be retried this many times.
- RetryTime - If a connection fails, it will be retried on this interval (in seconds).
- Db - The database to switch to when logging into SQL Relay. May be left empty or omitted to accept the default database that SQL Relay is already connected to.
- Debug - If this is set to 1 or to a file name then debug is enabled. If set to a file name then debug will be written to the file. If set to 1 then debug will be written to standard output. Defaults to 0.
- ResultSetBufferSize - Sets the number of rows to buffer at a time. If set to 0 (the default) then the entire result set is buffered.
- NOTE: This parameter is dangerous to set and probably shouldn't be set unless you wrote the application and know that it is safe to set. ResultSetBufferSize basically sets SQL_ROWSET_SIZE, but apps generally assume that SQL_ROWSET_SIZE is set to 1 unless they explicitly set it to something else. If ResultSetBufferSize is set to something other than 0 or 1, and the app assumes that SQL_ROWSET_SIZE is 1 then the app will only fetch the first row of each rowset.
- ColumnNameCase - Indicates the case to convert column names to. May be set to "mixed", "upper", or "lower". Defaults to "mixed".
- DontGetColumnInfo - If this is set to 1 then column info is not fetched with the result set. If set to 0 then column info is fetched. Defaults to 0.
- NullsAsNulls - If this is set to 1 then NULL values are returned as NULLs. If set to 0 then NULL values are returned as empty strings. Defaults to 0.
- LazyConnect - By default, the SQL Relay client does "lazy connects". I.e. the connection to the SQL Relay server isn't established until the first query or other operation is run. This optimizes availability of the connection pool but is inconsistent with most other database API's. Setting this parameter to 0 causes the connection to the SQL Relay server to be established immediately and the constructor to fail, returning undef, if the connection cannot be established. Defaults to 1.
- AutoCommit - Controls whether the driver explicitly turns autocommit on or off after connecting. May be set to yes (autocommit on), no (autocommit off), backend (whatever the backend does, overriding anything set by SQLSetConnectAttr(SQLR_ATTR_AUTOCOMMIT) prior to SQLConnect), or left empty (defer to whatever SQLSetConnectAttr(SQL_ATTR_AUTOCOMMIT) was called with prior to SQLConnect; or whatever the backend does if no such call was made). Defaults to empty.
- MapDateTimeToDate - By default, columns of type DATETIME are mapped to SQL_TIMESTAMP. If set to 1, they are mapped to SQL_DATE instead. Defaults to 0.
- MapDateToTimeStamp - By default, columns of type DATE are mapped to SQL_DATE. If set to 1, they are mapped to SQL_TIMESTAMP instead. Defaults to 0.
- MapNewDateToTimeStamp - By default, columns of type NEWDATE are mapped to SQL_DATE. If set to 1, they are mapped to SQL_TIMESTAMP instead. Defaults to 0.
- BindVariableDelimiters - The set of characters that the client should treat as bind variable markers when scanning queries. Any combination of ?, :, @, and $ may be specified. Defaults to "?:@$" (all four supported). Restrict this if the client must pass one of those characters through to the backend as a literal rather than have it interpreted as a parameter marker.
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.
- Krb - Set to yes to enable Kerberos/AD encryption/authentication or no to disable it.
- Krbservice - Specifies the Kerberos service name of the SQL Relay server. If omitted or left empty then the service name "sqlrelay" will be used. "sqlrelay" is the default service name of the SQL Relay server. Note that on Windows platforms the service name must be fully qualified, including the host and realm name. For example: "sqlrelay/sqlrserver.firstworks.com@AD.FIRSTWORKS.COM".
- Krbmech - Specifies the Kerberos mechanism to use. On Linux/Unix platforms, this should be a string representation of the mechnaism's OID, such as: { 1 2 840 113554 1 2 2 }. On Windows platforms, this should be a string like: Kerberos. If omitted or left empty then the default mechanism will be used. Only set this if you know that you have a good reason to.
- Krbflags - Specifies what Kerberos flags to use. Multiple flags may be specified, separated by commas. If left empty or NULL then a defalt set of flags will be used. Only set this if you know that you have a good reason to. Valid flags include: GSS_C_MUTUAL_FLAG, GSS_C_REPLAY_FLAG, GSS_C_SEQUENCE_FLAG, GSS_C_CONF_FLAG, and GSS_C_INTEG_FLAG. For a full list of flags, consult the GSSAPI documentation, though note that only the previously listed flags are supported on Windows.
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.
- Tls - Set to yes to enable TLS/SSL encryption/authentication or no to disable it.
- Tlsversion - Specifies the TLS/SSL protocol version that the client will attempt to use. Valid values include SSL2, SSL3, TLS1, TLS1.1, TLS1.2 or any more recent version of TLS, as supported by and enabled in the underlying TLS/SSL library. If omittted or left empty then the highest supported version will be negotiated.
- Tlscert - Specifies the file name of the certificate chain file to send to the SQL Relay server. This is only necessary if the SQL Relay server is configured to authenticate and authorize clients by certificate.
- Tlspassword - If Tlscert contains a password-protected private key, then Tlspassword may be supplied to access it. If the private key is not password-protected, then this argument is ignored, and may be omitted or left empty.
- Tlsciphers - Specifies a list of ciphers to allow. Ciphers may be separated by spaces, commas, or colons. If omitted or left empty then a default set is used. For a list of valid ciphers on Linux/Unix platforms, see: man ciphers. For a list of valid ciphers on Windows platforms, see this page. On Windows platforms, the ciphers (alg_id's) should omit CALG_ and may be given with underscores or dashes. For example: 3DES_112. Only set this if you know that you have a good reason to.
- Tlsvalidate - Indicates whether to validate the SQL Relay server's certificate, and may be set to one of the following: no - Don't validate the server's certificate. ca - Validate that the server's certificate was signed by a trusted certificate authority. ca+host - Perform "ca" validation and also validate that one of the subject altenate names (or the common name if no SANs are present) in the certificate matches the host parameter. (Falls back to "ca" validation when a unix socket is used.) ca+domain - Perform "ca" validation and also validate that the domain name of one of the subject alternate names (or the common name if no SANs are present) in the certificate matches the domain name of the host parameter. (Falls back to "ca" validation when a unix socket is used.)
- Tlsca - Specifies the location of a certificate authority file to use, in addition to the system's root certificates, when validating the SQL Relay server's certificate. This is useful if the SQL Relay server's certificate is self-signed. On Windows, "ca" must be a file name. On non-Windows systems, "ca" can be either a file or directory name. If it is a directory name, then all certificate authority files found in that directory will be used. If it a file name, then only that file will be used.
- Tlsdepth - Sets the maximum certificate chain validation depth. The absolute maximum depth is 9. If omitted or left empty then the underlying TLS/SSL library's default is used.
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