/**********************************************************************
* FILENAME :        ListTableKeyStructure.c
*
* DESCRIPTION :
*       Example to display primary and foreign key structures within tables.
*
*       For a given src table,
*           it shows the primary key field details
*           it shows foreign keys in other tables that reference primary
*           key of the src table
*           it shows foreign keys in the src table that refer to the primary
*           keys of other tables
*
* ODBC USAGE :
*       SQLBindCol to bind columns 3,4,5,7 and 8 of the results sets
*       SQLPrimaryKeys to get the primary key field detail on the src table
*       Displays results
*          SQLFreeStmt to close the cursor a SQLBindCol to rebind column 5
*               (from KEY_SEQ to FKTABLE_CAT)
*          SQLForeignKeys and SQLFetch to retrieve the foreign keys in other
*               tables that reference primary key of the src table
*          SQLFreeStmt to close the cursor again
*          SQLForeignKeys and SQLFetch to retrieve the foreign keys in the
*               src table that refer to the primary keys of other tables
*
*       NOTE: Uses the same results set bindings between SQLPrimaryKeys and
*               SQLForeignKeys but Column 5 is different and is re-bound.
*               SQL_C_SSHORT in SQLPrimaryKeys and SQL_C_CHAR in SQLForeignKeys
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>

#include "util.c"

#define TAB_LEN SQL_MAX_TABLE_NAME_LEN + 1
#define COL_LEN SQL_MAX_COLUMN_NAME_LEN + 1

int main () {

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

    SQLRETURN retcode;
    //
    // Four tables used in this example TestTBL5,TestTBL6,TestTBL7 and TestTBL8
    // Table 5 has a primary key and a foreign key linked to the primary key of
    // table 8. Table 6 has primary key and foreign key linked to the primary
    // key of table 5. Table 7 has primary key and foreign key linked to the
    // primary key of table 5. Table 8 has primary key
    //
    char   strTable[] = "TestTBL5";

    UCHAR strPkTable[TAB_LEN];       // Primary key table name
    UCHAR strFkTable[TAB_LEN];       // Foreign key table name
    UCHAR strFkTabCat[TAB_LEN];      // Foreign Key table catalog
                                     // Column 5 in SQLForeignKey call is char
    UCHAR strPkCol[COL_LEN];         // Primary key column
    UCHAR strFkCol[COL_LEN];         // Foreign key column

    SQLLEN lenPkTable, lenPkCol, lenFkTabCat, lenFkTable, lenFkCol, lenKeySeq;

   // Column key sequence (Note: Column 5 in SQLPrimaryKey call is small int) */

    SQLSMALLINT   iKeySeq;
    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);

    // Bind the columns that describe the primary and foreign keys.
    // Ignore the table schema, name, and catalog for this example.
    retcode = SQLBindCol(hstmt, 3, SQL_C_CHAR, strPkTable,
                                                TAB_LEN, &lenPkTable);
    CHECK_ERROR(retcode, "SQLBindCol(3)", hstmt, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt, 4, SQL_C_CHAR, strPkCol,
                                                COL_LEN, &lenPkCol);
    CHECK_ERROR(retcode, "SQLBindCol(4)", hstmt, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt, 5, SQL_C_SSHORT, &iKeySeq,
                                                TAB_LEN, &lenKeySeq);
    CHECK_ERROR(retcode, "SQLBindCol(5)", hstmt, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt, 7, SQL_C_CHAR, strFkTable,
                                                TAB_LEN, &lenFkTable);
    CHECK_ERROR(retcode, "SQLBindCol(7)", hstmt, SQL_HANDLE_STMT);

    retcode = SQLBindCol(hstmt, 8, SQL_C_CHAR, strFkCol,
                                                COL_LEN, &lenFkCol);
    CHECK_ERROR(retcode, "SQLBindCol(8)", hstmt, SQL_HANDLE_STMT);

    //
    // Get primary keys in the TestTBL5 table
    //
    retcode = SQLPrimaryKeys(hstmt,
                             NULL, 0,             // Catalog name
                             NULL, 0,             // Schema name
                             strTable, SQL_NTS);   // Table name

    printf ("\nGet primary key of the %s table\n", strTable);
    while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) {

       // Fetch and display the result set. This will be a list of the
       // columns in the primary key of the ORDERS table.
       retcode = SQLFetch(hstmt);
       if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
          printf("Table: %s Column: %s Key Seq: %hd \n",
                                        strPkTable, strPkCol, iKeySeq);
    }

    if (retcode != SQL_NO_DATA) {
        extract_error("SQLFetch : A", hstmt, SQL_HANDLE_STMT);
        goto exit;
    } else {
        printf ("...End of Data\n\n");
    }

    // Close the cursor (the hstmt is still allocated).
    retcode = SQLFreeStmt(hstmt, SQL_CLOSE);

    // Re-bind column 5 because its different between
    // SQLForeignKey() and SQLPrimaryKey()
    retcode = SQLBindCol(hstmt, 5, SQL_C_CHAR, &strFkTabCat,
                                                TAB_LEN, &lenFkTabCat);
    CHECK_ERROR(retcode, "SQLBindCol(5)", hstmt, SQL_HANDLE_STMT);

    //
    // Get the foreign keys in the other tables that reference the primary
    // key of the TestTBL5 table
    //
    retcode = SQLForeignKeys(hstmt,
                             NULL, 0,            // Primary catalog
                             NULL, 0,            // Primary schema
                             strTable, SQL_NTS,  // Primary table
                             NULL, 0,            // Foreign catalog
                             NULL, 0,            // Foreign schema
                             NULL, 0);           // Foreign table

    printf ("Get foreign keys in other tables that reference primary key");
    printf (" of the %s table\n", strTable);
    while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) {

        // Fetch and display the result set. This will be all of the
        // foreign keys in other tables that refer to the TestTBL5
        // primary key.
        retcode = SQLFetch(hstmt);
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
            printf("%-s ( %-s ) <-- %-s ( %-s )\n",
                                strPkTable, strPkCol, strFkTable, strFkCol);
    }

    // Should finish with SQL_NO_DATA status
    if (retcode != SQL_NO_DATA) {
        extract_error("SQLFetch : B", hstmt, SQL_HANDLE_STMT);
        goto exit;
    } else {
        printf ("...End of Data\n\n");
    }

    // Close the cursor (the hstmt is still allocated).
    retcode = SQLFreeStmt(hstmt, SQL_CLOSE);

    //
    // Get the foreign keys in TestTBL5 that refer to the primary keys of
    // other tables
    //
    retcode = SQLForeignKeys(hstmt,
                             NULL, 0,             // Primary catalog
                             NULL, 0,             // Primary schema
                             NULL, 0,             // Primary table
                             NULL, 0,             // Foreign catalog
                             NULL, 0,             // Foreign schema
                             strTable, SQL_NTS);  // Foreign table

    printf ("Get foreign keys in the %s table that refer to the primary keys");
    printf (" of other tables\n", strTable);
    while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) {

        // Fetch and display the result set. This will be all of the
        // primary keys in other tables that are referred to by foreign
        // keys in the TestTBL5 table.
        retcode = SQLFetch(hstmt);
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
            printf("%-s ( %-s )--> %-s ( %-s )\n",
                            strFkTable, strFkCol, strPkTable, strPkCol);
    }

    if (retcode != SQL_NO_DATA) {
        extract_error("SQLFetch : C", hstmt, SQL_HANDLE_STMT);
        goto exit;
    } else {
        printf ("...End of Data\n\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