/**********************************************************************
* FILENAME :        ColumnAttributes.c
*
* DESCRIPTION :
*       Example extends ListColumns.c example to get list of tables
*       starting with 'TestTBL' but then uses the column details (from
*       SQLColAttribute) to form a SELECT statement to read the data
*
* ODBC USAGE :
*       Gets list of tables in same manner as SQLColumns example
*       For each table name
*           SQLFreeStmt      - to close cursor used in statement to retrieve
*                              table names
*           SQLExecDirect    - with SELECT * FROM <tablename>
*           SQLNumResultCols - to get number of columns in
*                              <tablename>
*           SQLColAttribute  - for each column uses SQL_DESC_NAME to get
*                              column name/label
*           Allocate memory for column retrieval
*           SQLBindCol       - to bind memory to columns
*           SQLFetch         - to retrieve full records from table
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>

#include "util.c"

#define BUFFERSIZE      1024
#define NUMCOLS         5

struct DataBinding {
   SQLSMALLINT TargetType;
   SQLPOINTER TargetValuePtr;
   SQLINTEGER BufferLength;
   SQLLEN StrLen_or_Ind;
};

SQLRETURN printStatementResult(SQLHSTMT hstmt) {

    int i;
    SQLRETURN retcode = SQL_SUCCESS;
    SQLSMALLINT numColumns = 0, bufferLenUsed;
    SQLPOINTER* columnLabels = NULL;

    struct DataBinding* columnData = NULL;

    retcode = SQLNumResultCols(hstmt, &numColumns);
    CHECK_ERROR(retcode, "SQLNumResultCols()",
                hstmt, SQL_HANDLE_STMT);

    printf ("DataBinding Size : %i\n",
                (int)(numColumns * sizeof(struct DataBinding)));
    columnData = (struct DataBinding*)
                malloc ( numColumns * sizeof(struct DataBinding) );
    columnLabels = (SQLPOINTER *)malloc( numColumns * sizeof(SQLPOINTER*) );

    for ( i = 0 ; i < numColumns ; i++ ) {
        columnData[i].TargetValuePtr = NULL;
        columnLabels[i] = NULL;
    }

    printf( "No of columns : %i\n", numColumns );
    for ( i = 0 ; i < numColumns ; i++ ) {
        columnLabels[i] = (SQLPOINTER)malloc( BUFFERSIZE*sizeof(char) );

        // Get Field names from Table
        retcode = SQLColAttribute(hstmt, (SQLUSMALLINT)i + 1, SQL_DESC_NAME,
                            columnLabels[i], (SQLSMALLINT)BUFFERSIZE,
                            &bufferLenUsed, NULL);
        CHECK_ERROR(retcode, "SQLColAttribute()",
                    hstmt, SQL_HANDLE_STMT);

        printf( "Column %d: %s\n", i+1, (SQLCHAR*)columnLabels[i] );
    }

   // Allocate memory for the binding
    for ( i = 0 ; i < numColumns ; i++ ) {
        columnData[i].TargetType = SQL_C_CHAR;
        columnData[i].BufferLength = (BUFFERSIZE+1);
        columnData[i].TargetValuePtr =
                malloc( sizeof(unsigned char)*columnData[i].BufferLength );
    }

    // Set up the binding
    for ( i = 0 ; i < numColumns ; i++ ) {
        printf ("Binding Column %i\n", i+1);
        printf (" TargetType : %i\n", columnData[i].TargetType);
        printf (" ValuePtr : %p\n", columnData[i].TargetValuePtr);
        printf (" ColumnDataLen %i\n", columnData[i].BufferLength);
        printf (" StrLen_or_Ind %p\n", &(columnData[i].StrLen_or_Ind));
        retcode = SQLBindCol(hstmt, (SQLUSMALLINT)i+1,
                    columnData[i].TargetType, columnData[i].TargetValuePtr,
                    columnData[i].BufferLength, &(columnData[i].StrLen_or_Ind));
        CHECK_ERROR(retcode, "SQLBindCol(1)",
                    hstmt, SQL_HANDLE_STMT);
    }

    printf( "Data :\n" );
    // Fetch the data and print out the data
    for ( retcode = SQLFetch(hstmt) ;
          retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO ;
          retcode = SQLFetch(hstmt) ) {
        int j;
        printf ("\n");
        for ( j = 0 ; j < numColumns ; j++ ) {
            printf( "%s: %.12s\n", (char *) columnLabels[j],
                                (char *) columnData[j].TargetValuePtr );
            memset (columnData[j].TargetValuePtr, ' ', BUFFERSIZE+1);
        }
    }

    // If we've just read all the data return success
    if (retcode==SQL_NO_DATA) retcode=SQL_SUCCESS;

    printf( "\n" );

exit:

    // Free buffers
    for ( i = 0 ; i < numColumns ; i++ ) {
        if (columnLabels[i] != NULL) free (columnLabels[i]);
    }
    for ( i = 0 ; i < numColumns ; i++ ) {
        if (columnData[i].TargetValuePtr != NULL)
            free (columnData[i].TargetValuePtr);
    }

    if (columnLabels!=NULL) free (columnLabels);
    if (columnData!=NULL) free (columnData);

    return retcode;
}

int main () {
    int i, j, count = 1;
    SQLCHAR tableNames[100][BUFFERSIZE];
    SQLCHAR *dbName = (SQLCHAR *)malloc( sizeof(SQLCHAR)*BUFFERSIZE );
    SQLCHAR *userName = (SQLCHAR *)malloc( sizeof(SQLCHAR)*BUFFERSIZE );
    SQLCHAR connStrbuffer[BUFFERSIZE];
    SQLSMALLINT connStrBufferLen, bufferLen;

    SQLHENV  henv  = SQL_NULL_HENV;   	// Environment
    SQLHDBC  hdbc  = SQL_NULL_HDBC;   	// Connection handle
    SQLHSTMT hstmt = SQL_NULL_HSTMT;  	// Statement handle
    SQLRETURN retcode;			// Return status

    struct DataBinding* catalogResult = (struct DataBinding*)
                            malloc( NUMCOLS * sizeof(struct DataBinding) );
    SQLCHAR* selectAllQuery = (SQLCHAR *)malloc(sizeof(SQLCHAR) * BUFFERSIZE);

    for ( i = 0 ; i < NUMCOLS ; i++ ) {
        catalogResult[i].TargetValuePtr = NULL;
    }

    // Connect to database
    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(SQL_HANDLE_DBC)",
                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_STMT)",
                hstmt, SQL_HANDLE_STMT);

    // Display the database information
    retcode = SQLGetInfo(hdbc, SQL_DATABASE_NAME, dbName,
                        (SQLSMALLINT)BUFFERSIZE, (SQLSMALLINT *)&bufferLen);
    CHECK_ERROR(retcode, "SQLGetInfo(SQL_DATABASE_NAME)",
                hstmt, SQL_HANDLE_STMT);

    retcode = SQLGetInfo(hdbc, SQL_USER_NAME, userName,
                        (SQLSMALLINT)BUFFERSIZE, &bufferLen);
    CHECK_ERROR(retcode, "SQLGetInfo(SQL_USER_NAME)",
                hstmt, SQL_HANDLE_STMT);

    for ( i = 0 ; i < NUMCOLS ; i++ ) {
        catalogResult[i].TargetType = SQL_C_CHAR;
        catalogResult[i].BufferLength = (BUFFERSIZE + 1);
        catalogResult[i].TargetValuePtr =
                malloc( sizeof(unsigned char)*catalogResult[i].BufferLength );
    }

    // Set up the binding. This can be used even if the statement is
    // closed by closeStatementHandle
    for ( i = 0 ; i < NUMCOLS ; i++ ) {
        retcode = SQLBindCol(hstmt, (SQLUSMALLINT)i + 1,
                                    catalogResult[i].TargetType,
                                    catalogResult[i].TargetValuePtr,
                                    catalogResult[i].BufferLength,
                                    &(catalogResult[i].StrLen_or_Ind));
        CHECK_ERROR(retcode, "SQLBindCol(2)",
                    hstmt, SQL_HANDLE_STMT);
    }

    retcode = SQLTables(hstmt, dbName, SQL_NTS, userName, SQL_NTS,
                         "%", SQL_NTS, "TABLE", SQL_NTS );
    CHECK_ERROR(retcode, "SQLTables()",
                hstmt, SQL_HANDLE_STMT);

    // Create array of my tables starting with 'TestTBL'
    i=0;
    for ( retcode = SQLFetch(hstmt) ;
          retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO ;
          retcode = SQLFetch(hstmt), ++count ) {
        if ( strstr (catalogResult[2].TargetValuePtr, "TestTBL") != 0 ) {
            strcpy (tableNames[i++], catalogResult[2].TargetValuePtr);
            printf( "Saving Table %i, Name %s\n", count,
                            (char *) catalogResult[2].TargetValuePtr );
        }
    }

    // Output details of all my tables.
    // The SQLFreeStmt with SQL_CLOSE closes the cursor associated with
    // Statement Handle and discards all pending results.
    for (j=0; j<i; j++) {
        retcode = SQLFreeStmt(hstmt, SQL_CLOSE);
        CHECK_ERROR(retcode, "SQLFreeStmt()",
                    hstmt, SQL_HANDLE_STMT);
        printf( "Select all data from table : (%s)\n", tableNames[j] );
        sprintf( selectAllQuery, "SELECT * FROM %s", tableNames[j] );
        printf( "Query : %s\n", selectAllQuery);
        retcode = SQLExecDirect(hstmt, selectAllQuery, SQL_NTS);
        CHECK_ERROR(retcode, "SQLExecDirect()",
                    hstmt, SQL_HANDLE_STMT);
        retcode = printStatementResult(hstmt);
        CHECK_ERROR(retcode, "printStatementResult()",
                    hstmt, SQL_HANDLE_STMT);
    }

exit:

    // Free memory
    free (dbName);
    free (userName);
    free (selectAllQuery);
    for ( i = 0 ; i < NUMCOLS ; i++ ) {
        if (catalogResult[i].TargetValuePtr == NULL)
            free(catalogResult[i].TargetValuePtr);
    }
    free (catalogResult);

    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