[x]
Posted via EE Mobile

Search, ask, and monitor your questions on the go with EE Mobile. Visit Experts Exchange from your mobile device and never be out of touch again.

Question
[x]
Attachment Details

How do I use SQLFetchScroll with dynamic columns using C with ODBC?

Asked by gfrankharris in C Programming Language, Databases Miscellaneous

Tags: C, ODBC, database connectivity

Hi,

I'm new to C and am just now getting into ODBC, and I think I have a bug in my code.  I'm trying to pull data from an ODBC data source as quickly as possible.  To boost the speed, I'm using SQLFetchScroll instead of SQLFetch or SQLGetData.  My goal is to bind my columns so I can pull many rows in at a time.

The catch is I need to have dynamic columns.  I mean the query will not be hard-coded.  Any number of columns of any type may be requested by the user.  So far, I can use dynamic columns and data binding with SQLFetch with just one row at a time; and I can use SQLFetchScroll with many rows if I hard-code some space for pre-determined columns.  But I can't pull in many rows at once with dynamic columns.

One odd thing about my code is that once I call SQLFetchScroll it seems to overwrite my "cols" variable - it goes from 2 to 0.  I doubt that is supposed to happen, but I can't figure out my bug.

I've stripped out everything I can from my code to simplify the debugging.  So right now it is only set up to handle string columns.

I am using Code::Blocks with the MinGW compiler that comes installed with it.  I'm linking to:
C:\Program Files (x86)\CodeBlocks\MinGW\lib\libodbc32.a

Can you help make it work?

Thanks
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
#include <stdio.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include<sqltypes.h>
 
int main() {
    #define BATCHSIZE 10
    #define BUFFERSIZE 1024
    SQLHENV env;
    SQLHDBC dbc;
    SQLRETURN ret;
    SQLHSTMT stmt;
    SQLCHAR *command;
    SQLSMALLINT cols;
    SQLUSMALLINT count;
    SQLUSMALLINT status[BATCHSIZE];
    int i, j, irow;
    SQLCHAR *connectionString;
 
    struct DataBinding {
        SQLPOINTER* columnName;
        SQLPOINTER* SQLtype;
        SQLINTEGER CType;
        SQLINTEGER stringSize;
        SQLCHAR data[BATCHSIZE][100];
        SQLINTEGER status[BATCHSIZE];
    };
 
    connectionString = "DSN=MyDataSource;UID=MyUser;PWD=MyPassword;";
    command = "SELECT TOP 9 stringColumn1,stringColumn2 FROM MyTable";
 
    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
    ret = SQLDriverConnect(dbc, NULL, connectionString, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
    if (SQL_SUCCEEDED(ret)) {
        printf("Connected\n");
    } else {
        fprintf(stderr, "Failed to connect\n");
    }
 
    /* create a statement handle */
    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
 
    /* execute the SQL command */
    SQLExecDirect(stmt, command, SQL_NTS);
 
    /* request the number of columns returned */
    SQLNumResultCols(stmt, &cols);
 
    /* set the number of rows to return in each batch */
    SQLSetStmtAttr(stmt, SQL_ATTR_ROW_ARRAY_SIZE, BATCHSIZE, 0);
 
    /* tell the driver where to tell us how many rows were actually returned */
    SQLSetStmtAttr(stmt, SQL_ATTR_ROWS_FETCHED_PTR, &count, 0);
 
    /* tell the driver where to tell us the status of each row */
    SQLSetStmtAttr(stmt, SQL_ATTR_ROW_STATUS_PTR, status, 0);
 
    /* allocate an array of structures holding the column data */
    struct DataBinding* columnData = (struct DataBinding*)malloc(cols * sizeof(struct DataBinding));
 
    for (i = 0; i < cols; i++) {
        /* allocate memory to hold the column name and type */
        columnData[i].columnName = (SQLPOINTER)malloc(BUFFERSIZE*sizeof(char));
        columnData[i].SQLtype = (SQLPOINTER)malloc(BUFFERSIZE*sizeof(char));
 
        /* request the column name, type, and size */
        SQLColAttribute(stmt, i+1, SQL_DESC_LABEL, columnData[i].columnName, BUFFERSIZE, NULL, NULL);
        SQLColAttribute(stmt, i+1, SQL_DESC_TYPE_NAME, columnData[i].SQLtype, BUFFERSIZE, NULL, NULL);
        SQLColAttribute(stmt, i+1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &columnData[i].stringSize);
 
        /* assign C-types and allocate memory for the batch of rows */
        columnData[i].CType = SQL_C_CHAR;
        SQLBindCol(stmt, i+1, SQL_C_CHAR, columnData[i].data, 100, &columnData[i].status);
    }
 
    irow = 1;
    while (1) {
        ret = SQLFetchScroll(stmt, SQL_FETCH_NEXT, 0);
        if (SQL_SUCCEEDED(ret)) {
            for (i = 0; i < count; i++) {
                if (SQL_SUCCEEDED(status[i])) {
                    for (j = 0; j < cols; j++) {
                        if (columnData[j].status[i] == SQL_NO_TOTAL) {
                            printf("Column %d SQL_NO_TOTAL\n", j);
                        } else if (columnData[j].status[i] == SQL_NULL_DATA) {
                            printf("Column %d SQL_NULL_DATA\n", j);
                        } else {
                            if (columnData[i].CType == SQL_C_CHAR)
                                printf("Column %d data: %s\n\n", j, (wchar_t*)columnData[j].data[i]);
                        }
                    }
                    irow++;
                } else if (status[i] == SQL_ERROR) {
                    printf("SQL_ERROR\n");
                } else if (status[i] == SQL_SUCCESS) {
                    printf("SQL_NO_DATA\n");
                }
            }
        } else {
            break;
        }
    }
 
    /* release the statement handle */
    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
    SQLDisconnect(dbc);
    SQLFreeHandle(SQL_HANDLE_DBC, dbc);
    SQLFreeHandle(SQL_HANDLE_ENV, env);
    printf("Disconnected\n");
    return 0;
}
[+][-]11/05/09 05:44 AM, ID: 25749313Expert Comment

At Experts Exchange, members can ask their questions to thousands of technology professionals, also known as Experts. Experts compete and collaborate to answer those questions by leaving comments like this one.

Start your 30-day free trial to view this Expert Comment or ask the Experts your question.

 
 
Loading Advertisement...
20091021-EE-VQP-81 - Hierarchy / EE_QW_3_20080625