/************************************************************************* ** (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: spserver.sqc ** ** SAMPLE: Code implementations of various types of stored procedures ** ** The stored procedures defined in this program are called by the ** client application spclient.sqc. Before building and running ** spclient.sqc, 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 spserver", or use the ** makefile: "make spserver" (UNIX) or "nmake spserver" (Windows). ** ** CATALOGING THE STORED PROCEDURES ** 1. The stored procedures are cataloged automatically when you build ** the client application "spclient" 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". The spcat script (UNIX) or spcat.bat batch file (Windows) ** connects to the database, runs spdrop.db2 to uncatalog the stored ** procedures if they were previously cataloged, then runs spcreate.db2 ** which catalogs the stored procedures, then disconnects from the ** database. ** ** CALLING THE STORED PROCEDURES IN THE SHARED LIBRARY: ** 1. Compile the spclient program with "bldapp spclient" or use the ** makefile: "make spclient" (UNIX) or "nmake spclient" (Windows). ** 2. Run spclient: "spclient" (if calling remotely add the parameters for ** database, user ID and password.) ** ** SQL STATEMENTS USED: ** CLOSE ** DECLARE ** FETCH ** FREE LOCATOR ** OPEN ** SELECT ** SELECT INTO ** UPDATE ** VALUES ** WHENEVER ** ** STRUCTURES USED: ** sqlca ** sqlda ** ** EXTERNAL DEPENDENCIES: ** This program must be built on a DB2 server. ** Ensure existence of the sample database. ** Precompile with the SQL precompiler (PREP in DB2) ** Bind to a database (BIND in DB2) ** Compile and link loop with the compiler supported on your ** platform. ** ** spclient spclient *************************************************************************** ** ** For more information on the sample programs, see the README file. ** ** For information on developing C applications, see the Application ** Development Guide. ** ** 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 <sqlda.h> #include <sqlca.h> #include <sqludf.h> #include <sql.h> #include <memory.h> /* NOTE: ----- If a routine (stored procedure or user-defined function (UDF)) calls another routine within the same library, you must declare the function prototypes here (above the actual routine implementations). This is NOT required for this library, but it is important to note. */ /* a description of each routine and its parameters is provided with the code body of each routine in this file (see below). */ /************************************************************************* Stored Procedure: OutLanguage Purpose: Returns the code implementation language of routine 'OutLanguage' (as it appears in the database catalog) in an output parameter. Shows how to: - define an OUT parameter in PARAMETER STYLE SQL - define a NULL indicator for the parameter - execute an SQL statement - how to set a Null indicator when parameter is not null Parameters: IN: (none) OUT: outLanguage - the code language of this routine When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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 TIP EXAMPLE: ------------ The following stored procedure prototype is equivalent to the actual prototype implementation for this stored procedure. It is simpler to implement. See stored procedure sample TwoResultSets in this file to see the SQLUDF_TRAIL_ARGS macro in use. SQL_API_RC SQL_API_FN OutLanguage( char outLanguage[9], sqlint16 *outLanguageNullInd, SQLUDF_TRAIL_ARGS) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. **************************************************************************/ SQL_API_RC SQL_API_FN OutLanguage(char outLanguage[9], /* CHAR(8) */ sqlint16 *outLanguageNullInd, char sqlstate[6], char qualName[28], char specName[19], char diagMsg[71]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; char hvOutLangLanguage[9]; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* initialize output parameter string to NULL */ memset(outLanguage, '\0', 9); *outLanguageNullInd = -1; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); EXEC SQL WHENEVER SQLERROR GOTO return_error; strcpy(sqlStmtInfo, "SELECT statement failed"); /* select the implementation language of this routine from the DB2 catalog table sysibm.sysprocedures */ EXEC SQL SELECT language INTO :hvOutLangLanguage FROM sysibm.sysprocedures WHERE procname = 'OUT_LANGUAGE'; /* copy value of hvLanguage to output parameter language */ strcpy(outLanguage, hvOutLangLanguage); /* set NULL indicator for parameter 'outLanguage' to 0 to indicate that output parameter 'outLanguage' is not NULL. When the value to be returned is intended to be a NULL, set the null indicator for that parameter to -1 */ *outLanguageNullInd = 0; return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy(sqlstate, sqlca.sqlstate); sprintf(diagMsg, "%d", sqlca.sqlcode); strcat(diagMsg, ": "); strcat(diagMsg, sqlStmtInfo); return (0); } } /* OutLanguage function */ /************************************************************************** Stored Procedure: OutParam Purpose: Sorts table STAFF by salary, locates and returns the median salary Shows how to: - define OUT parameters in PARAMETER STYLE SQL - execute SQL to declare and work with a cursor - how to set a Null indicator when parameter is not null - define the extra parameters associated with PARAMETER STYLE SQL Parameters: IN: (none) OUT: outMedianSalary - median salary in table STAFF When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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 TIP EXAMPLE: ------------ The following stored procedure prototype is equivalent to the actual prototype implementation for this stored procedure. It is simpler to implement. See stored procedure sample TwoResultSets in this file to see the SQLUDF_TRAIL_ARGS macro in use. SQL_API_RC SQL_API_FN OutParam( double *outMedianSalary, sqlint16 *outMedianSalaryNullInd, SQLUDF_TRAIL_ARGS) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. **********************************************************************/ SQL_API_RC SQL_API_FN OutParam(double *outMedianSalary, sqlint16 *outMedianSalaryNullInd, char sqlstate[6], char qualName[28], char specName[19], char diagMsg[71]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; sqlint32 hvOutParamNumRecords; double hvOutParamSalary; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* local variable */ int counter = 0; /* initialize output parameter */ *outMedianSalary = 0; *outMedianSalaryNullInd = -1; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); EXEC SQL WHENEVER SQLERROR GOTO return_error; strcpy(sqlStmtInfo, "DECLARE statement failed"); EXEC SQL DECLARE curOutParam CURSOR FOR SELECT CAST(salary AS DOUBLE) FROM staff ORDER BY salary; strcpy(sqlStmtInfo, "SELECT statement failed"); EXEC SQL SELECT COUNT(*) INTO: hvOutParamNumRecords FROM staff; strcpy(sqlStmtInfo, "OPEN statement failed"); EXEC SQL OPEN curOutParam; strcpy(sqlStmtInfo, "FETCH statement failed"); while (counter < (hvOutParamNumRecords / 2 + 1)) { EXEC SQL FETCH curOutParam INTO :hvOutParamSalary; counter = counter + 1; } /* set value of OUT parameter to host variable */ *outMedianSalary = hvOutParamSalary; *outMedianSalaryNullInd = 0; strcpy(sqlStmtInfo, "CLOSE statement failed"); EXEC SQL CLOSE curOutParam; return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy(sqlstate, sqlca.sqlstate); sprintf(diagMsg, "%d", SQLCODE); strcat(diagMsg, ": "); strcat(diagMsg, sqlStmtInfo); return (0); } } /* OutParam function */ /************************************************************************** Stored Procedure: InParams Purpose: Updates salaries of employees in department indept using inputs inLowSal, inMedSal, inHighSal as salary raise or adjustment values. Shows how to: - define IN parameters using PARAMETER STYLE SQL - define and use NULL indicators for parameters - define the extra parameters associated with PARAMETER STYLE SQL Parameters: IN: inLowSal - new salary for low salary employees inMedSal - new salary for mid salary employees inHighSal - new salary for high salary employees inDept - department to use in SELECT predicate OUT: (none) When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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 TIP EXAMPLE: ------------ The following stored procedure prototype is equivalent to the actual prototype implementation for this stored procedure. It is simpler to implement. See stored procedure sample TwoResultSets in this file to see the SQLUDF_TRAIL_ARGS macro in use. SQL_API_RC SQL_API_FN InParams( double *inLowSal, double *inMedSal, double *inHighSal, char inDept[4], sqlint16 *inLowSalNullInd, sqlint16 *inMedSalNullInd, sqlint16 *inHighSalNullInd, sqlint16 *inDeptNullInd, SQLUDF_TRAIL_ARGS) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN InParams(double *inLowSal, double *inMedSal, double *inHighSal, char inDept[4], /* CHAR(3) */ sqlint16 *inLowSalNullInd, sqlint16 *inMedSalNullInd, sqlint16 *inHighSalNullInd, sqlint16 *inDeptNullInd, char sqlstate[6], char qualName[28], char specName[19], char diagMsg[71]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; double hvInParamLowSal; double hvInParamMedSal; double hvInParamHighSal; double hvInParamSalary; char hvInParamDept[4]; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); EXEC SQL WHENEVER SQLERROR GOTO return_error; if ((*inLowSalNullInd) < 0 || (*inMedSalNullInd) < 0 || (*inHighSalNullInd) < 0 || (*inDeptNullInd) < 0) { /* set custom sqlstate to return to client. */ strcpy(sqlstate, "38100"); /* set custom message to return to client. Note that although the OUT parameter is declared as CHAR(70), DB2 prepends the procedure name and shared library entry point to the message. Keep the custom message short to avoid truncation. */ strcpy(diagMsg, "Received null input."); return(0); } /* copy input parameters to local host variables */ hvInParamLowSal = *inLowSal; hvInParamMedSal = *inMedSal; hvInParamHighSal = *inHighSal; strcpy(hvInParamDept, inDept); strcpy(sqlStmtInfo, "DECLARE statement failed"); EXEC SQL DECLARE curInParam CURSOR FOR SELECT CAST(salary AS DOUBLE) FROM employee WHERE workdept = :hvInParamDept FOR UPDATE; strcpy(sqlStmtInfo, "OPEN statement failed"); EXEC SQL OPEN curInParam; strcpy(sqlStmtInfo, "FETCH statement failed"); EXEC SQL FETCH curInParam INTO :hvInParamSalary; if (sqlca.sqlcode == 100) { strcpy(sqlstate, "38200"); /* fetch returned no data, so exit the stored procedure */ strcpy(diagMsg, " 100: NO DATA FOUND"); strcpy(sqlStmtInfo, "CLOSE statement failed"); /* close cursor before exiting */ EXEC SQL CLOSE curInParam; return (0); } while (sqlca.sqlcode == 0) { strcpy(sqlStmtInfo, "UPDATE statement failed"); if (hvInParamLowSal > hvInParamSalary) { EXEC SQL UPDATE employee SET salary = :hvInParamLowSal WHERE CURRENT OF curInParam; } else if (hvInParamMedSal > hvInParamSalary) { EXEC SQL UPDATE employee SET salary = :hvInParamMedSal WHERE CURRENT OF curInParam; } else if (hvInParamHighSal > hvInParamSalary) { EXEC SQL UPDATE employee SET salary = :hvInParamHighSal WHERE CURRENT OF curInParam; } else { EXEC SQL UPDATE employee SET salary = :hvInParamSalary * 1.10 WHERE CURRENT OF curInParam; } strcpy(sqlStmtInfo, "FETCH statement failed"); EXEC SQL FETCH curInParam INTO: hvInParamSalary; } strcpy(sqlStmtInfo, "CLOSE statement failed"); EXEC SQL CLOSE curInParam; return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy(sqlstate, sqlca.sqlstate); sprintf(diagMsg, "%d", sqlca.sqlcode); strcat(diagMsg, ": "); strcat(diagMsg, sqlStmtInfo); return (0); } } /* InParams function */ /************************************************************************** Stored Procedure: InOutParam Purpose: Calculates the median salary of all salaries above the input median salary. Shows how to: - define an INOUT parameter using PARAMETER STYLE SQL - define and use NULL indicators for parameters - define the extra parameters associated with PARAMETER STYLE SQL Parameters: IN/OUT: inOutMedian - median salary input value used in SELECT predicate output set to median salary found When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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 TIP EXAMPLE: ------------ The following stored procedure prototype is equivalent to the actual prototype implementation for this stored procedure. It is simpler to implement. See stored procedure sample TwoResultSets in this file to see the SQLUDF_TRAIL_ARGS macro in use. SQL_API_RC SQL_API_FN InOutParam(double *inOutMedian, sqlint16 *inOutMedianNullInd, SQLUDF_TRAIL_ARGS) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN InOutParam(double *inOutMedian, sqlint16 *inOutMedianNullInd, char sqlstate[6], char qualName[28], char specName[19], char diagMsg[71]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* each host variable name must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; double hvInOutParamMedianSal; sqlint32 hvInOutParamNumRecords; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* local variable declaration */ int counter; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); EXEC SQL WHENEVER SQLERROR GOTO return_error; if ((*inOutMedianNullInd) < 0) { /* set custom sqlstate to return to client. */ strcpy(sqlstate, "38100"); /* set the null indicator */ *inOutMedianNullInd = -1; /* set custom message to return to client. Note that although the OUT parameter is declared as CHAR(70), DB2 prepends the procedure name and shared library entry point to the message. Keep the custom message short to avoid truncation. */ strcpy(diagMsg, "Received null input."); return (0); } if ((*inOutMedian) < 0) { /* set custom sqlstate to return to client. */ strcpy(sqlstate, "38100"); /* set the null indicator */ *inOutMedianNullInd = -1; strcpy(diagMsg, "Received invalid input."); return (0); } counter = 0; hvInOutParamMedianSal = *inOutMedian; strcpy(sqlStmtInfo, "DECLARE statement failed"); EXEC SQL DECLARE curInOutParam CURSOR FOR SELECT CAST(salary AS DOUBLE) FROM staff WHERE salary > :hvInOutParamMedianSal ORDER BY salary; strcpy(sqlStmtInfo, "SELECT statement failed"); EXEC SQL SELECT COUNT(*) INTO :hvInOutParamNumRecords FROM staff WHERE salary > :hvInOutParamMedianSal; if (hvInOutParamNumRecords > 0) /* at least one record was found */ { strcpy(sqlStmtInfo, "OPEN statement failed"); EXEC SQL OPEN curInOutParam USING :hvInOutParamMedianSal; strcpy(sqlStmtInfo, "FETCH statement failed"); while (counter < (hvInOutParamNumRecords / 2 + 1)) { EXEC SQL FETCH curInOutParam INTO :hvInOutParamMedianSal; counter = counter + 1; } /* set value of INOUT parameter to host variable */ *inOutMedian = hvInOutParamMedianSal; strcpy(sqlStmtInfo, "CLOSE statement failed"); EXEC SQL CLOSE curInOutParam; } else /* no records were found */ { *inOutMedianNullInd = -1; /* return the custom error state and error message to client */ strcpy(sqlstate, "38200"); strcpy(diagMsg, "100: NO DATA FOUND"); } return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy(sqlstate, sqlca.sqlstate); sprintf(diagMsg, "%d", sqlca.sqlcode); strcat(diagMsg, ": "); strcat(diagMsg, sqlStmtInfo); return (0); } } /* InOutParam function */ /************************************************************************** Stored Procedure: ExtractFromClob Purpose: Extracts department information from a large object (LOB) resume of employee data returns this information to the caller in output parameter outDeptInfo. Shows how to: - define IN and OUT parameters in STYLE SQL - define a local lob locator variable - locate information within a formatted lob - extract information from within a clob/copy it to a host variable Parameters: IN: inEmpNumber - employee number OUT: outDeptInfo - department information section of the employee's resume When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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 TIP EXAMPLE: ------------ The following stored procedure prototype is equivalent to the actual prototype implementation for this stored procedure. It is simpler to implement. See stored procedure sample TwoResultSets in this file to see the SQLUDF_TRAIL_ARGS macro in use. SQL_API_RC SQL_API_FN ExtractFromClob( char inEmpNumber[7], char outDeptInfo[1001], sqlint16 *inEmpNumberNullInd, sqlint16 *outDeptInfoNullInd, SQLUDF_TRAIL_ARGS) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN ExtractFromClob(char inEmpNumber[7], /* CHAR(6) */ char outDeptInfo[1001], /* VARCHAR(1000) */ sqlint16 *inEmpNumberNullInd, sqlint16 *outDeptInfoNullInd, char sqlstate[6], char qualName[28], char specName[19], char diagMsg[71]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; char hvExtFromClobEmpNum[7]; char hvExtFromClobResume[1001]; sqlint32 hvExtFromClobDeptInfoBeginLoc; sqlint32 hvExtFromClobDeptInfoEndLoc; SQL TYPE IS CLOB_LOCATOR locExtFromClobResume; SQL TYPE IS CLOB_LOCATOR locExtFromClobDept; sqlint16 hvExtFromClobDeptLobInd; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); /* initialize output parameters (strings set to NULL) */ memset(hvExtFromClobResume, '\0', 1001); memset(outDeptInfo, '\0', 1001); *outDeptInfoNullInd = -1; EXEC SQL WHENEVER SQLERROR GOTO return_error; /* copy input into local host variable */ strcpy(hvExtFromClobEmpNum, inEmpNumber); strcpy(sqlStmtInfo, "SELECT statement failed"); EXEC SQL SELECT resume INTO :locExtFromClobResume:hvExtFromClobDeptLobInd FROM emp_resume WHERE resume_format = 'ascii' AND empno =:hvExtFromClobEmpNum; if (hvExtFromClobDeptLobInd < 0) { /* set custom sqlstate to return to client. */ strcpy(sqlstate, "38200"); /* set custom message to return to client. Note that although the OUT parameter is declared as CHAR(70), DB2 prepends the procedure name and shared library entry point to the message. Keep the custom message short to avoid truncation. */ strcpy(diagMsg, "NULL value returned for CLOB.\n"); } else { /* locate the beginning of the 'Department Information' section of the lob resume */ strcpy(sqlStmtInfo, "VALUES statement failed(1)"); EXEC SQL VALUES(POSSTR(:locExtFromClobResume, 'Department Information')) INTO :hvExtFromClobDeptInfoBeginLoc; /* locate the beginning of the 'Education' section - this marks the end of the 'Department Information' section of the lob resume */ strcpy(sqlStmtInfo, "VALUES statement failed(2)"); EXEC SQL VALUES(POSSTR(:locExtFromClobResume, 'Education')) INTO :hvExtFromClobDeptInfoEndLoc; /* obtain ONLY the "Department Information" section by using SUBSTR */ strcpy(sqlStmtInfo, "VALUES statement failed(3)"); EXEC SQL VALUES(SUBSTR(:locExtFromClobResume, :hvExtFromClobDeptInfoBeginLoc, :hvExtFromClobDeptInfoEndLoc - :hvExtFromClobDeptInfoBeginLoc)) INTO:locExtFromClobDept; /* append the "Department Information" section to the :out_resume var. */ strcpy(sqlStmtInfo, "VALUES statement failed(4)"); EXEC SQL VALUES(:hvExtFromClobResume || :locExtFromClobDept) INTO :hvExtFromClobResume; strcpy(outDeptInfo, hvExtFromClobResume); *outDeptInfoNullInd = 0; } /* endif */ strcpy(sqlStmtInfo, "FREE statement failed"); EXEC SQL FREE LOCATOR :locExtFromClobResume, :locExtFromClobDept; return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy(sqlstate, sqlca.sqlstate); sprintf(diagMsg, "%d", sqlca.sqlcode); strcat(diagMsg, ": "); strcat(diagMsg, sqlStmtInfo); return (0); } } /* ExtractFromClob function */ /************************************************************************** Stored Procedure: DbinfoExample Purpose: This routine takes in a job type and returns the average salary of all employees with that job, as well as information about the database (name, version of database). The database information is retrieved from the dbinfo object. Shows how to: - define IN/ OUT parameters in PARAMETER STYLE SQL - declare a parameter pointer to the dbinfo structure - retrieve values from the dbinfo structure Parameters: IN: inJob - a job type, used in a SELECT predicate OUT: outSalary - average salary of employees with job specified by injob outDbName - database name retrieved from DBINFO outDbVersion - database version retrieved from DBINFO sqludf_dbinfo - pointer to DBINFO structure When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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 TIP EXAMPLE: ------------ The following stored procedure prototype is equivalent to the actual prototype implementation for this stored procedure. It is simpler to implement. See stored procedure sample TwoResultSets in this file to see the SQLUDF_TRAIL_ARGS macro in use. SQL_API_RC SQL_API_FN DbinfoExample(char inJob[9], double *outSalary, char outDbName[129], char outDbVersion[9], sqlint16 *inJobNullInd, sqlint16 *outSalaryNullInd, sqlint16 *outDbNameNullInd, sqlint16 *outDbVersionNullInd, SQLUDF_TRAIL_ARGS, struct sqludf_dbinfo * dbinfo) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN DbinfoExample(char inJob[9], /* CHAR(8) */ double *outSalary, char outDbName[129], /* CHAR(128) */ char outDbVersion[9], /* CHAR(8) */ sqlint16 *inJobNullInd, sqlint16 *outSalaryNullInd, sqlint16 *outDbNameNullInd, sqlint16 *outDbVersionNullInd, char sqlstate[6], char qualName[28], char specName[19], char diagMsg[71], struct sqludf_dbinfo *dbinfo) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; char hvDbinfoJob[9]; double hvDbinfoSalary; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); /* initialize output parameters - set strings to NULL */ memset(outDbName, '\0', 129); memset(outDbVersion, '\0', 9); *outSalary = 0; *outSalaryNullInd = -1; *outDbNameNullInd = -1; *outDbVersionNullInd = -1; EXEC SQL WHENEVER SQLERROR GOTO return_error; if ((*inJobNullInd) < 0) { /* set custom sqlstate to return to client. */ strcpy(sqlstate, "38100"); /* set custom message to return to client. Note that although the OUT parameter is declared as CHAR(70), DB2 prepends the procedure name and shared library entry point to the message. Keep the custom message short to avoid truncation. */ strcpy(diagMsg, "Received null input."); *outSalaryNullInd = -1; } else { /* copy input parameter into local host variable */ strcpy(hvDbinfoJob, inJob); strcpy(sqlStmtInfo, "SELECT statement failed"); EXEC SQL SELECT AVG(salary) INTO :hvDbinfoSalary FROM employee WHERE job = :hvDbinfoJob; *outSalary = hvDbinfoSalary; *outSalaryNullInd = 0; } /* copy values from the DBINFO structure into the output parameters You must explicitly null-terminate the strings. Information such as the database name, and the version of the database product can be found in the DBINFO structure as well as other information fields. */ strncpy(outDbName, (char *)(dbinfo->dbname), dbinfo->dbnamelen); outDbName[dbinfo->dbnamelen] = '\0'; strncpy(outDbVersion, (char *)(dbinfo->ver_rel), 8); outDbVersion[8] = '\0'; *outDbNameNullInd = 0; *outDbVersionNullInd = 0; return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy(sqlstate, sqlca.sqlstate); sprintf(diagMsg, "%d", sqlca.sqlcode); strcat(diagMsg, ": "); strcat(diagMsg, sqlStmtInfo); return (0); } } /* DbinfoExample function */ /************************************************************************** Stored Procedure: MainExample Purpose: Returns the average salary of employees in table employee that have the job specified by argv[1] Shows how to: - use standard argc and argv parameters to a main C routine to pass parameters in and out - define IN parameters using PARAMETER STYLE SQL - define and use NULL indicators for parameters - define the extra parameters associated with PARAMETER STYLE SQL Parameters: IN: argc - count of the number of parameters argv[1] - job type (char[8]) OUT: argv[2] - average salary of employees with that job (double) When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN MainExample(int argc, char **argv) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; char hvMainJob[9]; double hvMainSalary; EXEC SQL END DECLARE SECTION; /* note: argv[0]: program name argv[1]: job type (char[8], input) argv[2]: average salary (double, output) argv[3]: null indicator for job type argv[4]: null indicator for average salary argv[5]: sqlstate (char[6], output) argv[6]: qualName (char[28], output) argv[7]: specName (char[19], output) argv[8]: diagMsg (char[71], output) */ /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; char sqlCode[10]; /* initialize ouput null indicator */ *(sqlint16 *)argv[4] = -1; strcpy(sqlStmtInfo, "\0"); strcpy(sqlCode, "\0"); /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); EXEC SQL WHENEVER SQLERROR GOTO return_error; /* check the null indicator variable for the input parameter */ if (*(sqlint16 *)argv[3] < 0) { /* set custom SQLSTATE to return to client. */ strcpy((char *)argv[5], "38100"); /* set custom message to return to client. Note that although the OUT parameter is declared as CHAR(70), DB2 prepends the procedure name and shared library entry point to the message. Keep the custom message short to avoid truncation. */ strcpy((char *)argv[8], "Received null input."); /* set the null indicator variable for the output parameter to indicate a null value */ *(sqlint16 *)argv[4] = -1; return (0); } else { /* argv[0] contains the procedure name, so parameters start at argv[1] */ strcpy(hvMainJob, (char *)argv[1]); strcpy(sqlStmtInfo, "SELECT statement failed"); EXEC SQL SELECT AVG(salary) INTO :hvMainSalary FROM employee WHERE job = :hvMainJob; /* set the output parameter values including the sqlca.sqlcode */ memcpy((double *)argv[2], (double *)&hvMainSalary, sizeof(double)); /* set the null indicator variable for the output parameter */ /* to indicate a non-null value */ *(sqlint16 *)argv[4] = 0; } return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy((char *)argv[5], sqlca.sqlstate); sprintf(sqlCode, "%d", sqlca.sqlcode); strcat(sqlCode, " : "); strcpy((char *)argv[8], sqlCode); strcat((char *)argv[8], sqlStmtInfo); return (0); } } /* MainExample function */ /************************************************************************** Embedded C/C++ stored procedures do not provide direct support for DECIMAL data type. The following programming languages can be used to directly manipulate DECIMAL type: - JDBC - SQLJ - SQL routines - .NET common language runtime languages (C#, Visual Basic) Please see the SpServer implementation for one of the above language to see this functionality. ***************************************************************************/ /************************************************************************** Stored Procedure: AllDataTypes Purpose: Take each parameter and set it to a new output value. This sample shows only a subset of DB2 supported data types. For a full listing of DB2 data types, please see the SQL Reference. Shows how to: - define INOUT/OUT parameters in PARAMETER STYLE SQL - declare host variables and assign values to them - assign output values to INOUT/OUT parameters Parameters: INOUT: inOutSmall, inOutInt, inOutBig, inOutReal, outDouble OUT: outChar, outChars, outVarchar, outDate, outTime When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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) In this example we use macro SQLUDF_TRAIL_ARGS (defined in sqludf.h - this file must be included) to replace the 4 'extra' parameters: sqlstate, qualified routine name, specific name of routine, diagnostic string. When referencing the 'extra' parameters, use the parameter names provided in the macro definition in sqludf.h: sqludf_sqlstate sqludf_fname sqludf_fspecname sqludf_msgtext Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN AllDataTypes(sqlint16 *inOutSmall, sqlint32 *inOutInt, sqlint64 *inOutBig, float *inOutReal, double *inOutDouble, char outChar[2], /* CHAR(1) */ char outChars[16], /* CHAR(15) */ char outVarchar[13], /* VARCHAR(13) */ char outDate[11], /* DATE */ char outTime[9], /* TIME */ sqlint16 *inOutSmallNullInd, sqlint16 *inOutIntNullInd, sqlint16 *inOutBigNullInd, sqlint16 *inOutRealNullInd, sqlint16 *inOutDoubleNullInd, sqlint16 *outCharNullInd, sqlint16 *outCharsNullInd, sqlint16 *outVarcharNullInd, sqlint16 *outDateNullInd, sqlint16 *outTimeNullInd, SQLUDF_TRAIL_ARGS) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; char hvAllDataTypesChar[2]; char hvAllDataTypesChars[16]; char hvAllDataTypesVarchar[13]; char hvAllDataTypesDate[11]; char hvAllDataTypesTime[9]; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); /* initialize the OUT parameters and Null indicators */ memset(outChar, '\0', 2); memset(outChars, '\0', 16); memset(outVarchar, '\0', 13); memset(outDate, '\0', 11); memset(outTime, '\0', 9); *outCharNullInd = -1; *outCharsNullInd = -1; *outVarcharNullInd = -1; *outDateNullInd = -1; *outTimeNullInd = -1; EXEC SQL WHENEVER SQLERROR GOTO return_error; if (*inOutSmall == 0) { *inOutSmall = 1; } else { *inOutSmall = (*inOutSmall / 2); } if (*inOutInt == 0) { *inOutInt = 1; } else { *inOutInt = (*inOutInt / 2); } if (*inOutBig == 0) { *inOutBig = 1; } else { *inOutBig = (*inOutBig / 2); } if (*inOutReal == 0) { *inOutReal = 1; } else { *inOutReal = (*inOutReal / 2); } if (*inOutDouble == 0) { *inOutDouble = 1; } else { *inOutDouble = (*inOutDouble / 2); } strcpy(sqlStmtInfo, "SELECT midinit,... failed"); EXEC SQL SELECT midinit, lastname, firstnme INTO :hvAllDataTypesChar, :hvAllDataTypesChars, :hvAllDataTypesVarchar FROM employee WHERE empno = '000180'; strcpy(outChar, hvAllDataTypesChar); strcpy(outChars, hvAllDataTypesChars); strcpy(outVarchar, hvAllDataTypesVarchar); *outCharNullInd = 0; *outCharsNullInd = 0; *outVarcharNullInd = 0; strcpy(sqlStmtInfo, "VALUES statement failed"); EXEC SQL VALUES CURRENT DATE INTO :hvAllDataTypesDate; strcpy(outDate, hvAllDataTypesDate); *outDateNullInd = 0; strcpy(sqlStmtInfo, "VALUES statement failed"); EXEC SQL VALUES CURRENT TIME INTO :hvAllDataTypesTime; strcpy(outTime, hvAllDataTypesTime); *outTimeNullInd = 0; return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { /* set custom sqlstate to return to client. */ strcpy(sqludf_sqlstate, sqlca.sqlstate); sprintf(sqludf_msgtext, "%d", sqlca.sqlcode); strcat(sqludf_msgtext, " : "); strcat(sqludf_msgtext, sqlStmtInfo); return (0); } } /* AllDataTypes function */ /************************************************************************** Stored Procedure: OneResultSetToClient Purpose: Returns a result set to the caller that identifies employees with salaries greater than the value of input parameter inSalary. Shows how to: - define IN and OUT parameters in STYLE SQL - define and use NULL indicators for parameters - define the extra parameters associated with PARAMETER STYLE SQL - return a result set to the client Note: Extraction of result sets is not supported from C/C++ clients Parameters: IN: inSalary - salary When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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 TIP EXAMPLE: ------------ The following stored procedure prototype is equivalent to the actual prototype implementation for this stored procedure. It is simpler to implement. See stored procedure sample TwoResultSets in this file to see the SQLUDF_TRAIL_ARGS macro in use. SQL_API_RC SQL_API_FN OneResultSetToClient(double *inSalary, sqlint16 *inSalaryNullInd, SQLUDF_TRAIL_ARGS) Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN OneResultSetToClient(double *inSalary, sqlint16 *inSalaryNullInd, char sqlstate[6], char qualName[28], char specName[19], char diagMsg[71]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; double hvOneResultSetSalary; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); EXEC SQL WHENEVER SQLERROR GOTO return_error; if ((*inSalaryNullInd) < 0) { /* set custom sqlstate to return to client. */ strcpy(sqlstate, "38100"); /* set custom message to return to client. Note that although the OUT parameter is declared as CHAR(70), DB2 prepends the procedure name and shared library entry point to the message. Keep the custom message short to avoid truncation. */ strcpy(diagMsg, "Received null input."); return (0); } hvOneResultSetSalary = *inSalary; strcpy(sqlStmtInfo, "DECLARE statement failed"); EXEC SQL DECLARE curOneResultSet CURSOR FOR SELECT name, job, CAST(salary AS DOUBLE) FROM staff WHERE salary > :hvOneResultSetSalary ORDER BY salary; strcpy(sqlStmtInfo, "OPEN statement failed"); /* leave cursor open to return result set */ EXEC SQL OPEN curOneResultSet; return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { strcpy(sqlstate, sqlca.sqlstate); sprintf(diagMsg, "%d", sqlca.sqlcode); strcat(diagMsg, ": "); strcat(diagMsg, sqlStmtInfo); return (0); } } /* OneResultSetToClient function */ /************************************************************************** Stored Procedure: TwoResultSets Purpose: Returns two result sets to the caller. One result set consists of employee data of all employees with salaries less than inMedianSalary. The other result set contains employee data for employees with salaries greater than inMedianSalary. Shows how to: - define IN and OUT parameters in STYLE SQL - define and use NULL indicators for parameters - define the extra parameters associated with PARAMETER STYLE SQL - return more than 1 result set to the client Note: Extraction of result sets is not supported from C/C++ clients Parameters: IN: inMedianSalary - salary When the PARAMETER STYLE SQL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.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) CODE TIP: ======== See the parameter declarations in stored procedure example OutLanguage for datatypes and sizes for these. In this example we use macro SQLUDF_TRAIL_ARGS (defined in sqludf.h - this file must be included) to replace the 4 'extra' parameters: sqlstate, qualified routine name, specific name of routine, diagnostic string. When referencing the 'extra' parameters, use the parameter names provided in the macro definition in sqludf.h: sqludf_sqlstate sqludf_fname sqludf_fspecname sqludf_msgtext Note: With parameter style SQL it is mandatory to declare either the four non-functional parameters (sqlstate, routine-name, specific-name, diagnostic-message) or SQLUDF_TRAIL_ARGS macro. ***************************************************************************/ SQL_API_RC SQL_API_FN TwoResultSets(double *inMedianSalary, sqlint16 *inMedianSalaryNullInd, SQLUDF_TRAIL_ARGS) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; double hvTwoResultSetsSalary; EXEC SQL END DECLARE SECTION; /* text information appended to diagMsg upon the event of an error */ char sqlStmtInfo[60]; /* initialize sqlStmtInfo to NULL */ memset(sqlStmtInfo, '\0', 60); EXEC SQL WHENEVER SQLERROR GOTO return_error; if (*inMedianSalaryNullInd < 0) { /* set custom sqlstate to return to client. */ strcpy(sqludf_sqlstate, "38100"); /* set custom message to return to client. Note that although the OUT parameter is declared as CHAR(70), DB2 prepends the procedure name and shared library entry point to the message. Keep the custom message short to avoid truncation. */ strcpy(sqludf_msgtext, "Received null input."); return (0); } hvTwoResultSetsSalary = *inMedianSalary; strcpy(sqlStmtInfo, "DECLARE curTwoResultSets1 statement failed"); EXEC SQL DECLARE curTwoResultSets1 CURSOR FOR SELECT name, job, CAST(salary AS DOUBLE) FROM staff WHERE salary > :hvTwoResultSetsSalary ORDER BY salary; strcpy(sqlStmtInfo, "DECLARE curTwoResultSets2 statement failed"); EXEC SQL DECLARE curTwoResultSets2 CURSOR FOR SELECT name, job, CAST(salary AS DOUBLE) FROM staff WHERE salary < :hvTwoResultSetsSalary ORDER BY salary DESC; strcpy(sqlStmtInfo, "OPEN statement failed"); EXEC SQL OPEN curTwoResultSets1; strcpy(sqlStmtInfo, "OPEN statement failed"); EXEC SQL OPEN curTwoResultSets2; /* leave both cursors open to return result sets */ return (0); /* set custom diagMsg and sqlstate to return to client */ return_error: { /* set custom sqlstate to return to client. */ strcpy(sqludf_sqlstate, sqlca.sqlstate); sprintf(sqludf_msgtext, "%d", sqlca.sqlcode); strcat(sqludf_msgtext, " : "); strcat(sqludf_msgtext, sqlStmtInfo); return (0); } } /* TwoResultSets function */ /************************************************************************** Stored Procedure: GeneralExample Purpose: Return a result set to the caller that identifies those employees with an education level equal to the value of input parameter inEdLevel. Shows how to: - define IN and OUT parameters in STYLE GENERAL - execute SQL to declare and work with a cursor - return a result set to the client Note: Extraction of result sets is not supported from C/C++ clients Parameters: IN: inEdLevel - education level of the employee OUT: outReturnCode - sqlcode of error (if one is raised) outErrorMsg - text information returned to the client to locate the error, if any When PARAMETER STYLE GENERAL clause is specified in the CREATE PROCEDURE statement for the procedure (see the script spcreate.db2), only the parameters passed during invocation are passed to the routine. With PARAMETER STYLE GENERAL, there is no concept of null. You cannot assess the nullability of variable, nor can you set a value to an SQL equivalent to NULL. ***************************************************************************/ SQL_API_RC SQL_API_FN GeneralExample(sqlint32 *inEdLevel, sqlint32 *outReturnCode, char outErrorMsg[33]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; sqlint32 hvGeneralEduLevel; EXEC SQL END DECLARE SECTION; /* initialize output parameters */ *outReturnCode = 0; memset(outErrorMsg, '\0', 33); EXEC SQL WHENEVER SQLERROR GOTO return_error; if ( (*inEdLevel > 25) || (*inEdLevel < 0)) { /* set the output error code and message */ *outReturnCode = -1; strcpy(outErrorMsg, "Received invalid input"); return (0); } hvGeneralEduLevel = *inEdLevel; strcpy(outErrorMsg, "DECLARE curGeneral failed"); EXEC SQL DECLARE curGeneral CURSOR FOR SELECT firstnme, lastname, workdept FROM employee WHERE edlevel = :hvGeneralEduLevel ORDER BY workdept; /* leave cursor open to return result set */ strcpy(outErrorMsg, "OPEN curGeneral failed"); EXEC SQL OPEN curGeneral; return (0); /* set error code information to return to client */ return_error: { *outReturnCode = sqlca.sqlcode; return (0); } } /* GeneralExample function */ /************************************************************************** Stored Procedure: GeneralWithNullsExample Purpose: Returns a result set to the caller that contains sales information for a particular business quarter, as specified by input parameter inQuarter. Shows how to: - define IN and OUT parameters in STYLE GENERAL WITH NULLS - define and use NULL indicators for parameters - execute SQL to declare and use a cursor - return a result set to the client Note: Extraction of result sets is not supported from C/C++ clients Parameters: IN: inQuarter - the business quarter for which sales information is returned OUT: outReturnCode - sqlcode of error (if one is raised) outErrorMsg - text information returned to the client to locate the error, if any When PARAMETER STYLE GENERAL WITH NULLS is defined for the routine (see routine registration script spcreate.db2), in addition to the parameters passed during invocation, a vector containing a null indicator for each parameter in the CALL statement is passed to the routine. ***************************************************************************/ SQL_API_RC SQL_API_FN GeneralWithNullsExample(sqlint32 *inQuarter, sqlint32 *outReturnCode, char outErrorMsg[33], sqlint16 nullInds[3]) { EXEC SQL INCLUDE SQLCA; /* SQL host variable declaration section */ /* Each host variable names must be unique within a code file, or the precompiler will raise SQL0307 error */ EXEC SQL BEGIN DECLARE SECTION; sqlint32 hvGenWithNullsQuarter; EXEC SQL END DECLARE SECTION; EXEC SQL WHENEVER SQLERROR GOTO return_error; /* Note: nullInds[0] corresponds to inQuarter nullInds[1] corresponds to outReturnCode nullInds[2] corresponds to outErrorMsg */ /* initialize output parameters and their corresponding null indicators */ *outReturnCode = 0; memset(outErrorMsg, '\0', 33); nullInds[1] = 0; nullInds[2] = -1; if (nullInds[0] < 0) { /* set the output error code and message */ *outReturnCode = -1; strcpy(outErrorMsg, "Received null input"); /* received null inputs, so set the output null indicators accordingly */ nullInds[1] = 0; nullInds[2] = 0; return (0); } if ((*inQuarter) < 1 || (*inQuarter) > 4) { /* set the output error code and message */ *outReturnCode = -1; strcpy(outErrorMsg, "Received invalid input"); /* set the output null indicators to indicate that the output parameters are not null */ nullInds[1] = 0; nullInds[2] = 0; return (0); } hvGenWithNullsQuarter = *inQuarter; strcpy(outErrorMsg, "DECLARE curGenWithNulls failed"); EXEC SQL DECLARE curGenWithNulls CURSOR FOR SELECT sales_person, region, sales FROM sales WHERE quarter(sales_date) = :hvGenWithNullsQuarter; /* leave cursor open to return result set */ strcpy(outErrorMsg, "OPEN curGenWithNulls failed"); EXEC SQL OPEN curGenWithNulls; return (0); /* set error code information to return to client */ return_error: { *outReturnCode = sqlca.sqlcode; nullInds[1] = 0; nullInds[2] = 0; return (0); } } /* GeneralWithNullsExample function */