/**********************************************************************
* FILENAME : DescribeAndBindColumns.c
*
* DESCRIPTION :
* Simple ODBC example to use SQLDescribeCol and SQLBindCol to
* retrieve records from a table based on fixed statement. SQLDescribeCol
* is used to obtain column information which is then used in SQLBindCol
* prior to to reading data from the table.
*
* ODBC USAGE :
* SQLPrepare - to prepare a SELECT statement with 5 cols
* SQLNumResultCols - to retrieve number of columns in prepared
statement
* For each column:
* SQLDescribeCol - to obtain column information
* SQLBindCol - to bind data buffers for column
* SQLExecute - to execute SELECT statement
* SQLFetch - to fetch records until SQL_NO_DATA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>
#include "util.c"
#define MAX_COL_NAME_LEN 256
#define MAX_COLS 5
int main () {
SQLHENV henv = SQL_NULL_HENV; // Environment
SQLHDBC hdbc = SQL_NULL_HDBC; // Connection handle
SQLHSTMT hstmt = SQL_NULL_HSTMT; // Statement handle
SQLRETURN retcode;
SQLCHAR * ColumnName[MAX_COLS];
SQLSMALLINT ColumnNameLen[MAX_COLS];
SQLSMALLINT ColumnDataType[MAX_COLS];
SQLULEN ColumnDataSize[MAX_COLS];
SQLSMALLINT ColumnDataDigits[MAX_COLS];
SQLSMALLINT ColumnDataNullable[MAX_COLS];
SQLCHAR * ColumnData[MAX_COLS];
SQLLEN ColumnDataLen[MAX_COLS];
SQLSMALLINT i,j;
SQLCHAR Statement[]="SELECT PersonID, FirstName, LastName, Address, City FROM TestTBL1 ORDER BY 1, 3, 2, 4, 5";
SQLSMALLINT numCols;
// Initialise buffers
for (i=0;i<MAX_COLS;i++) {
ColumnName[i]=NULL;
ColumnData[i]=NULL;
}
// Allocate environment handle
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
henv, SQL_HANDLE_ENV);
// Set the ODBC version environment attribute
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 connection handle
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_DBC)",
hdbc, SQL_HANDLE_DBC);
// Set login timeout to 5 seconds
retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT,
(SQLPOINTER)5, 0);
CHECK_ERROR(retcode, "SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)",
hdbc, SQL_HANDLE_DBC);
// Connect to data source
retcode = SQLConnect(hdbc, (SQLCHAR*) "DATASOURCE", SQL_NTS,
(SQLCHAR*) NULL, 0, NULL, 0);
CHECK_ERROR(retcode, "SQLConnect(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);
// Prepare Statement (my change to free format input by user)
retcode = SQLPrepare (hstmt, Statement, strlen(Statement));
CHECK_ERROR(retcode, "SQLPrepare(SQL_HANDLE_ENV)",
hstmt, SQL_HANDLE_STMT);
// Retrieve number of columns
retcode = SQLNumResultCols (hstmt, &numCols);
CHECK_ERROR(retcode, "SQLNumResultCols()", hstmt,
SQL_HANDLE_STMT);
printf ("Number of Result Columns %i\n", numCols);
// Loop round number of columns using SQLDescribeCol to get info about
// the column, followed by SQLBindCol to bind the column to a data area
for (i=0;i<numCols;i++) {
ColumnName[i] = (SQLCHAR *) malloc (MAX_COL_NAME_LEN);
retcode = SQLDescribeCol (
hstmt, // Select Statement (Prepared)
i+1, // Columnn Number
ColumnName[i], // Column Name (returned)
MAX_COL_NAME_LEN, // size of Column Name buffer
&ColumnNameLen[i], // Actual size of column name
&ColumnDataType[i], // SQL Data type of column
&ColumnDataSize[i], // Data size of column in table
&ColumnDataDigits[i], // Number of decimal digits
&ColumnDataNullable[i]); // Whether column nullable
CHECK_ERROR(retcode, "SQLDescribeCol()", hstmt, SQL_HANDLE_STMT);
// Display column data
printf("\nColumn : %i\n", i+1);
printf("Column Name : %s\n Column Name Len : %i\n SQL Data Type : %i\n Data Size : %i\n DecimalDigits : %i\n Nullable %i\n",
ColumnName[i], (int)ColumnNameLen[i], (int)ColumnDataType[i],
(int)ColumnDataSize[i], (int)ColumnDataDigits[i],
(int)ColumnDataNullable[i]);
// Bind column, changing SQL data type to C data type
// (assumes INT and VARCHAR for now)
ColumnData[i] = (SQLCHAR *) malloc (ColumnDataSize[i]+1);
switch (ColumnDataType[i]) {
case SQL_INTEGER:
ColumnDataType[i]=SQL_C_LONG;
break;
case SQL_VARCHAR:
ColumnDataType[i]=SQL_C_CHAR;
break;
}
retcode = SQLBindCol (hstmt, // Statement handle
i+1, // Column number
ColumnDataType[i], // C Data Type
ColumnData[i], // Data buffer
ColumnDataSize[i], // Size of Data Buffer
&ColumnDataLen[i]); // Size of data returned
CHECK_ERROR(retcode, "SQLBindCol()", hstmt, SQL_HANDLE_STMT);
}
// Fetch records
printf ("\nRecords ...\n\n");
retcode = SQLExecute (hstmt);
CHECK_ERROR(retcode, "SQLExecute()", hstmt, SQL_HANDLE_STMT);
printf ("\n Data Records\n ------------\n");
for (i=0; ; i++) {
retcode = SQLFetch(hstmt);
//No more data?
if (retcode == SQL_NO_DATA) {
break;
}
CHECK_ERROR(retcode, "SQLFetch()", hstmt, SQL_HANDLE_STMT);
//Display it
printf ("\nRecord %i \n", i+1);
for (j=0;j<numCols;j++) {
printf("Column %s : ", ColumnName[j]);
if (ColumnDataType[j]==SQL_INTEGER) {
printf(" %i\n", (int) *ColumnData[j]);
} else {
printf(" %s\n", rtrim(ColumnData[j], ' '));
}
}
}
exit:
for (i=0;i<numCols;i++) {
if (ColumnName[i]!=NULL) free (ColumnName[i]);
if (ColumnData[i]!=NULL) free (ColumnData[i]);
}
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