/**********************************************************************
* FILENAME :        ShowDescriptorContents.c
* DESCRIPTION :
*       Example dumps out the descriptors for each of 3 different
*       prepared statements
*
* ODBC USAGE :
*       Three classes of statement:-
*           Insert - with parameters
*           Select - with columns
*           Select - with parameters and columns
*
*           Prepares each statement and dumps out IRD, ARD, IPD and APD
*           for each. Uses function DumpDescriptors () in util.c.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>

#include "util.c"

#define PERSONID_LEN  2
#define LASTNAME_LEN  255
#define FIRSTNAME_LEN 255
#define ADDRESS_LEN 255
#define CITY_LEN  255

void show_error() {
   printf("error\n");
}

SQLHENV  henv   = SQL_NULL_HENV;     // Environment
SQLHDBC  hdbc   = SQL_NULL_HDBC;     // Connection handle
SQLHSTMT hstmt0 = SQL_NULL_HSTMT;    // Statement handle
SQLHSTMT hstmt1 = SQL_NULL_HSTMT;    // Statement handle
SQLHSTMT hstmt2 = SQL_NULL_HSTMT;    // Statement handle
SQLRETURN retcode;

SQLLEN  cPersonId;
SQLCHAR strFirstName[FIRSTNAME_LEN];
SQLCHAR strLastName[LASTNAME_LEN];
SQLCHAR strAddress[ADDRESS_LEN];
SQLCHAR strCity[CITY_LEN];
SQLLEN  lenPersonId=0, lenFirstName=0, lenLastName=0, lenAddress=0, lenCity=0;

SQLSMALLINT     NumParams, NumCols;
int i;

//
// Statement test cases
//
// parameters
char stmt0[] = "INSERT INTO TestTBL1 (FirstName, LastName, Address, City)"
               "VALUES (?, ?, ?, ?)";
// columns
char stmt1[] = "SELECT PersonID, FirstName, LastName, Address, City"
               "FROM TestTBL1";
// columns and parameters
char stmt2[] = "SELECT PersonID FROM TestTBL1 where FirstName=?";

int main () {

    int inlenookmarkRec;
    // Allocate environment
    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,
                                            (SQLPOINTER*)SQL_OV_ODBC3, 0);
    CHECK_ERROR(retcode, "SQLSetEnvAttr(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    // Allocate Connection
    retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_DBC)",
                hdbc, SQL_HANDLE_DBC);

    // Set Login Timeout
    retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
    CHECK_ERROR(retcode, "SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)",
                hdbc, SQL_HANDLE_DBC);

    // Connect to DSN
    retcode = SQLConnect(hdbc, (SQLCHAR*) "DATASOURCE", SQL_NTS,
                               (SQLCHAR*) NULL, 0, NULL, 0);
    CHECK_ERROR(retcode, "SQLConnect(DATASOURCE)", hdbc, SQL_HANDLE_DBC);

    // --------------------------------------------------
    // Process statement 0 (Needs parameter binding only)
    // --------------------------------------------------

    // Allocate Statement Handle
    retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt0);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
                hstmt0, SQL_HANDLE_STMT);

    // Just to force it to create a bookmark record in the descriptors
    retcode = SQLSetStmtAttr(hstmt0, SQL_ATTR_USE_BOOKMARKS,
                                            (SQLPOINTER)SQL_UB_VARIABLE, 0);
    CHECK_ERROR(retcode, "SQLSetStmtAttr(SQL_ATTR_USE_BOOKMARKS)",
                hstmt0, SQL_HANDLE_STMT);

    // Bind Parameters to all fields
    retcode = SQLBindParameter(hstmt0, 1,
                               SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
                               FIRSTNAME_LEN, 0,
                               strFirstName, FIRSTNAME_LEN, &lenFirstName);
    CHECK_ERROR(retcode, "SQLBindParameter()", hstmt0, SQL_HANDLE_STMT);

    retcode = SQLBindParameter(hstmt0, 2,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, LASTNAME_LEN, 0,
                               strLastName, LASTNAME_LEN, &lenLastName);
    CHECK_ERROR(retcode, "SQLBindParameter()", hstmt0, SQL_HANDLE_STMT);

    retcode = SQLBindParameter(hstmt0, 3,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, ADDRESS_LEN, 0,
                               strAddress, ADDRESS_LEN, &lenAddress);
    CHECK_ERROR(retcode, "SQLBindParameter()", hstmt0, SQL_HANDLE_STMT);

    retcode = SQLBindParameter(hstmt0, 4,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, CITY_LEN, 0,
                               strCity, CITY_LEN, &lenCity);
    CHECK_ERROR(retcode, "SQLBindParameter()", hstmt0, SQL_HANDLE_STMT);

    retcode = SQLPrepare(hstmt0, (SQLCHAR*) stmt0, SQL_NTS);
	CHECK_ERROR(retcode, "SQLPrepare(hstmt0)", hstmt0, SQL_HANDLE_STMT);

    //
    // NumParams is number of parameter records
    //
    retcode= SQLNumParams(hstmt0, &NumParams);
    CHECK_ERROR(retcode, "SQLNumParams(hstmt0)", hstmt0, SQL_HANDLE_STMT);

    printf ("\nNo of params in INSERT Statement : %i\n", NumParams);

    //
    // Now use SQLGetDescRec() to pull out descriptor details
    //
    inlenookmarkRec=hasBookmarkRecord (hstmt0);
    if (inlenookmarkRec) {
        printf ("\n** STMT0 HAS BOOKMARK RECORD **\n");
    } else {
        printf ("\n** STMT0 HAS NO BOOKMARK RECORD **\n");
    }

    printf ("\nDescriptors Statement 0 (INSERT) Prepare \n");
    printf ("\n%s\n", stmt0);
    //
    // Dump out the header and records of the descriptors
    //
    dumpDescriptors ("HSTMT 0 ", hstmt0, 'Y', 'Y', inlenookmarkRec);

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt0);
    hstmt0 = SQL_NULL_HSTMT;
    // ------------------------------------------
    // Process Statement 1 (Needs column binding only)
    // ------------------------------------------

    // Setup statement two as if we  were to select
    retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt1);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    // Just to force it to create a bookmark record in the descriptors
    retcode = SQLSetStmtAttr(hstmt1, SQL_ATTR_USE_BOOKMARKS,
                                            (SQLPOINTER)SQL_UB_VARIABLE, 0);
    CHECK_ERROR(retcode, "SQLSetStmtAttr(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    // Bind columns 1, 2, 3, 4 and 5
    retcode = SQLBindCol(hstmt1, 1, SQL_C_USHORT, &cPersonId, 2, &lenPersonId);
    CHECK_ERROR(retcode, "SQLBindCol(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt1, 2, SQL_C_CHAR, &strFirstName,
                                                FIRSTNAME_LEN, &lenFirstName);
    CHECK_ERROR(retcode, "SQLBindCol(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt1, 3, SQL_C_CHAR, &strLastName,
                                                LASTNAME_LEN, &lenLastName);
    CHECK_ERROR(retcode, "SQLBindCol(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt1, 4, SQL_C_CHAR, &strAddress,
                                                ADDRESS_LEN, &lenAddress);
    CHECK_ERROR(retcode, "SQLBindCol(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt1, 5, SQL_C_CHAR, &strCity,
                                                CITY_LEN, &lenCity);
    CHECK_ERROR(retcode, "SQLBindCol(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    retcode = SQLPrepare(hstmt1, (SQLCHAR*) stmt1, SQL_NTS);
    CHECK_ERROR(retcode, "SQLBindCol(SQL_HANDLE_STMT)",
                hstmt1, SQL_HANDLE_STMT);

    //
    // NumCols is number of column records
    //
    retcode = SQLNumResultCols (hstmt1, &NumCols);
	CHECK_ERROR(retcode, "SQLNumResultCols(hstmt1)",
                hstmt1, SQL_HANDLE_STMT);

    printf ("\nNo of columns in SELECT statement : %i\n", NumCols);

    //
    // Now use SQLGetDescRec() to pull out descriptor details
    //
    inlenookmarkRec=hasBookmarkRecord (hstmt1);
    if (inlenookmarkRec) {
        printf ("\n** STMT1 HAS BOOKMARK RECORD **\n");
    } else {
        printf ("\n** STMT1 HAS NO BOOKMARK RECORD **\n");
    }
    printf ("\nDescriptors Statement 1 (SELECT) Prepare \n");
    printf ("\n%s\n", stmt1);

    //
    // Dump out the header and records of the descriptors
    //
    dumpDescriptors ("HSTMT 1 ", hstmt1, 'Y', 'Y', inlenookmarkRec);

    // Read the data and dump out descriptors again
    retcode=SQLExecute (hstmt1);
	CHECK_ERROR(retcode, "SQLExecute(hstmt1)", hstmt1, SQL_HANDLE_STMT);

    printf ("\nDATA :\n");
    for (i=0; ; i++) {
        retcode = SQLFetch(hstmt1);
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            printf("Record %d : %i %s, %s, %s, %s\n",
                    i+1, (int) cPersonId, rtrim(strFirstName, ' '),
                    rtrim(strLastName, ' '), rtrim(strAddress, ' '),
                    rtrim(strCity, ' '));
        } else {
            if (retcode != SQL_NO_DATA) {
                CHECK_ERROR(retcode, "SQLFetch(hstmt1)",
                            hstmt1, SQL_HANDLE_STMT);
            }
            break;
        }
    }
    printf ("EOF :\n\n");

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt1);
    hstmt1 = SQL_NULL_HSTMT;

    // ---------------------------------------------------------
    // Process Statement 2 (Needs both param AND column binding)
    // ---------------------------------------------------------

    // Setup statement two as if we  were to select
    retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt2);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    //just to force it to create a bookmark record in the descriptors
    retcode = SQLSetStmtAttr(hstmt2, SQL_ATTR_USE_BOOKMARKS,
                                            (SQLPOINTER)SQL_UB_VARIABLE, 0);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    // Bind parameter and column
    retcode = SQLBindParameter(hstmt2, 1,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, FIRSTNAME_LEN, 0,
                               strFirstName, FIRSTNAME_LEN, &lenFirstName);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    retcode = SQLBindCol(hstmt2, 1, SQL_C_USHORT, &cPersonId, 2, &lenPersonId);

    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    retcode = SQLPrepare(hstmt2, (SQLCHAR*) stmt2, SQL_NTS);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    //
    // NumCols is number of column records
    //
    retcode = SQLNumResultCols (hstmt2, &NumCols);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    printf ("\nNo of columns in statement 2 is: %i\n", NumCols);

    //
    // NumParams is number of parameter records
    //
    retcode= SQLNumParams(hstmt2, &NumParams);
	CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    printf ("\nNo of params in statement 2 is : %i\n", NumParams);

    //
    // Now use SQLGetDescRec() to pull out descriptor details
    //
    inlenookmarkRec=hasBookmarkRecord (hstmt2);
    if (inlenookmarkRec) {
        printf ("\n** STMT2 HAS BOOKMARK RECORD **\n");
    } else {
        printf ("\n** STMT2 HAS NO BOOKMARK RECORD **\n");
    }
    printf ("\nDescriptors Statement 2 (SELECT) Prepare \n");
    printf ("\n%s\n", stmt2);

    //
    // Dump out the header and records of the descriptors
    //
    dumpDescriptors ("HSTMT 2 ", hstmt2, 'Y', 'Y', inlenookmarkRec);

    printf ("\nThe End.\n");

exit:

    printf ("\nComplete.\n");

    // Free handles
    // Statement 0
    if (hstmt0 != SQL_NULL_HSTMT)
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt0);

    // Statement 1
    if (hstmt1 != SQL_NULL_HSTMT)
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt1);

    // Statement 2
    if (hstmt2 != SQL_NULL_HSTMT)
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt2);

    // 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