/*************************************************************************
** (c) Copyright IBM Corp. 2007 All rights reserved.
**
** The following sample of source code ("Sample") is owned by International
** Business Machines Corporation or one of its subsidiaries ("IBM") and is
** copyrighted and licensed, not sold. You may use, copy, modify, and
** distribute the Sample in any form without payment to IBM, for the purpose of
** assisting you in the development of your applications.
**
** The Sample code is provided to you on an "AS IS" basis, without warranty of
** any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR
** IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Some jurisdictions do
** not allow for the exclusion or limitation of implied warranties, so the above
** limitations or exclusions may not apply to you. IBM shall not be liable for
** any damages you suffer as a result of using, copying, modifying or
** distributing the Sample, even if IBM has been advised of the possibility of
** such damages.
***************************************************************************
**
** SOURCE FILE NAME: simple_xmlproc.c
**
** SAMPLE: Code implementation of a stored procedure Simple_XML_Proc_CLI
**
** The stored procedures defined in this program is called by the client
** application simple_xmlproc_client.c. Before building and running
** simple_xmlproc_client.c, build the shared library by completing the
** following steps:
**
** BUILDING THE SHARED LIBRARY:
** 1. Ensure the Database Manager Configuration file has the keyword
** KEEPFENCED set to "no". This allows shared libraries to be unloaded
** while you are developing stored procedures. You can view the file's
** settings by issuing the command: "db2 get dbm cfg". You can set
** KEEPFENCED to "no" with this command: "db2 update dbm cfg using
** KEEPFENCED no". NOTE: Setting KEEPFENCED to "no" reduces performance
** the performance of accessing stored procedures, because they have
** to be reloaded into memory each time they are called. If this is a
** concern, set KEEPFENCED to "yes", stop and then restart DB2 before
** building the shared library, by entering "db2stop" followed by
** "db2start". This forces DB2 to unload shared libraries and enables
** the build file or the makefile to delete a previous version of the
** shared library from the "sqllib/function" directory.
** 2. To build the shared library, enter "bldrtn simple_xmlproc", or use the
** makefile: "make simple_xmlproc" (UNIX) or "nmake simple_xmlproc" (Windows).
**
** CATALOGING THE STORED PROCEDURES
** 1. The stored procedures are cataloged automatically when you build
** the client application "simple_xmlproc_client" using the appropriate "make"
** utility for your Operating System and the "makefile" provided with these
** samples. If you wish to catalog or recatalog them manually, enter
** "spcat_xml". The spcat_xml script (UNIX) or spcat_xml.bat batch file (Windows)
** connects to the database, runs simple_xmlproc_drop.db2 to uncatalog the stored
** procedures if they were previously cataloged, then runs simple_xmlproc_create.db2
** which catalogs the stored procedures, then disconnects from the database.
**
** CALLING THE STORED PROCEDURES IN THE SHARED LIBRARY:
** 1. Compile the simple_xmlproc_client program with "bldapp simple_xmlproc_client" or use the
** makefile: "make simple_xmlproc_client" (UNIX) or "nmake simple_xmlproc_client" (Windows).
** 2. Run simple_xmlproc_client: "simple_xmlproc_client" (if calling remotely add the parameters
** for database, user ID and password.)
**
** DESCRIPTION OF FUNCTION SIMPLE_PROC:
** This function will take Customer Information ( of type XML) as input ,
** finds whether the customer with Cid in Customer Information exists in the
** customer table or not, if not this will insert the customer information
** into the customer table with same Customer id, and returns all the customers
** from the same city of the input customer information in XML format to the caller
** along with location as an output parameter in XML format.
**
** CLI FUNCTIONS USED:
** SQLAllocHandle -- Allocate Handle
** SQLBindCol -- Bind a Column to an Application Variable or
** LOB locator
** SQLBindParameter -- Bind a Parameter Marker to a Buffer or
** LOB locator
** SQLConnect -- Connect to a Data Source
** SQLDisconnect -- Disconnect from a Data Source
** SQLExecDirect -- Execute a Statement Directly
** SQLExecute -- Execute a Statement
** SQLFetch -- Fetch Next Row
** SQLFreeHandle -- Free Handle Resources
** SQLPrepare -- Prepare a Statement
** SQLSetConnectAttr -- Set Connection Attributes
*****************************************************************************
**
** For more information on the sample programs, see the README file.
**
** For information on developing CLI applications, see the CLI Guide
** and Reference.
**
** For information on using SQL statements, see the SQL Reference.
**
** For the latest information on programming, building, and running DB2
** applications, visit the DB2 Information Center:
** http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlcli1.h>
#include <sqlca.h>
#include <sqludf.h>
#include "utilcli.h"
/* macros for handle checking */
#define SRV_HANDLE_CHECK(htype, hndl, CLIrc, henv, hdbc) \
if (CLIrc == SQL_INVALID_HANDLE) \
{ \
StpCleanUp(henv, hdbc); \
return (0); \
} \
if (CLIrc == SQL_ERROR) \
{ \
StpCleanUp(henv, hdbc); \
return (0); \
}
#define \
SRV_HANDLE_CHECK_SETTING_SQLST(htype, hndl, CLIrc, henv, hdbc, sqlstate) \
If (CLIrc == SQL_INVALID_HANDLE) \
{ \
memset(sqlstate, '0', 6); \
StpCleanUp(henv, hdbc); \
return (0); \
} \
if (CLIrc == SQL_ERROR) \
{ \
StpCleanUp(henv, hdbc); \
return (0); \
}
#define \
SRV_HANDLE_CHECK_SETTING_SQLRC_AND_MSG(htype, \
hndl, \
CLIrc, \
henv, \
hdbc, \
outReturnCode, \
outErrorMsg, \
inMsg) \
if (CLIrc == SQL_INVALID_HANDLE) \
{ \
*outReturnCode = 0; \
strcpy(outErrorMsg, inMsg); \
StpCleanUp(henv, hdbc); \
return (0); \
} \
if (CLIrc == SQL_ERROR) \
{ \
*outReturnCode = -1; \
strcat(outErrorMsg, inMsg); \
StpCleanUp(henv, hdbc); \
return (0); \
}
#define \
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(htype, \
hndl, \
CLIrc, \
henv, \
hdbc, \
sqlstate, \
outMsg, \
inMsg) \
if (CLIrc == SQL_INVALID_HANDLE) \
{ \
memset(sqlstate, '0', 6); \
strcpy(outMsg, inMsg); \
StpCleanUp(henv, hdbc); \
return (0); \
} \
if (CLIrc != 0 && CLIrc != SQL_NO_DATA_FOUND ) \
{ \
SetErrorMsg(htype, hndl, henv, hdbc, outMsg, inMsg); \
StpCleanUp(henv, hdbc); \
return (0); \
}
void StpCleanUp(SQLHANDLE henv, SQLHANDLE hdbc)
{
/* disconnect from a data source */
SQLDisconnect(hdbc);
/* free the database handle */
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
/* free the environment handle */
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
void SetErrorMsg(SQLSMALLINT htype,
SQLHANDLE hndl,
SQLHANDLE henv,
SQLHANDLE hdbc,
char *outMsg,
char *inMsg)
{
SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1];
SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1];
SQLINTEGER sqlcode;
SQLSMALLINT length;
SQLGetDiagRec(htype,
hndl,
1,
sqlstate,
&sqlcode,
message,
SQL_MAX_MESSAGE_LENGTH + 1,
&length);
sprintf(outMsg, "%ld: ", sqlcode);
strcat(outMsg, inMsg);
}
/**************************************************************************
** Stored procedure: simple_proc
**
** Purpose:
** This sample will take Customer Information ( of type XML) as input ,
** finds whether the customer with Cid in Customer Information exists in the
** customer table or not, if not this will insert the customer information
** into the customer table with same Customer id, and returns all the customers
** from the same city of the input customer information in XML format to the caller
** along with location as an output parameter in XML format.
**
** Shows how to:
** - define XML type parameters in a Stored Procedure
** - return a result set to the client
**
** Parameters:
**
** IN: inXML - Customer information an XML document
** OUT: outXML - Location of input customer as an XML document
** Returns Customers from that city to caller.
**
** When the PARAMETER STYLE SQL clause is specified
** in the CREATE PROCEDURE statement for the procedure
** (see the script xquery_xmlproc_create.db2), in addition to the
** parameters passed at procedure invocation time, the
** following parameters are passed to the routine
** in the following order:
** - one null indicator for each IN/INOUT/OUT parameter
** is specified in the same order as the corresponding
** parameter declarations.
** - sqlstate: to be returned to the caller to indicate
** state (output)
** - routine-name: qualified name of the routine (input)
** - specific-name: the specific name of the routine (input)
** - diagnostic-message: an optional text string returned to the
** caller (output)
** See the actual parameter declarations below to see
** the recommended datatypes and sizes for them.
**
** CODE TIP:
** --------
** As an alternative to coding the non-functional parameters
** required with parameter style SQL (sqlstate, routine-name,
** specific-name, diagnostic-message), you can use a macro:
** SQLUDF_TRAIL_ARGS. This macro is defined in DB2 include
** file sqludf.h
**
**************************************************************************/
SQL_API_RC SQL_API_FN simple_proc ( SQLUDF_CLOB* inXML,
SQLUDF_CLOB* outXML,
sqlint16 *inXML_ind,
sqlint16 *outXML_ind,
char sqlstate[6],
char qualName[28],
char specName[19],
char diagMsg[71])
{
SQLHANDLE henv;
SQLHANDLE hdbc = 0;
SQLHANDLE hstmt,hstmt1,hstmt2,hstmt3,hstmt4,hstmt5;
SQLRETURN cliRC;
SQLCHAR stmt[1024],stmt1[1024],stmt2[1024],stmt3[1024],stmt4[1024],stmt5[1024];
SQLINTEGER custid,quantity,count;
char city[100];
/* Initialize output parameters to NULL*/
memset(outXML->data,'\0',5000);
*outXML_ind=-1;
/* intilize the application variables */
quantity=0;
count=0;
custid=0;
cliRC = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
SRV_HANDLE_CHECK(SQL_HANDLE_ENV, henv, cliRC, henv, hdbc);
/* allocate the database handle */
cliRC = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
SRV_HANDLE_CHECK(SQL_HANDLE_ENV, henv, cliRC, henv, hdbc);
/* set AUTOCOMMIT off */
cliRC = SQLSetConnectAttr(hdbc,
SQL_ATTR_AUTOCOMMIT,
SQL_AUTOCOMMIT_OFF,
SQL_NTS);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* issue NULL Connect, because in CLI a statement handle is
required and thus a connection handle and environment handle.
A connection is not established; rather the current
connection from the calling application is used. */
/* connect to a data source */
cliRC = SQLConnect(hdbc, NULL, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* allocate the statement handle */
cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* allocate the statement handle */
cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt1);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* allocate the statement handle */
cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt2);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* allocate the statement handle */
cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt3);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* allocate the statement handle */
cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt4);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* allocate the statement handle */
cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt5);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* find whether the customer with that Info exists in the customer table */
strcpy((char *)stmt,"SELECT COUNT(*) FROM customer WHERE "
"XMLEXISTS('$info/customerinfo[@Cid=$id]' "
"PASSING by ref cast(? as XML) AS \"info\", cid as \"id\")");
cliRC = SQLPrepare(hstmt, stmt, SQL_NTS);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement stmt failed.");
/* bind the input XML document */
cliRC = SQLBindParameter(hstmt,
1,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_XML,
5000,
0,
&(inXML->data),
inXML->length,
(SQLINTEGER *)&(inXML->length));
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SQLBindParameter1");
/* execute the statement */
cliRC = SQLExecute(hstmt);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement failed.");
/* bind the column to an application variable */
cliRC = SQLBindCol(hstmt,
1,
SQL_C_LONG,
&count,
0,
NULL);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt, cliRC, henv, hdbc);
/* fetch a row */
cliRC = SQLFetch(hstmt);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt, cliRC, henv, hdbc);
/* if customer doesn't exist ...... insert into the table */
if(count < 1 )
{
/* get the custid from the customer information */
strcpy((char *)stmt1,"SELECT XMLCAST( XMLQUERY('$info/customerinfo/@Cid' "
"passing by ref cast(? as XML) as \"info\") as "
"BIGINT) FROM SYSIBM.SYSDUMMY1");
cliRC = SQLPrepare(hstmt1, stmt1, SQL_NTS);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt1,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement stmt failed.");
cliRC = SQLBindParameter(hstmt1,
1,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_XML,
5000,
0,
&(inXML->data),
inXML->length,
(SQLINTEGER *)&(inXML->length));
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt1,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SQLBindParameter1");
cliRC = SQLExecute(hstmt1);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt1,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement failed.");
/* get customer id into custid */
cliRC = SQLBindCol(hstmt1,
1,
SQL_C_LONG,
&custid,
0,
NULL);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt1, cliRC, henv, hdbc);
/* fetch a row */
cliRC = SQLFetch(hstmt1);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt1, cliRC, henv, hdbc);
/* insert into customer table with that custid */
strcpy((char *)stmt2,"INSERT INTO customer(Cid, Info) VALUES (?,?)");
cliRC = SQLPrepare(hstmt2, stmt2, SQL_NTS);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt2,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement stmt failed.");
/* bind the parameter to the statement */
cliRC = SQLBindParameter(hstmt2,
1,
SQL_PARAM_INPUT,
SQL_C_LONG,
SQL_INTEGER,
0,
0,
&custid,
0,
NULL);
SRV_HANDLE_CHECK_SETTING_SQLRC_AND_MSG(SQL_HANDLE_STMT,
hstmt2,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SQLBindParameter1");
cliRC = SQLBindParameter(hstmt2,
2,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_XML,
5000,
0,
&(inXML->data),
inXML->length,
(SQLINTEGER *)&(inXML->length));
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt2,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SQLBindParameter1");
cliRC = SQLExecute(hstmt2);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt2,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement failed.");
}
/* find the city of the customer and assign it to an application variable */
strcpy((char *)stmt3,"SELECT XMLCAST( XMLQUERY('$info/customerinfo//city' "
"passing by ref cast(? as XML) as \"info\") as "
"VARCHAR(100)) FROM SYSIBM.SYSDUMMY1");
cliRC = SQLPrepare(hstmt3, stmt3, SQL_NTS);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt3,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement stmt failed.");
cliRC = SQLBindParameter(hstmt3,
1,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_XML,
5000,
0,
&(inXML->data),
inXML->length,
(SQLINTEGER *)&(inXML->length));
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt3,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SQLBindParameter1");
cliRC = SQLExecute(hstmt3);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt3,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SELECT statement failed.");
/* bind a column to an application variable */
cliRC = SQLBindCol(hstmt3, 1, SQL_C_CHAR, city, 100, NULL);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt3, cliRC, henv, hdbc);
/* fetch each row */
cliRC = SQLFetch(hstmt3);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt3, cliRC, henv, hdbc);
/* Location of the input information as XML output */
strcpy((char *)stmt4,"SELECT XMLQUERY('let $city:=$info/customerinfo//city "
"let $prov:=$info/customerinfo//prov-state return <Location> "
"{$city}{$prov} </Location>' passing by ref cast(? as XML) as "
"\"info\") FROM SYSIBM.SYSDUMMY1");
cliRC = SQLPrepare(hstmt4, stmt4, SQL_NTS);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt4,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"statement stmt4 failed.");
cliRC = SQLBindParameter(hstmt4,
1,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_XML,
5000,
0,
&(inXML->data),
inXML->length,
(SQLINTEGER *)&(inXML->length));
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt4,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"SQLBindParameter1");
cliRC = SQLExecute(hstmt4);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt4,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"EXECUTE statement failed.");
/* bind a column to an application variable */
cliRC = SQLBindCol(hstmt4,
1,
SQL_C_CHAR,
&(outXML->data),
5000,
(SQLINTEGER*)&(outXML->length));
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt4, cliRC, henv, hdbc);
/* fetch a row */
cliRC = SQLFetch(hstmt4);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt4, cliRC, henv, hdbc);
*outXML_ind = 0;
/* findout all he customers from that city and return as an XML to caller */
strcpy((char *)stmt5,"XQUERY for $cust in db2-fn:xmlcolumn"
"(\"CUSTOMER.INFO\")/customerinfo/addr[city=\"");
strcat((char *)stmt5, city);
strcat((char *)stmt5, "\"] order by xs:double($cust/../@Cid) return <Customer>{$cust/../@Cid}{$cust/../name}</Customer>");
cliRC = SQLPrepare(hstmt5, stmt5, SQL_NTS);
SRV_HANDLE_CHECK_SETTING_SQLST_AND_MSG(SQL_HANDLE_STMT,
hstmt5,
cliRC,
henv,
hdbc,
sqlstate,
diagMsg,
"XQUERY statement failed.");
cliRC = SQLExecute(hstmt5);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt5, cliRC, henv, hdbc);
/* free the handles */
cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt, cliRC, henv, hdbc);
cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt1);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt1, cliRC, henv, hdbc);
cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt2);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt2, cliRC, henv, hdbc);
cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt3);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt3, cliRC, henv, hdbc);
cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt4);
SRV_HANDLE_CHECK(SQL_HANDLE_STMT, hstmt4, cliRC, henv, hdbc);
/* disconnect from the data source */
cliRC = SQLDisconnect(hdbc);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* free the database handle */
cliRC = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
SRV_HANDLE_CHECK(SQL_HANDLE_DBC, hdbc, cliRC, henv, hdbc);
/* free the environment handle */
cliRC = SQLFreeHandle(SQL_HANDLE_ENV, henv);
SRV_HANDLE_CHECK(SQL_HANDLE_ENV, henv, cliRC, henv, hdbc);
return (0);
}