/**********************************************************************
* FILENAME :        ListSpecialColumns.c
*
* DESCRIPTION :
*       Example finds all tables starting with 'TestTBL' and for each
*       one outputs the optimal set of columns that uniquely identifies
*       a row in the table.
*
*       NOTE: Doesn't return information where table records are uniquely
*       identified by identity fields
*
* ODBC USAGE :
*       SQLGetInfo() to get the DB and USER names
*       SQLBindCol and SQLTables to obtain list of tables like 'TestTBL'
*       For each table
*           SQLSpecialColumns () to get column info
*           SQLBindCol() to bind the 8 columns of the result set (those being
*           Catalog, Schema, Table, Column, Grantor, Grantee and Is Grantable)
*           SQLFetch() to get the values
*           Display results
*/

#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>

#include "util.c"

#define STR_LEN 128 + 1
#define REM_LEN 254 + 1

// arbitary sizes for number of tables and
#define MAX_TABLES 100

//Number of columns in SQLTables
#define NUMCOLS 5

// Declare buffers for result set data
SQLSMALLINT Scope;
SQLCHAR     strColumnName[STR_LEN];
SQLSMALLINT DataType;
SQLCHAR     strTypeName[STR_LEN];
SQLLEN      ColumnSize;
SQLLEN      BufferLength;
SQLSMALLINT DecimalDigits;
SQLSMALLINT PseudoColumn;

SQLLEN     lenColumnName, lenTypeName;
SQLLEN     lenScope, lenDataType, lenColumnSize;
SQLLEN     lenBufferLength, lenDecimalDigits, lenPseudoColumn;

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

//
// Function to free handles
//
void Cleanup(SQLHSTMT henv, SQLHSTMT hdbc, SQLHSTMT hstmt) {

   if (hstmt != SQL_NULL_HSTMT)
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

   if (hdbc != SQL_NULL_HDBC) {
      SQLDisconnect(hdbc);
      SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
   }

   if (henv != SQL_NULL_HENV)
      SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

//
//gets list of tables based on iTableStrPtr
//
int getTheseTables (char *pTableName, SQLCHAR *tableNames[]) {

    int bufferSize = 1024, i, j, count = 1;
    SQLCHAR * dbName = (SQLCHAR *)malloc( sizeof(SQLCHAR)*bufferSize );
    SQLCHAR * userName = (SQLCHAR *)malloc( sizeof(SQLCHAR)*bufferSize );
    SQLCHAR connStrbuffer[1024];
    SQLSMALLINT connStrBufferLen, bufferLen;

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

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

    // 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)",
                hdbc, SQL_HANDLE_DBC);

    retcode = SQLGetInfo(hdbc, SQL_USER_NAME, userName,
                         (SQLSMALLINT)bufferSize, &bufferLen);
    CHECK_ERROR(retcode, "SQLGetInfo(SQL_USER_NAME)",
                hdbc, SQL_HANDLE_DBC);

    printf ("Current DB Name : %s\n", dbName);
    printf ("Current User Name : %s\n", userName);

    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.
    for ( i = 0 ; i < NUMCOLS ; i++ ) {
        //printf ("Binding Column %i\n", i+1);
        retcode = SQLBindCol(hstmt, (SQLUSMALLINT)i + 1,
                             catalogResult[i].TargetType,
                             catalogResult[i].TargetValuePtr,
                             catalogResult[i].BufferLength,
                             &(catalogResult[i].StrLen_or_Ind));

        CHECK_ERROR(retcode, "SQLBindCol(SQLUSMALLINT)",
                    hstmt, SQL_HANDLE_STMT);
    }

    retcode = SQLTables( hstmt, dbName, SQL_NTS, userName, SQL_NTS, "%",
                                        SQL_NTS, "TABLE", SQL_NTS );
    CHECK_ERROR(retcode, "SQLTables(dbName)",
                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, pTableName) != 0 ) {
            tableNames[i]=(char *)
              malloc((strlen(catalogResult[2].TargetValuePtr)+1)*sizeof(char));
            strcpy (tableNames[i], catalogResult[2].TargetValuePtr);
            printf( "Found Table %s\n", tableNames[i++] );
        }
    }

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

exit:
    Cleanup(henv, hdbc, hstmt);
    return i;
}

int main () {

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

    char padding[] = "           ";
    int header;

    SQLCHAR *tableNames[MAX_TABLES];
    SQLINTEGER tableCount, i, max=strlen(padding);

    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_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)5, 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);

    tableCount=getTheseTables ("TestTBL", tableNames);
    for (i=0; i<tableCount; i++) {

        // allocate statement handle
        retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
        CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
                    hstmt, SQL_HANDLE_STMT);

        printf ("\nTable a : %s", tableNames[i]);
        // Permutations:-
        // Param 2 Identifier Type - SQL_BEST_ROWID or SQL_ROWVER
        // Param 9 Scope           - SQL_SCOPE_CURROW,
        //                           SQL_SCOPE_TRANSACTION or
        //                           SQL_SCOPE_SESSION
        // Param 10 Nullable       - SQL_NO_NULLS or SQL_NULLABLE

        retcode = SQLSpecialColumns (hstmt,
                                     SQL_BEST_ROWID,
                                     NULL, 0,
                                     NULL, 0,
                                     (SQLCHAR *)tableNames[i], SQL_NTS,
                                     SQL_SCOPE_CURROW,
                                     SQL_NULLABLE);

        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {

            // Bind columns in result set to buffers
            SQLBindCol(hstmt, 1, SQL_C_SHORT, &Scope,
                                 sizeof(Scope), &lenScope);
            SQLBindCol(hstmt, 2, SQL_C_CHAR,  strColumnName,
                                 sizeof(strColumnName), &lenColumnName);
            SQLBindCol(hstmt, 3, SQL_C_SHORT, &DataType,
                                 sizeof(DataType), &lenDataType);
            SQLBindCol(hstmt, 4, SQL_C_CHAR,
                                 strTypeName, STR_LEN, &lenTypeName);
            SQLBindCol(hstmt, 5, SQL_C_SHORT, &ColumnSize,
                                 sizeof(ColumnSize), &lenColumnSize);
            SQLBindCol(hstmt, 6, SQL_C_SHORT, &BufferLength,
                                 sizeof(BufferLength), &lenBufferLength);
            SQLBindCol(hstmt, 7, SQL_C_SHORT, &DecimalDigits,
                                 sizeof(DecimalDigits), &lenDecimalDigits);
            SQLBindCol(hstmt, 8, SQL_C_SHORT, &PseudoColumn,
                                 sizeof(PseudoColumn), &lenPseudoColumn);

            max=strlen(padding);

            header=0;

            while (SQL_SUCCESS == retcode) {
                retcode = SQLFetch(hstmt);
                CHECK_ERROR(retcode, "SQLFetch(SpecialColumns)",
                            hstmt, SQL_HANDLE_STMT);

                if (header++==0) {
                    printf ("\n%s\n",tableNames[i]);
                }

                printf ("\nScope      : %i\n", (int) Scope);
                printf ("Column     : %s\n", strColumnName);
                printf ("Data Type  : %i\n", (int) DataType);
                printf ("Type Name  : %s\n", strTypeName);
                printf ("Col Size   : %i\n", (int) ColumnSize);
                printf ("Buff Len   : %i\n", (int) BufferLength);
                printf ("Digits     : %i\n", (int) DecimalDigits);
                printf ("Pseudo Col : %i\n", (int) PseudoColumn);

                if (retcode==SQL_NO_DATA && header==1) {
                    printf ("(NO DATA)\n");
                }
            }
        } else {
            CHECK_ERROR(retcode, "SQLSpecialColumns()",
                        hstmt, SQL_HANDLE_STMT);
        }
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
    }

exit:
    printf ("\nComplete.\n");

    //Free statement, connection and environment handle
    Cleanup(henv, hdbc, hstmt);

    return 0;
}

Further information