/**********************************************************************
* FILENAME : WritingMultipleLongTextFields.c
*
* DESCRIPTION :
* Example mainly illustrates use of SQLPutData () to insert record
* with 'text' fields uses either TestTBL3 (one text field) or
* TestTBL4 (two text fields) in chunks which are requested at
* execute time thus illustrating DAE and how long text fields
* are handled.
*
* Can also be used to insert records into two other tables used
* for testing, TestTBL1 and TestTBL2, which do not have text fields.
*
* ODBC USAGE :
*
* Asks user to select table (1, 2 3 or 4)
* SQLPrepare () - to prepare statement for selected table
* SQLNumParams () - to establish how many parameters are needed
* Asks for how many records are to be written
* For TestTBL1 and TestTBL2
* Uses SQLBindParam () to bind the 4 paramaters
* For each record,
* prompts for data.
* calls SQLExecute () to execute the insert
* For TestTBL3 and TestTBL4
* Uses SQLDescribeParam () to get paramater attributes
* Uses SQLBindParameter () to bind the paramater with
* overall param size and, using SQL_LEN_DATA_AT_EXEC,
* the size of the chunks to be written each time when
* execute asks for more data.
* For each record,
* calls SQLExecute () to execute the insert
* Checks for SQL_NEED_DATA and calls SQLParamData ()
* to find out which of the text paramaters needs more
* data.
* While SQL_NEED_DATA returned,
* calls SQLPutData () to write data in chunks
* call SQLParamData () which will complete the
* current parameter and return a new param ID
* and SQL_NEED_DATA if any more paramaters need
* more data (e.g. the second Memo paramater).
*
* Use CreateTable to create TestTBL3 and/or TestTBL4
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>
#include "util.c"
#define MEMO1TEXTSIZE 12000
#define MEMO2TEXTSIZE 12000
#define PERSONID_LEN 2
#define LASTNAME_LEN 255
#define FIRSTNAME_LEN 255
#define ADDRESS_LEN 255
#define CITY_LEN 255
SQLLEN cPersonId;
SQLCHAR strFirstName[FIRSTNAME_LEN];
SQLCHAR strLastName[LASTNAME_LEN];
SQLCHAR strAddress[ADDRESS_LEN];
SQLCHAR strCity[CITY_LEN];
SQLLEN lenFirstName=0, lenLastName=0, lenAddress=0, lenCity=0;
int main () {
SQLHENV henv = SQL_NULL_HENV; // Environment
SQLHDBC hdbc = SQL_NULL_HDBC; // Connection handle
SQLHSTMT hstmt = SQL_NULL_HSTMT; // Statement handle
SQLRETURN retcode;
// Tables
// TestTBL1 has identity field 1 PersonID
// TestTBL2 is same as TestTBL1 but without the identity field
// TestTBL3 has one text field Memo1 in column 4
// TestTBL4 is same as TestTBL3 plus another text field, Memo2 in columns 5
char sqlStatement[4][256]= {
{"INSERT INTO TestTBL1 VALUES (?,?,?,?)"},
{"INSERT INTO TestTBL2 VALUES (?,?,?,?)"},
{"INSERT INTO TestTBL3 VALUES ('A N Other',55,'1958-12-25 00:00:00',?)"},
{"INSERT INTO TestTBL4 VALUES ('A N Other',55,'1958-12-25 00:00:00',?,?)"}
};
int tableNo, numRecs;
char confirm='N';
char reply=' ';
// SQLBindParameter variables (for TestTBL3 and TestTBL4).
SQLLEN lenMemo1TextSize, lMemo1Bytes;
SQLLEN lenMemo2TextSize, lMemo2Bytes;
PTR pParamID;
SQLSMALLINT NumParams, i, DataType, DecimalDigits, Nullable, paramNo;
SQLULEN bytesRemaining;
// SQLPutData text data for Memo1 (for TestTBL3 and TestTBL4).
UCHAR Memo1Data[] =
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyz";
SDWORD lenMemo1Batch = (SDWORD)sizeof(Memo1Data) - 1;
// SQLPutData text data for Memo1 (for TestTBL4).
UCHAR Memo2Data[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
SDWORD lenMemo2Batch = (SDWORD)sizeof(Memo2Data) - 1;
// used for each text parameter needing data
SQLLEN lBytes;
SDWORD lenBatch;
UCHAR *Data;
char strRecs[32];
// get table for inserts ?
reply=getInt ("Which Table"
"\n0 (Quit)"
"\n1 (TestTBL1 ID)"
"\n2 (TestTBL2 NoID)"
"\n3 (TestTBL3 Memo1)"
"\n4 (TestTBL4 Memo1, Memo2)"
"\n ?", &tableNo, confirm, 0);
// or quit
if (tableNo==0) goto exit;
// Allocate the ODBC environment and save handle.
retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
henv, SQL_HANDLE_ENV);
// Notify ODBC that this is an ODBC 3.0 app.
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3, 0);
CHECK_ERROR(retcode, "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)",
henv, SQL_HANDLE_ENV);
// Allocate ODBC connection handle and connect.
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_DBC)",
hdbc, SQL_HANDLE_DBC);
retcode=SQLDriverConnect(hdbc, NULL, "DSN=DATASOURCE;", SQL_NTS, NULL, 0,
NULL, SQL_DRIVER_COMPLETE);
CHECK_ERROR(retcode, "SQLDriverConnect(DATASOURCE)",
hdbc, SQL_HANDLE_DBC);
// Allocate statement handle.
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
hstmt, SQL_HANDLE_STMT);
printf ("Preparing : %s\n", &sqlStatement[tableNo-1][0]);
retcode = SQLPrepare(hstmt, &sqlStatement[tableNo-1][0], SQL_NTS);
CHECK_ERROR(retcode, "SQLPrepare(SQL_HANDLE_STMT)",
hstmt, SQL_HANDLE_STMT);
printf("SQLPrepare OK\n");
retcode = SQLNumParams(hstmt, &NumParams);
CHECK_ERROR(retcode, "SQLNumParams()", hstmt, SQL_HANDLE_STMT);
printf("SQLNumParams(hstmt, &NumParams) OK\n");
// Expecting DAE (Data at Execution) for :
// if table 1 or 2 - 4 parameters - FirstName, LastName, Address, City
// if table 3 - 1 parameter - for Memo1
// if table 4 - 2 parameters - Memo1 and Memo2
printf ("Num Params : %i\n", NumParams);
if (NumParams) {
switch (tableNo)
{
case 1: // TestTBL1 - should be asking for 4 params
case 2: // TestTBL2 - should be asking for 4 params
// Get Records to Add
memset (strRecs, ' ', sizeof(strRecs));
reply=getStr ("Number of Recs", strRecs, sizeof(strRecs), 'N');
numRecs=atoi(strRecs);
printf ("Writing %i records\n", numRecs);
// Bind Parameters to all fields
retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, FIRSTNAME_LEN,
0, strFirstName, FIRSTNAME_LEN,
&lenFirstName);
retcode = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, LASTNAME_LEN,
0, strLastName, LASTNAME_LEN,
&lenLastName);
retcode = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, ADDRESS_LEN,
0, strAddress, ADDRESS_LEN,
&lenAddress);
retcode = SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, CITY_LEN,
0, strCity, CITY_LEN, &lenCity);
for (i=0;i<numRecs;i++) {
// Setup data and lengths
memset (strFirstName, ' ', FIRSTNAME_LEN);
memset (strLastName, ' ', LASTNAME_LEN);
memset (strAddress, ' ', ADDRESS_LEN);
memset (strCity, ' ', CITY_LEN);
// Get data from user
getStr ("First Name", strFirstName, FIRSTNAME_LEN, 'N');
getStr ("Last Name", strLastName, LASTNAME_LEN, 'N');
getStr ("Address ", strAddress, ADDRESS_LEN, 'N');
getStr ("City ", strCity, CITY_LEN, 'N');
// Remove Nulls
strFirstName[strlen(strFirstName)]=' ';
strLastName[strlen(strLastName)]=' ';
strAddress[strlen(strAddress)]=' ';
strCity[strlen(strCity)]=' ';
// Set sizes
lenFirstName=sizeof(strFirstName);
lenLastName=sizeof(strLastName);
lenAddress=sizeof(strAddress);
lenCity=sizeof(strCity);
// Execute statement
retcode = SQLExecute(hstmt);
if (retcode == SQL_SUCCESS ||
retcode == SQL_SUCCESS_WITH_INFO) {
printf ("Status : ok\n");
} else {
printf ("Status : Error %i\n", retcode);
}
}
break;
case 3: // TestTBL3 - has text field Memo1
case 4: // TestTBL4 - has text fields Memo1, Memo2
// Get Records to Add
memset (strRecs, ' ', sizeof(strRecs));
reply=getStr ("Number of Recs", strRecs, sizeof(strRecs), 'N');
numRecs=atoi(strRecs);
for (i = 0; i < NumParams; i++) {
// Describe the parameter.
retcode = SQLDescribeParam(hstmt, i+1,
&DataType,
&bytesRemaining,
&DecimalDigits,
&Nullable);
CHECK_ERROR(retcode, "SQLDescribeParam()",
hstmt, SQL_HANDLE_STMT);
printf("\nSQLDescribeParam() OK\n");
printf("Data Type : %i, bytesRemaining : %i, DecimalDigits : %i, Nullable %i\n",
(int)DataType, (int)bytesRemaining,
(int)DecimalDigits, (int)Nullable);
// Assume first param is for Memo1 text field
if (i==0) {
// Total number of bytes to write overall
lMemo1Bytes=(SDWORD) MEMO1TEXTSIZE;
// Chunk size to be written each time
lenMemo1TextSize=SQL_LEN_DATA_AT_EXEC(lMemo1Bytes);
printf ("Binding MEMO field 1 - ");
printf ("Bytes Remaining : %i, ", (int)lMemo1Bytes);
printf ("LenOrIndArray : %i\n", (int)lenMemo1TextSize);
// For DATA_AT_EXEC parameter, whatever is passed in
// parameter 8, is passed back in the second parameter
// of SQLParamData () and used to find the field for
// which more data is being requested.
retcode = SQLBindParameter(
hstmt, // Statment Handle
i + 1, // Parameter Number
SQL_PARAM_INPUT, // Type is INPUT
SQL_C_CHAR, // C Type
SQL_LONGVARCHAR, // SQL Type
lMemo1Bytes, // Overall size
0, // Decimal Digits
Memo1Data, // Param value Pointer
0, // Buffer Length
&lenMemo1TextSize); // Chunk size
CHECK_ERROR(retcode,
"SQLBindParameter(3 SQL_PARAM_INPUT)",
hstmt, SQL_HANDLE_STMT);
}
// Assume second param is for Memo2 text field
if (i==1) {
// Total number of bytes to write overall
lMemo2Bytes=(SDWORD) MEMO2TEXTSIZE;
// Chunk size to be written each time
lenMemo2TextSize=SQL_LEN_DATA_AT_EXEC(lMemo2Bytes);
printf ("Binding MEMO field 2 - ");
printf ("Bytes Remaining : %i, ", (int)lMemo2Bytes);
printf ("LenOrIndArray : %i\n", (int)lenMemo2TextSize);
// For DATA_AT_EXEC parameter, whatever is passed in
// parameter 8, is passed back in the second parameter
// of SQLParamData () and used to find the field for
// which more data is being requested.
retcode = SQLBindParameter(
hstmt, // Statment Handle
i + 1, // Parameter Number
SQL_PARAM_INPUT, // Type is INPUT
SQL_C_CHAR, // C Type
SQL_LONGVARCHAR, // SQL Type
lMemo2Bytes, // Overall size
0, // Decimal Digits
Memo2Data, // Param value Pointer
0, // Buffer Length
&lenMemo2TextSize); // Chunk size
CHECK_ERROR(retcode,
"SQLBindParameter(4 SQL_PARAM_INPUT)",
hstmt, SQL_HANDLE_STMT);
}
}
// write 'numrecs' records to selected table
for (i=0;i<numRecs;i++) {
// Execute the command.
printf ("\nCall SQLExecute\n");
retcode = SQLExecute(hstmt);
if ( (retcode != SQL_SUCCESS) &&
(retcode != SQL_NEED_DATA) &&
(retcode != SQL_SUCCESS_WITH_INFO) ) {
CHECK_ERROR(retcode, "SQLExecute()",
hstmt, SQL_HANDLE_STMT);
}
// we would expect SQLExecute to return SQL_NEED_DATA for
// the DATA AT EXEC fields set up for either Memo1 or Memo2
if (retcode==SQL_NEED_DATA) {
// Find the parameter/column for which more data is need
// SQLParamData returns (in pParamID) the value of
// whatever was given in param 8 passed in the
// SQLBindParameter call earlier, which in this example
// is the address of the buffer for that parameter.
printf ("First Call - SQLParamData\n");
retcode = SQLParamData(hstmt, &pParamID);
// parameter is Memo1
if (pParamID==Memo1Data) {
printf ("Need data for Memo1\n");
lBytes=lMemo1Bytes;
lenBatch=lenMemo1Batch;
Data=Memo1Data;
}
// parameter is Memo2
if (pParamID==Memo2Data) {
printf ("Need data for Memo2\n");
lBytes=lMemo2Bytes;
lenBatch=lenMemo2Batch;
Data=Memo2Data;
}
}
// Loop round while asking for more data
while (retcode == SQL_NEED_DATA) {
printf ("Param ID : %p\n", pParamID);
while (lBytes > lenBatch) {
printf ("SQL_NEED_DATA Call SQLPutData : lBytes %i, lenBatch %i\n",
(int)lBytes, (int)lenBatch);
SQLPutData(hstmt, Data, lenBatch);
lBytes -= lenBatch;
}
// Put final chunk.
printf ("Final Call - SQLPutData : lMemo1Bytes %i\n",
(int)lBytes);
retcode = SQLPutData(hstmt, Data, lBytes);
CHECK_ERROR(retcode, "SQLPutData()",
hstmt, SQL_HANDLE_STMT);
// Make final SQLParamData call for this paramater.
printf ("Final Call - SQLParamData\n");
retcode = SQLParamData(hstmt, &pParamID);
if ( (retcode != SQL_SUCCESS) &&
(retcode != SQL_SUCCESS_WITH_INFO) &&
(retcode != SQL_NEED_DATA) ) {
CHECK_ERROR(retcode, "SQLPutData()",
hstmt, SQL_HANDLE_STMT);
} else {
// if the last call to SQLParamData returns
// SQL_NEED_DATA again then another parameter
// needs more data. (NOTE: the parameter
// ordering is not necessarily guaranteed so
// we check for which one more data is needed
// for).
if (retcode == SQL_NEED_DATA) {
if (pParamID==Memo1Data) {
printf ("Need data for Memo1\n");
lBytes=lMemo1Bytes;
lenBatch=lenMemo1Batch;
Data=Memo1Data;
}
if (pParamID==Memo2Data) {
printf ("Need data for Memo2\n");
lBytes=lMemo2Bytes;
lenBatch=lenMemo2Batch;
Data=Memo2Data;
}
}
}
}
}
break;
}
} else {
printf ("No Params\n");
}
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