/**********************************************************************
* FILENAME : UsingParameterArraysAndKeySetCursors.c
*
* DESCRIPTION :
* Example uses a parameter array with a keyset driven cursor to retrieve
* rowsets with SQLFetchScroll.
*
* Also utilises a ParamOperationsArray which can be used to
* tell the driver which parameter sets in the parameter array
* to use or to ignore.
*
* It illustrates what SQL_PARAM_ARRAY_SELECTS = SQL_PAS_BATCH
* and SQL_NO_PAS_BATCH actually mean. The SQL_PARAM_ARRAY_SELECTS
* option indicates whether a result set is available for each set
* of parameters (SQL_PAS_BATCH) or whether only one result set
* is available (SQL_PAS_NO_BATCH).
*
* In other words, if the driver indicates SQL_PAS_BATCH, effectively
* individual parameter sets are processed one at a time irrespective
* of any rowset size indicated. Here the parameter status array,
* where a select is successful, will indicate one record returned
* in the first element, followed by NO ROW in the rest.
*
*
* ODBC USAGE :
* Uses parameterised SELECT statement on TestTBL1
* Set Statement Attributes for SQLFetchScroll:
* SQLSetStmtAttr - SQL_ATTR_CURSOR_TYPE - SQL_CURSOR_KEYSET_DRIVEN
* SQL_ATTR_ROW_BIND_TYPE - ROW-WISE Binding
* SQL_ATTR_ROW_ARRAY_SIZE - 3 rows
* SQL_ATTR_USE_BOOKMARKS - SQL_UB_VARIABLE
* SQL_ATTR_ROW_STATUS_PTR - Row Status Array Addr
* SQL_ATTR_ROW_BIND_OFFSET_PTR - Bind Offset Addr
* SQL_ATTR_ROWS_FETCHED_PTR - Rows Fetched Addr
* SQL_ATTR_CONCURRENCY - SQL_CONCUR_LOCK
* Set Parameter Array related Statement Attributes
* SQLSetStmtAttr - SQL_ATTR_PARAMSET_SIZE - size of parameter array
* SQL_ATTR_PARAM_STATUS_PTR - status array addr
* SQL_ATTR_PARAMS_PROCESSED_PTR - params processed addr
* SQL_ATTR_PARAM_OPERATION_PTR - ignore/proceed flags
* SQLExecDirect - to execute the select statement
* Loop using SQLFetchScroll and SQLMoreResults to retrieve data
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>
#include "util.c"
#define ROWSET_SIZE 3 // How many rows at a time
#define PARAM_ARRAY_SIZE 15 // Number of params in total
#define DATA_ARRAY_SIZE 3 // Buffers for rowsets
#define BOOKMARK_LEN 10
int main () {
SQLHENV henv = SQL_NULL_HENV; // Environment
SQLHDBC hdbc = SQL_NULL_HDBC; // Connection handle
SQLHSTMT hstmt = SQL_NULL_HSTMT; // Statement handle
SQLRETURN retcode;
// Select statements to return person details from TestTBL1
SQLCHAR stmt[] = "SELECT PersonID, FirstName, LastName, Address, City "
"FROM TestTBL1 WHERE PersonID=?";
// Array of person IDs to get list of all Record IDs for
SQLINTEGER PersonIDs[] = {1,2,3,4,5,6,7,118,119,10,111,112,113,114,15};
// Define structure for data
typedef struct tagCustStruct {
SQLCHAR Bookmark[BOOKMARK_LEN];
SQLLEN BookmarkLen;
SQLUINTEGER PersonID;
SQLLEN PersonIDInd;
SQLCHAR FirstName[255];
SQLLEN FirstNameLenOrInd;
SQLCHAR LastName[255];
SQLLEN LastNameLenOrInd;
SQLCHAR Address[255];
SQLLEN AddressLenOrInd;
SQLCHAR City[255];
SQLLEN CityLenOrInd;
} CustStruct;
CustStruct CustArray[DATA_ARRAY_SIZE];
SQLUSMALLINT ParamStatusArray[PARAM_ARRAY_SIZE];
// Operation Array used to indicate whether a parameter from the parameter
// array should be ignored or used. Here the first 2 parameters are ignored
// and will not appear in the results.
SQLUSMALLINT ParamOperationsArray[PARAM_ARRAY_SIZE] = {
SQL_PARAM_IGNORE, SQL_PARAM_IGNORE, SQL_PARAM_PROCEED,
SQL_PARAM_PROCEED, SQL_PARAM_PROCEED, SQL_PARAM_PROCEED,
SQL_PARAM_PROCEED, SQL_PARAM_PROCEED, SQL_PARAM_PROCEED,
SQL_PARAM_PROCEED, SQL_PARAM_PROCEED, SQL_PARAM_PROCEED,
SQL_PARAM_PROCEED, SQL_PARAM_PROCEED, SQL_PARAM_PROCEED,
};
SQLLEN ParamsProcessed=0;
int i;
SQLUSMALLINT RowStatusArray[DATA_ARRAY_SIZE], Action, RowNum;
SQLLEN NumUpdates = 0, NumInserts = 0, NumDeletes = 0;
SQLLEN BindOffset = 0;
SQLLEN RowsFetched = 0;
SQLLEN Concurrency = SQL_CONCUR_LOCK;
SQLLEN rowCount;
//
// Column-wise binding
//
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
henv, SQL_HANDLE_ENV);
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(SQLCHAR *)(void*)SQL_OV_ODBC3, -1);
CHECK_ERROR(retcode, "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)",
henv, SQL_HANDLE_ENV);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
CHECK_ERROR(retcode, "SQLAllocHandle(SQLAllocHandle)",
hdbc, SQL_HANDLE_DBC);
retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)10, 0);
CHECK_ERROR(retcode, "SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)",
hdbc, SQL_HANDLE_DBC);
retcode = SQLConnect(hdbc, (SQLCHAR*) "DATASOURCE", SQL_NTS,
(SQLCHAR*) NULL, 0, NULL, 0);
CHECK_ERROR(retcode, "SQLConnect(DATASOURCE)", hdbc, SQL_HANDLE_DBC);
retcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
hstmt, SQL_HANDLE_STMT);
// Setup for SQLFetchScroll and SQLMoreResults
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER)SQL_CURSOR_KEYSET_DRIVEN, 0);
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE,
(SQLPOINTER)sizeof(CustStruct), 0);
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE,
(SQLPOINTER)3, 0);
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_USE_BOOKMARKS,
(SQLPOINTER)SQL_UB_VARIABLE, 0);
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR,
RowStatusArray, 0);
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR,
&BindOffset, 0);
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR,
&RowsFetched,0);
retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY,
(SQLPOINTER)SQL_CONCUR_LOCK ,0);
// Setup for parameter array processing
retcode = SQLSetStmtAttr (hstmt, SQL_ATTR_PARAMSET_SIZE,
(SQLPOINTER) PARAM_ARRAY_SIZE, 0);
retcode = SQLSetStmtAttr (hstmt, SQL_ATTR_PARAM_STATUS_PTR,
ParamStatusArray, PARAM_ARRAY_SIZE);
retcode = SQLSetStmtAttr (hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR,
&ParamsProcessed, 0);
retcode = SQLSetStmtAttr (hstmt, SQL_ATTR_PARAM_OPERATION_PTR,
ParamOperationsArray, PARAM_ARRAY_SIZE);
// Bind array values of parameter 1 data in
retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG,
SQL_INTEGER, 0, 0, PersonIDs, 0, NULL);
// Bind columns for data out
retcode = SQLBindCol(hstmt, 1, SQL_C_LONG,
&CustArray[0].PersonID, 0,
&CustArray[0].PersonIDInd);
retcode = SQLBindCol(hstmt, 2, SQL_C_CHAR,
(SQLPOINTER) CustArray[0].FirstName, 255,
(SQLLEN *) &CustArray[0].FirstNameLenOrInd);
retcode = SQLBindCol(hstmt, 3, SQL_C_CHAR,
(SQLPOINTER) CustArray[0].LastName, 255,
(SQLLEN *) &CustArray[0].LastNameLenOrInd);
retcode = SQLBindCol(hstmt, 4, SQL_C_CHAR,
(SQLPOINTER) CustArray[0].Address, 255,
(SQLLEN *) &CustArray[0].AddressLenOrInd);
retcode = SQLBindCol(hstmt, 5, SQL_C_CHAR,
(SQLPOINTER) CustArray[0].City, 255,
(SQLLEN *) &CustArray[0].CityLenOrInd);
retcode = SQLExecDirect(hstmt, stmt, SQL_NTS);
CHECK_ERROR(retcode, "SQLExecDirect()", hstmt, SQL_HANDLE_STMT);
do {
retcode = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);
if (retcode!=SQL_NO_DATA) {
printf("\nParams Processed : %i", (int)ParamsProcessed);
printf("\nNext : %i Row ", (int)RowsFetched);
printf ("\nRowset Status Array : ");
for (i=0;i<DATA_ARRAY_SIZE;i++) {
switch (RowStatusArray[i]) {
case SQL_ROW_SUCCESS_WITH_INFO:
case SQL_ROW_SUCCESS:
printf ("\n %i - ROW SUCCESS", i);
break;
case SQL_ROW_NOROW:
printf ("\n %i - NO ROW", i);
break;
default:
printf ("\n %i - ?", (int)RowStatusArray[i]);
}
}
printf ("\nData : \n");
for (i = 0; i < RowsFetched; i++) {
printf ("Rowset Row %i Data : ", i);
printf("%i ", (int)CustArray[i].PersonID);
printf("%.10s ", CustArray[i].FirstName);
printf("%.10s ", CustArray[i].LastName);
printf("%.10s ", CustArray[i].Address);
printf("%.10s \n", CustArray[i].City);
}
} else {
printf("\nParams Processed : %i", (int)ParamsProcessed);
printf("\nNext : No Row\n");
}
} while (SQLMoreResults(hstmt) == SQL_SUCCESS);
exit:
printf ("\nComplete.\n");
// Free handles
// Statement
if (hstmt != SQL_NULL_HSTMT)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
// Connection
if (hdbc != SQL_NULL_HDBC) {
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
// Environment
if (henv != SQL_NULL_HENV)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return 0;
}
Further information