]> granicus.if.org Git - postgresql/commitdiff
Update for version 6-40-0002 and re-merge Thomas' changes.
authorByron Nikolaidis <byronn@insightdist.com>
Tue, 29 Dec 1998 01:49:58 +0000 (01:49 +0000)
committerByron Nikolaidis <byronn@insightdist.com>
Tue, 29 Dec 1998 01:49:58 +0000 (01:49 +0000)
26 files changed:
src/interfaces/odbc/bind.c
src/interfaces/odbc/bind.h
src/interfaces/odbc/connection.c
src/interfaces/odbc/connection.h
src/interfaces/odbc/convert.c
src/interfaces/odbc/convert.h
src/interfaces/odbc/dlg_specific.c
src/interfaces/odbc/dlg_specific.h
src/interfaces/odbc/environ.c
src/interfaces/odbc/execute.c
src/interfaces/odbc/info.c
src/interfaces/odbc/misc.c
src/interfaces/odbc/misc.h
src/interfaces/odbc/options.c
src/interfaces/odbc/pgtypes.c
src/interfaces/odbc/pgtypes.h
src/interfaces/odbc/psqlodbc.c
src/interfaces/odbc/psqlodbc.h
src/interfaces/odbc/psqlodbc.rc
src/interfaces/odbc/qresult.c
src/interfaces/odbc/qresult.h
src/interfaces/odbc/resource.h
src/interfaces/odbc/results.c
src/interfaces/odbc/socket.h
src/interfaces/odbc/statement.c
src/interfaces/odbc/statement.h

index 30e76af255a038f795f7564305d655893d4d449b..512f352181aa3c51699b58f4392c016addba15f5 100644 (file)
@@ -128,12 +128,13 @@ static char *func="SQLBindParameter";
                stmt->parameters[ipar].EXEC_buffer = NULL;
        }
 
-       if (pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
+       /*      Data at exec macro only valid for C char/binary data */
+       if ((fSqlType == SQL_LONGVARBINARY || fSqlType == SQL_LONGVARCHAR) && pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
                stmt->parameters[ipar].data_at_exec = TRUE;
        else
                stmt->parameters[ipar].data_at_exec = FALSE;
 
-       mylog("SQLBindParamater: ipar = %d, *pcbValue = %d, data_at_exec = %d\n", ipar, pcbValue ? *pcbValue: -777, stmt->parameters[ipar].data_at_exec);
+       mylog("SQLBindParamater: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d, rgbValue=%d, *pcbValue = %d, data_at_exec = %d\n", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue ? *pcbValue: -777, stmt->parameters[ipar].data_at_exec);
 
        return SQL_SUCCESS;
 }
@@ -195,6 +196,9 @@ mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol);
 
        icol--;         /* use zero based col numbers from here out */
 
+       /*      Reset for SQLGetData */
+       stmt->bindings[icol].data_left = -1;
+
        if (rgbValue == NULL) {
                /* we have to unbind the column */
                stmt->bindings[icol].buflen = 0;
@@ -357,6 +361,7 @@ int i;
                new_bindings[i].buflen = 0;
                new_bindings[i].buffer = NULL;
                new_bindings[i].used = NULL;
+               new_bindings[i].data_left = -1;
        }
 
        return new_bindings;
index 7d19064e765fa8c8602996e26eb7363367b266ab..39e594f34601c286b0d92123541b99e682f286d9 100644 (file)
@@ -17,6 +17,7 @@
  */
 struct BindInfoClass_ {
        Int4 buflen;            /* size of buffer */
+       Int4 data_left;         /* amount of data left to read (SQLGetData) */
        char *buffer;           /* pointer to the buffer */
        Int4 *used;                     /* used space in the buffer (for strings not counting the '\0') */
        Int2 returntype;        /* kind of conversion to be applied when returning (SQL_C_DEFAULT, SQL_C_CHAR...) */
index c07b0510705f5aa1f1e336d5986395468329e452..bd82f5e7ba48ff31442f31ef099327c68ab7ca17 100644 (file)
@@ -31,7 +31,6 @@
 
 extern GLOBAL_VALUES globals;
 
-// void CC_test(ConnectionClass *self);
 
 RETCODE SQL_API SQLAllocConnect(
                                 HENV     henv,
@@ -252,6 +251,14 @@ ConnectionClass *rv;
                rv->translation_handle = NULL;
                rv->DataSourceToDriver = NULL;
                rv->DriverToDataSource = NULL;
+
+
+               /*      Initialize statement options to defaults */
+               /*      Statements under this conn will inherit these options */
+
+               InitializeStatementOptions(&rv->stmtOptions);
+
+
     } 
     return rv;
 }
@@ -337,7 +344,7 @@ QResultClass *res;
 
                mylog("CC_abort:  sending ABORT!\n");
 
-               res = CC_send_query(self, "ABORT", NULL, NULL);
+               res = CC_send_query(self, "ABORT", NULL);
                CC_set_no_trans(self);
 
                if (res != NULL)
@@ -664,7 +671,7 @@ static char *func="CC_connect";
        /* database really exists on the server machine */
        mylog("sending an empty query...\n");
 
-       res = CC_send_query(self, " ", NULL, NULL);
+       res = CC_send_query(self, " ", NULL);
        if ( res == NULL || QR_get_status(res) != PGRES_EMPTY_QUERY) {
                mylog("got no result from the empty query.  (probably database does not exist)\n");
                self->errornumber = CONNECTION_NO_SUCH_DATABASE;
@@ -691,8 +698,6 @@ static char *func="CC_connect";
        CC_send_settings(self);
        CC_lookup_lo(self);             /* a hack to get the oid of our large object oid type */
 
-       // CC_test(self);
-
        CC_clear_error(self);   /* clear any initial command errors */
        self->status = CONN_CONNECTED;
 
@@ -812,9 +817,9 @@ int rv;
        'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
 */
 QResultClass *
-CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor)
+CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi)
 {
-QResultClass *res = NULL;
+QResultClass *result_in, *res = NULL;
 char id, swallow;
 SocketClass *sock = self->sock;
 static char msgbuffer[MAX_MESSAGE_LEN+1];
@@ -970,7 +975,7 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];  // QR_set_command() dups this string so dont
                        swallow = SOCK_get_char(sock);
                        if ((swallow != '\0') || SOCK_get_errcode(sock) != 0) {
                                self->errornumber = CONNECTION_BACKEND_CRAZY;
-                               self->errormsg = "Unexpected protocol character from backend";
+                               self->errormsg = "Unexpected protocol character from backend (send_query - I)";
                                res = QR_Constructor();
                                QR_set_status(res, PGRES_FATAL_ERROR);
                                return res;
@@ -1006,7 +1011,9 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];        // QR_set_command() dups this string so dont
                        SOCK_get_string(sock, msgbuffer, MAX_MESSAGE_LEN);
                        break;
                case 'T': /* Tuple results start here */
-                       if (result_in == NULL) {
+                       result_in = qi ? qi->result_in : NULL;
+
+                       if ( result_in == NULL) {
                                result_in = QR_Constructor();
                                mylog("send_query: 'T' no result_in: res = %u\n", result_in);
                                if ( ! result_in) {
@@ -1015,7 +1022,10 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];       // QR_set_command() dups this string so dont
                                        return NULL;
                                }
 
-                               if ( ! QR_fetch_tuples(result_in, self, cursor)) {
+                               if (qi)
+                                       QR_set_cache_size(result_in, qi->row_size);
+
+                               if ( ! QR_fetch_tuples(result_in, self, qi ? qi->cursor : NULL)) {
                                        self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
                                        self->errormsg = QR_get_message(result_in);
                                        return NULL;
@@ -1040,7 +1050,7 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];        // QR_set_command() dups this string so dont
                        return res;
                default:
                        self->errornumber = CONNECTION_BACKEND_CRAZY;
-                       self->errormsg = "Unexpected protocol character from backend";
+                       self->errormsg = "Unexpected protocol character from backend (send_query)";
                        CC_set_no_trans(self);
 
                        mylog("send_query: error - %s\n", self->errormsg);
@@ -1058,7 +1068,6 @@ static char msgbuffer[MAX_MESSAGE_LEN+1];
 int i;
 
        mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs);
-//     qlog("conn=%u, func=%d\n", self, fnid);
 
        if (SOCK_get_errcode(sock) != 0) {
                self->errornumber = CONNECTION_COULD_NOT_SEND;
@@ -1124,9 +1133,12 @@ int i;
 
                        return FALSE;
 
+               case 'Z':
+                       break;
+
                default:
                        self->errornumber = CONNECTION_BACKEND_CRAZY;
-                       self->errormsg = "Unexpected protocol character from backend";
+                       self->errormsg = "Unexpected protocol character from backend (send_function, args)";
                        CC_set_no_trans(self);
 
                        mylog("send_function: error - %s\n", self->errormsg);
@@ -1178,7 +1190,7 @@ int i;
 
                default:
                        self->errornumber = CONNECTION_BACKEND_CRAZY;
-                       self->errormsg = "Unexpected protocol character from backend";
+                       self->errormsg = "Unexpected protocol character from backend (send_function, result)";
                        CC_set_no_trans(self);
 
                        mylog("send_function: error - %s\n", self->errormsg);
@@ -1352,107 +1364,3 @@ CC_log_error(char *func, char *desc, ConnectionClass *self)
                qlog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
 }
 
-/*
-void
-CC_test(ConnectionClass *self)
-{
-static char *func = "CC_test";
-HSTMT hstmt1;
-RETCODE result;
-char pktab[255], fktab[255], pkcol[255], fkcol[255], tgname[255];
-SDWORD pktab_len, pkcol_len, fktab_len, fkcol_len, ur_len, dr_len, tgname_len;
-SWORD cols, seq;
-SDWORD update_rule, delete_rule;
-
-       mylog( "%s: entering...\n", func);
-
-       result = SQLAllocStmt( self, &hstmt1);
-       if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
-               return;
-       }
-
-       result = SQLPrimaryKeys(hstmt1, NULL, 0, NULL, 0, "t1", SQL_NTS);
-
-       qlog("SQLPrimaryKeys result = %d\n", result);
-
-       result = SQLNumResultCols(hstmt1, &cols);
-       qlog("cols SQLTables result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 3, SQL_C_CHAR, pktab, sizeof(pktab), &pktab_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 4, SQL_C_CHAR, pkcol, sizeof(pkcol), &pkcol_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 5, SQL_C_SHORT, &seq, 0, NULL);
-       qlog("bind result = %d\n", result);
-
-       result = SQLFetch(hstmt1);
-       qlog("SQLFetch result = %d\n", result);
-       while (result != SQL_NO_DATA_FOUND) {
-               qlog("fetch on stmt1: result = %d, pktab='%s', pkcol='%s', seq=%d\n", 
-                       result, pktab, pkcol, seq);
-
-               result = SQLFetch(hstmt1);
-       }
-       qlog("SQLFetch result = %d\n", result);
-
-       // Test of case #1 
-       result = SQLForeignKeys(hstmt1, "", SQL_NTS, "", SQL_NTS, "t1", SQL_NTS, 
-               NULL, 0, NULL, 0, NULL, 0);
-
-       //      Test of case #2 
-       result = SQLForeignKeys(hstmt1, "", SQL_NTS, "", SQL_NTS, NULL, 0, 
-               NULL, 0, NULL, 0, "ar_register", SQL_NTS);
-
-
-       //      Test of case #3 
-       result = SQLForeignKeys(hstmt1, NULL, 0, NULL, 0, "employee", SQL_NTS, 
-               NULL, 0, NULL, 0, "invoice", SQL_NTS);
-
-       qlog("SQLForeignKeys result = %d\n", result);
-
-       result = SQLNumResultCols(hstmt1, &cols);
-       qlog("cols SQLTables result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 3, SQL_C_CHAR, pktab, sizeof(pktab), &pktab_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 4, SQL_C_CHAR, pkcol, sizeof(pkcol), &pkcol_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 7, SQL_C_CHAR, fktab, sizeof(fktab), &fktab_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 8, SQL_C_CHAR, fkcol, sizeof(fkcol), &fkcol_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 9, SQL_C_SHORT, &seq, 0, NULL);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 10, SQL_C_LONG, &update_rule, 0, &ur_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 11, SQL_C_LONG, &delete_rule, 0, &dr_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLBindCol(hstmt1, 14, SQL_C_CHAR, tgname, sizeof(tgname), &tgname_len);
-       qlog("bind result = %d\n", result);
-
-       result = SQLFetch(hstmt1);
-       qlog("SQLFetch result = %d\n", result);
-       while (result != SQL_NO_DATA_FOUND) {
-               qlog("fetch on stmt1: result = %d, pktab='%s', pkcol='%s', fktab='%s', fkcol='%s', seq=%d, update_rule=%d, ur_len=%d, delete_rule=%d, dr_len=%d, tgname='%s', tgname_len=%d\n", 
-                       result, pktab, pkcol, fktab, fkcol, seq, update_rule, ur_len, delete_rule, dr_len, tgname, tgname_len);
-
-               result = SQLFetch(hstmt1);
-       }
-       qlog("SQLFetch result = %d\n", result);
-
-       SQLFreeStmt(hstmt1, SQL_DROP);
-
-}
-*/
-
-
-
index 3340e15cbeed0a2cc8a14b25970c16b225d31fc1..aaf0fffebbd0a578c5d04a3a6e69219590d71704 100644 (file)
@@ -35,33 +35,34 @@ typedef enum {
 } CONN_Status;
 
 /*     These errors have general sql error state */
-#define CONNECTION_SERVER_NOT_REACHED 1
-#define CONNECTION_MSG_TOO_LONG 3
-#define CONNECTION_COULD_NOT_SEND 4
-#define CONNECTION_NO_SUCH_DATABASE 5
-#define CONNECTION_BACKEND_CRAZY 6
-#define CONNECTION_NO_RESPONSE 7
-#define CONNECTION_SERVER_REPORTED_ERROR 8
-#define CONNECTION_COULD_NOT_RECEIVE 9
-#define CONNECTION_SERVER_REPORTED_WARNING 10
-#define CONNECTION_NEED_PASSWORD 12
+#define CONNECTION_SERVER_NOT_REACHED 101
+#define CONNECTION_MSG_TOO_LONG 103
+#define CONNECTION_COULD_NOT_SEND 104
+#define CONNECTION_NO_SUCH_DATABASE 105
+#define CONNECTION_BACKEND_CRAZY 106
+#define CONNECTION_NO_RESPONSE 107
+#define CONNECTION_SERVER_REPORTED_ERROR 108
+#define CONNECTION_COULD_NOT_RECEIVE 109
+#define CONNECTION_SERVER_REPORTED_WARNING 110
+#define CONNECTION_NEED_PASSWORD 112
 
 /*     These errors correspond to specific SQL states */
-#define CONN_INIREAD_ERROR 1
-#define CONN_OPENDB_ERROR 2
-#define CONN_STMT_ALLOC_ERROR 3
-#define CONN_IN_USE 4 
-#define CONN_UNSUPPORTED_OPTION 5
+#define CONN_INIREAD_ERROR 201
+#define CONN_OPENDB_ERROR 202
+#define CONN_STMT_ALLOC_ERROR 203
+#define CONN_IN_USE 20
+#define CONN_UNSUPPORTED_OPTION 205
 /* Used by SetConnectoption to indicate unsupported options */
-#define CONN_INVALID_ARGUMENT_NO 6
+#define CONN_INVALID_ARGUMENT_NO 206
 /* SetConnectOption: corresponds to ODBC--"S1009" */
-#define CONN_TRANSACT_IN_PROGRES 7
-#define CONN_NO_MEMORY_ERROR 8
-#define CONN_NOT_IMPLEMENTED_ERROR 9
-#define CONN_INVALID_AUTHENTICATION 10
-#define CONN_AUTH_TYPE_UNSUPPORTED 11
-#define CONN_UNABLE_TO_LOAD_DLL 12
+#define CONN_TRANSACT_IN_PROGRES 207
+#define CONN_NO_MEMORY_ERROR 208
+#define CONN_NOT_IMPLEMENTED_ERROR 209
+#define CONN_INVALID_AUTHENTICATION 210
+#define CONN_AUTH_TYPE_UNSUPPORTED 211
+#define CONN_UNABLE_TO_LOAD_DLL 212
 
+#define CONN_OPTION_VALUE_CHANGED 213
 
 /* Conn_status defines */
 #define CONN_IN_AUTOCOMMIT 0x01
@@ -200,6 +201,7 @@ typedef BOOL (FAR WINAPI *DriverToDataSourceProc) (UDWORD,
 /*******       The Connection handle   ************/
 struct ConnectionClass_ {
        HENV                    henv;                                   /* environment this connection was created on */
+       StatementOptions stmtOptions;
        char                    *errormsg;
        int                             errornumber;
        CONN_Status             status;
@@ -244,7 +246,7 @@ char CC_connect(ConnectionClass *self, char do_password);
 char CC_add_statement(ConnectionClass *self, StatementClass *stmt);
 char CC_remove_statement(ConnectionClass *self, StatementClass *stmt);
 char CC_get_error(ConnectionClass *self, int *number, char **message);
-QResultClass *CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor);
+QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi);
 void CC_clear_error(ConnectionClass *self);
 char *CC_create_errormsg(ConnectionClass *self);
 int CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs);
index a5b3cbfd3f4c354b762ca4c99e86692d379c7986..73e92e16c04e4f985e0b6431415d98db632f8d8b 100644 (file)
@@ -101,18 +101,38 @@ copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *val
 BindInfoClass *bic = &(stmt->bindings[col]);
 
        return copy_and_convert_field(stmt, field_type, value, (Int2)bic->returntype, (PTR)bic->buffer,
-                                (SDWORD)bic->buflen, (SDWORD *)bic->used, FALSE);
+                                (SDWORD)bic->buflen, (SDWORD *)bic->used);
 }
 
 /*     This is called by SQLGetData() */
 int
 copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType, 
-                                          PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue, char multiple)
+                                          PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue)
 {
-Int4 len = 0;
+Int4 len = 0, copy_len = 0;
 SIMPLE_TIME st;
 time_t t = time(NULL);
 struct tm *tim;
+int pcbValueOffset, rgbValueOffset;
+char *rgbValueBindRow, *ptr;
+int bind_row = stmt->bind_row;
+int bind_size = stmt->options.bind_size;
+int result = COPY_OK;
+char tempBuf[TEXT_FIELD_SIZE+5];
+
+/*     rgbValueOffset is *ONLY* for character and binary data */
+/*     pcbValueOffset is for computing any pcbValue location */
+
+       if (bind_size > 0) {
+
+               pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
+       }
+       else {
+
+               pcbValueOffset = bind_row * sizeof(SDWORD);
+               rgbValueOffset = bind_row * cbValueMax;
+
+       }
 
        memset(&st, 0, sizeof(SIMPLE_TIME));
 
@@ -122,13 +142,13 @@ struct tm *tim;
        st.d = tim->tm_mday;
        st.y = tim->tm_year + 1900;
 
-       mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, value, cbValueMax);
+       mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType,  (value==NULL)?"<NULL>":value, cbValueMax);
 
        if ( ! value) {
         /* handle a null just by returning SQL_NULL_DATA in pcbValue, */
         /* and doing nothing to the buffer.                           */
         if(pcbValue) {
-            *pcbValue = SQL_NULL_DATA;
+                       *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = SQL_NULL_DATA;
         }
                return COPY_OK;
        }
@@ -191,7 +211,7 @@ struct tm *tim;
        /* This is for internal use by SQLStatistics() */
        case PG_TYPE_INT28: {
                // this is an array of eight integers
-               short *short_array = (short *)rgbValue;
+               short *short_array = (short *) ( (char *) rgbValue + rgbValueOffset);
 
                len = 16;
 
@@ -207,7 +227,7 @@ struct tm *tim;
 
                /*  There is no corresponding fCType for this. */
                if(pcbValue)
-                       *pcbValue = len;
+                       *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len;
 
                return COPY_OK;         /* dont go any further or the data will be trashed */
                                                }
@@ -215,12 +235,12 @@ struct tm *tim;
        /* This is a large object OID, which is used to store LONGVARBINARY objects. */
        case PG_TYPE_LO:
 
-               return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple);
+               return convert_lo( stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
 
        default:
 
                if (field_type == stmt->hdbc->lobj_type)        /* hack until permanent type available */
-                       return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple);
+                       return convert_lo( stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
        }
 
        /*  Change default into something useable */
@@ -231,21 +251,24 @@ struct tm *tim;
        }
 
 
+       rgbValueBindRow = (char *) rgbValue + rgbValueOffset;
+
     if(fCType == SQL_C_CHAR) {
 
+
                /*      Special character formatting as required */
                /*      These really should return error if cbValueMax is not big enough. */
                switch(field_type) {
                case PG_TYPE_DATE:
                    len = 10;
                        if (cbValueMax > len)
-                               sprintf((char *)rgbValue, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
+                               sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
                        break;
 
                case PG_TYPE_TIME:
                        len = 8;
                        if (cbValueMax > len)
-                               sprintf((char *)rgbValue, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
+                               sprintf(rgbValueBindRow, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
                        break;
 
                case PG_TYPE_ABSTIME:
@@ -253,15 +276,15 @@ struct tm *tim;
                case PG_TYPE_TIMESTAMP:
                        len = 19;
                        if (cbValueMax > len)
-                               sprintf((char *) rgbValue, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", 
+                               sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", 
                                        st.y, st.m, st.d, st.hh, st.mm, st.ss);
                        break;
 
                case PG_TYPE_BOOL:
                        len = 1;
                        if (cbValueMax > len) {
-                               strcpy((char *) rgbValue, value);
-                               mylog("PG_TYPE_BOOL: rgbValue = '%s'\n", rgbValue);
+                               strcpy(rgbValueBindRow, value);
+                               mylog("PG_TYPE_BOOL: rgbValueBindRow = '%s'\n", rgbValueBindRow);
                        }
                        break;
 
@@ -276,15 +299,48 @@ struct tm *tim;
                        object used to store those.
                */
                case PG_TYPE_BYTEA:             // convert binary data to hex strings (i.e, 255 = "FF")
-                       len = convert_pgbinary_to_char(value, rgbValue, cbValueMax);
+                       len = convert_pgbinary_to_char(value, rgbValueBindRow, cbValueMax);
+
+                       /***** THIS IS NOT PROPERLY IMPLEMENTED *****/
                        break;
 
                default:
                        /*      convert linefeeds to carriage-return/linefeed */
-                       convert_linefeeds( (char *) value, rgbValue, cbValueMax);
-                   len = strlen(rgbValue);
+                       len = convert_linefeeds(value, tempBuf, sizeof(tempBuf));
+                       ptr = tempBuf;
+
+                       mylog("DEFAULT: len = %d, ptr = '%s'\n", len, ptr);
+
+                       if (stmt->current_col >= 0) {
+                               if (stmt->bindings[stmt->current_col].data_left == 0)
+                                       return COPY_NO_DATA_FOUND;
+                               else if (stmt->bindings[stmt->current_col].data_left > 0) {
+                                       ptr += len - stmt->bindings[stmt->current_col].data_left;
+                                       len = stmt->bindings[stmt->current_col].data_left;
+                               }
+                               else
+                                       stmt->bindings[stmt->current_col].data_left = strlen(value);
+                       }
+
+                       if (cbValueMax > 0) {
+                               
+                               copy_len = (len >= cbValueMax) ? cbValueMax -1 : len;
 
-                       mylog("    SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValue = '%s'\n", len, cbValueMax, rgbValue);
+                               /*      Copy the data */
+                               strncpy_null(rgbValueBindRow, ptr, copy_len + 1);
+
+                               /*      Adjust data_left for next time */
+                               if (stmt->current_col >= 0) {
+                                       stmt->bindings[stmt->current_col].data_left -= copy_len;
+                               }
+                       }
+
+                       /*      Finally, check for truncation so that proper status can be returned */
+                       if ( len >= cbValueMax)
+                               result = COPY_RESULT_TRUNCATED;
+
+
+                       mylog("    SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
                        break;
                }
 
@@ -301,7 +357,13 @@ struct tm *tim;
                case SQL_C_DATE:
                        len = 6;
                        {
-                       DATE_STRUCT *ds = (DATE_STRUCT *) rgbValue;
+                       DATE_STRUCT *ds;
+                       
+                       if (bind_size > 0) {
+                               ds = (DATE_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
+                       } else {
+                               ds = (DATE_STRUCT *) rgbValue + bind_row;
+                       }
                        ds->year = st.y;
                        ds->month = st.m;
                        ds->day = st.d;
@@ -311,7 +373,13 @@ struct tm *tim;
                case SQL_C_TIME:
                        len = 6;
                        {
-                       TIME_STRUCT *ts = (TIME_STRUCT *) rgbValue;
+                       TIME_STRUCT *ts;
+                       
+                       if (bind_size > 0) {
+                               ts = (TIME_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
+                       } else {
+                               ts = (TIME_STRUCT *) rgbValue + bind_row;
+                       }
                        ts->hour = st.hh;
                        ts->minute = st.mm;
                        ts->second = st.ss;
@@ -321,7 +389,12 @@ struct tm *tim;
                case SQL_C_TIMESTAMP:                                   
                        len = 16;
                        {
-                       TIMESTAMP_STRUCT *ts = (TIMESTAMP_STRUCT *) rgbValue;
+                       TIMESTAMP_STRUCT *ts;
+                       if (bind_size > 0) {
+                               ts = (TIMESTAMP_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
+                       } else {
+                               ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row;
+                       }
                        ts->year = st.y;
                        ts->month = st.m;
                        ts->day = st.d;
@@ -334,59 +407,132 @@ struct tm *tim;
 
                case SQL_C_BIT:
                        len = 1;
-                       *((UCHAR *)rgbValue) = atoi(value);
-                       mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue));
+                       if (bind_size > 0) {
+                               *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
+                       } else {
+                               *((UCHAR *)rgbValue + bind_row) = atoi(value);
+                       }
+                       // mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue));
                        break;
 
                case SQL_C_STINYINT:
                case SQL_C_TINYINT:
                        len = 1;
-                       *((SCHAR *) rgbValue) = atoi(value);
+                       if (bind_size > 0) {
+                               *(SCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
+                       } else {
+                               *((SCHAR *) rgbValue + bind_row) = atoi(value);
+                       }
                        break;
 
                case SQL_C_UTINYINT:
                        len = 1;
-                       *((UCHAR *) rgbValue) = atoi(value);
+                       if (bind_size > 0) {
+                               *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
+                       } else {
+                               *((UCHAR *) rgbValue + bind_row) = atoi(value);
+                       }
                        break;
 
                case SQL_C_FLOAT:
                        len = 4;
-                       *((SFLOAT *)rgbValue) = (float) atof(value);
+                       if (bind_size > 0) {
+                               *(SFLOAT *) ((char *) rgbValue + (bind_row * bind_size)) = (float) atof(value);
+                       } else {
+                               *((SFLOAT *)rgbValue + bind_row) = (float) atof(value);
+                       }
                        break;
 
                case SQL_C_DOUBLE:
                        len = 8;
-                       *((SDOUBLE *)rgbValue) = atof(value);
+                       if (bind_size > 0) {
+                               *(SDOUBLE *) ((char *) rgbValue + (bind_row * bind_size)) = atof(value);
+                       } else {
+                               *((SDOUBLE *)rgbValue + bind_row) = atof(value);
+                       }
                        break;
 
                case SQL_C_SSHORT:
                case SQL_C_SHORT:
                        len = 2;
-                       *((SWORD *)rgbValue) = atoi(value);
+                       if (bind_size > 0) {
+                               *(SWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
+                       } else {
+                               *((SWORD *)rgbValue + bind_row) = atoi(value);
+                       }
                        break;
 
                case SQL_C_USHORT:
                        len = 2;
-                       *((UWORD *)rgbValue) = atoi(value);
+                       if (bind_size > 0) {
+                               *(UWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
+                       } else {
+                               *((UWORD *)rgbValue + bind_row) = atoi(value);
+                       }
                        break;
 
                case SQL_C_SLONG:
                case SQL_C_LONG:
                        len = 4;
-                       *((SDWORD *)rgbValue) = atol(value);
+                       if (bind_size > 0) {
+                               *(SDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(value);
+                       } else {
+                               *((SDWORD *)rgbValue + bind_row) = atol(value);
+                       }
                        break;
 
                case SQL_C_ULONG:
                        len = 4;
-                       *((UDWORD *)rgbValue) = atol(value);
+                       if (bind_size > 0) {
+                               *(UDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(value);
+                       } else {
+                               *((UDWORD *)rgbValue + bind_row) = atol(value);
+                       }
                        break;
 
                case SQL_C_BINARY:      
 
                        //      truncate if necessary
                        //      convert octal escapes to bytes
-                       len = convert_from_pgbinary(value, rgbValue, cbValueMax);
-                       mylog("SQL_C_BINARY: len = %d\n", len);
+
+                       len = convert_from_pgbinary(value, tempBuf, sizeof(tempBuf));
+                       ptr = tempBuf;
+
+                       if (stmt->current_col >= 0) {
+
+                               /*      No more data left for this column */
+                               if (stmt->bindings[stmt->current_col].data_left == 0)
+                                       return COPY_NO_DATA_FOUND;
+
+                               /*      Second (or more) call to SQLGetData so move the pointer */
+                               else if (stmt->bindings[stmt->current_col].data_left > 0) {
+                                       ptr += len - stmt->bindings[stmt->current_col].data_left;
+                                       len = stmt->bindings[stmt->current_col].data_left;
+                               }
+
+                               /*      First call to SQLGetData so initialize data_left */
+                               else    
+                                       stmt->bindings[stmt->current_col].data_left = len;
+
+                       }
+
+                       if (cbValueMax > 0) {
+                               copy_len = (len > cbValueMax) ? cbValueMax : len;
+
+                               /*      Copy the data */
+                               memcpy(rgbValueBindRow, ptr, copy_len);
+
+                               /*      Adjust data_left for next time */
+                               if (stmt->current_col >= 0) {
+                                       stmt->bindings[stmt->current_col].data_left -= copy_len;
+                               }
+                       }
+
+                       /*      Finally, check for truncation so that proper status can be returned */
+                       if ( len > cbValueMax)
+                               result = COPY_RESULT_TRUNCATED;
+
+                       mylog("SQL_C_BINARY: len = %d, copy_len = %d\n", len, copy_len);
                        break;
                        
                default:
@@ -395,10 +541,11 @@ struct tm *tim;
        }
 
     // store the length of what was copied, if there's a place for it
-    if(pcbValue)
-        *pcbValue = len;
+    if(pcbValue) {
+        *(SDWORD *) ((char *)pcbValue + pcbValueOffset) = len;
+       }
 
-       return COPY_OK;
+       return result;
 
 }
 
@@ -423,6 +570,8 @@ struct tm *tim;
 SDWORD used;
 char *buffer, *buf;
 char in_quote = FALSE;
+Oid  lobj_oid;
+int lobj_fd, retval;
 
 
        if ( ! old_statement) {
@@ -731,18 +880,50 @@ char in_quote = FALSE;
                case SQL_VARBINARY:                     /* non-ascii characters should be converted to octal */
                        new_statement[npos++] = '\'';   /*    Open Quote */
 
-                       mylog("SQL_LONGVARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
+                       mylog("SQL_VARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
 
                        npos += convert_to_pgbinary(buf, &new_statement[npos], used);
 
                        new_statement[npos++] = '\'';   /*    Close Quote */
                        
                        break;
+
                case SQL_LONGVARBINARY:         
+
+                       if ( stmt->parameters[param_number].data_at_exec) {
+
+                               lobj_oid = stmt->parameters[param_number].lobj_oid;
+
+                       }
+                       else {
+
+                               /*      store the oid */
+                               lobj_oid = lo_creat(stmt->hdbc, INV_READ | INV_WRITE);
+                               if (lobj_oid == 0) {
+                                       stmt->errornumber = STMT_EXEC_ERROR;
+                                       stmt->errormsg = "Couldnt create (in-line) large object.";
+                                       SC_log_error(func, "", stmt);
+                                       return SQL_ERROR;
+                               }
+
+                               /*      store the fd */
+                               lobj_fd = lo_open(stmt->hdbc, lobj_oid, INV_WRITE);
+                               if ( lobj_fd < 0) {
+                                       stmt->errornumber = STMT_EXEC_ERROR;
+                                       stmt->errormsg = "Couldnt open (in-line) large object for writing.";
+                                       SC_log_error(func, "", stmt);
+                                       return SQL_ERROR;
+                               }
+
+                               retval = lo_write(stmt->hdbc, lobj_fd, buffer, used);
+
+                               lo_close(stmt->hdbc, lobj_fd);
+                       }
+
                        /*      the oid of the large object -- just put that in for the
                                parameter marker -- the data has already been sent to the large object
                        */
-                       sprintf(param_string, "%d", stmt->parameters[param_number].lobj_oid);
+                       sprintf(param_string, "%d", lobj_oid);
                        strcpy(&new_statement[npos], param_string);
                        npos += strlen(param_string);
 
@@ -912,38 +1093,27 @@ int nf;
 }
 
 /*     Change linefeed to carriage-return/linefeed */
-char *
+int
 convert_linefeeds(char *si, char *dst, size_t max)
 {
 size_t i = 0, out = 0;
-static char sout[TEXT_FIELD_SIZE+5];
-char *p;
-
-       if (dst)
-               p = dst;
-       else {
-               p = sout;
-               max = sizeof(sout);
-       }
-
-       p[0] = '\0';
 
-       for (i = 0; i < strlen(si) && out < max; i++) {
+       for (i = 0; i < strlen(si) && out < max - 1; i++) {
                if (si[i] == '\n') {
                        /*      Only add the carriage-return if needed */
                        if (i > 0 && si[i-1] == '\r') {
-                               p[out++] = si[i];
+                               dst[out++] = si[i];
                                continue;
                        }
 
-                       p[out++] = '\r';
-                       p[out++] = '\n';
+                       dst[out++] = '\r';
+                       dst[out++] = '\n';
                }
                else
-                       p[out++] = si[i];
+                       dst[out++] = si[i];
        }
-       p[out] = '\0';
-       return p;
+       dst[out] = '\0';
+       return out;
 }
 
 /*     Change carriage-return/linefeed to just linefeed 
@@ -1029,6 +1199,7 @@ convert_from_pgbinary(unsigned char *value, unsigned char *rgbValue, int cbValue
 {
 size_t i;
 int o=0;
+
        
        for (i = 0; i < strlen(value); ) {
                if (value[i] == '\\') {
@@ -1042,7 +1213,7 @@ int o=0;
                o++;
        }
 
-       rgbValue[o] = '\0';
+       rgbValue[o] = '\0';     // extra protection
 
        return o;
 }
@@ -1075,12 +1246,13 @@ int i, o=0;
 
        for (i = 0; i < len; i++) {
                mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]);
-               if (in[i] < 32 || in[i] > 126) {
+               if ( isalnum(in[i]) || in[i] == ' ') {
+                       out[o++] = in[i];
+               }
+               else {
                        strcpy(&out[o], conv_to_octal(in[i])); 
                        o += 5;
                }
-               else
-                       out[o++] = in[i];
 
        }
 
@@ -1150,15 +1322,25 @@ unsigned int i, o = 0;
 */
 int
 convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue, 
-                  SDWORD cbValueMax, SDWORD *pcbValue, char multiple)
+                  SDWORD cbValueMax, SDWORD *pcbValue)
 {
 Oid oid;
-int retval;
+int retval, result, left = -1;
+int bind_row = stmt->bind_row;
+BindInfoClass *bindInfo = NULL;
+
+
+/*     If using SQLGetData, then current_col will be set */
+       if (stmt->current_col >= 0) {
+               bindInfo = &stmt->bindings[stmt->current_col];
+               left = bindInfo->data_left;
+       }
 
        /*      if this is the first call for this column,
                open the large object for reading 
        */
-       if ( ! multiple) {
+
+       if ( ! bindInfo || bindInfo->data_left == -1) {
                oid = atoi(value);
                stmt->lobj_fd = lo_open(stmt->hdbc, oid, INV_READ);
                if (stmt->lobj_fd < 0) {
@@ -1166,6 +1348,22 @@ int retval;
                        stmt->errormsg = "Couldnt open large object for reading.";
                        return COPY_GENERAL_ERROR;
                }
+
+               /*      Get the size */
+               retval = lo_lseek(stmt->hdbc, stmt->lobj_fd, 0L, SEEK_END);
+               if (retval >= 0) {
+
+                       left = lo_tell(stmt->hdbc, stmt->lobj_fd);
+                       if (bindInfo)
+                               bindInfo->data_left = left;
+
+                       /*      return to beginning */
+                       lo_lseek(stmt->hdbc, stmt->lobj_fd, 0L, SEEK_SET);
+               }
+       }
+
+       if (left == 0) {
+               return COPY_NO_DATA_FOUND;
        }
 
        if (stmt->lobj_fd < 0) {
@@ -1174,7 +1372,7 @@ int retval;
                return COPY_GENERAL_ERROR;
        }
 
-       retval = lo_read(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValueMax);
+       retval = lo_read(stmt->hdbc, stmt->lobj_fd, (char *) rgbValue, cbValueMax);
        if (retval < 0) {
                lo_close(stmt->hdbc, stmt->lobj_fd);
                stmt->lobj_fd = -1;
@@ -1183,20 +1381,26 @@ int retval;
                stmt->errormsg = "Error reading from large object.";
                return COPY_GENERAL_ERROR;
        }
-       else if (retval < cbValueMax)  {        /* success, all done */
-               lo_close(stmt->hdbc, stmt->lobj_fd);
-               stmt->lobj_fd = -1;     /* prevent further reading */
 
-               if (pcbValue)
-                       *pcbValue = retval;
+       if (retval < left)
+               result = COPY_RESULT_TRUNCATED;
+       else
+               result = COPY_OK;
+
+       if (pcbValue)
+               *pcbValue = left < 0 ? SQL_NO_TOTAL : left;
 
-               return COPY_OK;
-       }
-       else {  /* retval == cbVaueMax -- assume truncated */
-               if (pcbValue)
-                       *pcbValue = SQL_NO_TOTAL;
 
-               return COPY_RESULT_TRUNCATED;
+       if (bindInfo && bindInfo->data_left > 0) 
+               bindInfo->data_left -= retval;
 
+
+       if (! bindInfo || bindInfo->data_left == 0) {
+               lo_close(stmt->hdbc, stmt->lobj_fd);
+               stmt->lobj_fd = -1;     /* prevent further reading */
        }
+
+
+       return result;
+
 }
index 8a75f647fab11db07802e5bff49d064d33ba1424..7fd8254779075d9b2e4caa6fdc65e0d08024f51d 100644 (file)
@@ -31,13 +31,13 @@ typedef struct {
 
 int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col);
 int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType, 
-                                                  PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue, char multiple);
+                                                  PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue);
 
 int copy_statement_with_parameters(StatementClass *stmt);
 char *convert_escape(char *value);
 char *convert_money(char *s);
 char parse_datetime(char *buf, SIMPLE_TIME *st);
-char *convert_linefeeds(char *s, char *dst, size_t max);
+int convert_linefeeds(char *s, char *dst, size_t max);
 char *convert_special_chars(char *si, char *dst, int used);
 
 int convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax);
@@ -46,6 +46,6 @@ int convert_to_pgbinary(unsigned char *in, char *out, int len);
 void encode(char *in, char *out);
 void decode(char *in, char *out);
 int convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue, 
-                  SDWORD cbValueMax, SDWORD *pcbValue, char multiple);
+                  SDWORD cbValueMax, SDWORD *pcbValue);
 
 #endif
index 371b2d7ad891a32ebf86191da1b52d70764cecdf..ac83a2345f77b514030f2f95ef8b45c72511eb85 100644 (file)
@@ -112,6 +112,8 @@ int CALLBACK driver_optionsProc(HWND   hdlg,
 
                CheckDlgButton(hdlg, DRV_PARSE, globals.parse);
 
+               CheckDlgButton(hdlg, DRV_CANCELASFREESTMT, globals.cancel_as_freestmt);
+
                SetDlgItemInt(hdlg, DRV_CACHE_SIZE, globals.fetch_max, FALSE);
                SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, globals.max_varchar_size, FALSE);
                SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, globals.max_longvarchar_size, TRUE);
@@ -150,6 +152,8 @@ int CALLBACK driver_optionsProc(HWND   hdlg,
 
                        globals.parse = IsDlgButtonChecked(hdlg, DRV_PARSE);
 
+                       globals.cancel_as_freestmt = IsDlgButtonChecked(hdlg, DRV_CANCELASFREESTMT);
+
                        globals.fetch_max = GetDlgItemInt(hdlg, DRV_CACHE_SIZE, NULL, FALSE);
                        globals.max_varchar_size = GetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, NULL, FALSE);
                        globals.max_longvarchar_size= GetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, NULL, TRUE);    // allows for SQL_NO_TOTAL
@@ -176,6 +180,7 @@ int CALLBACK driver_optionsProc(HWND   hdlg,
                        CheckDlgButton(hdlg, DRV_USEDECLAREFETCH, DEFAULT_USEDECLAREFETCH);
        
                        CheckDlgButton(hdlg, DRV_PARSE, DEFAULT_PARSE);
+                       CheckDlgButton(hdlg, DRV_CANCELASFREESTMT, DEFAULT_CANCELASFREESTMT);
 
                        /*      Unknown Sizes */
                        CheckDlgButton(hdlg, DRV_UNKNOWN_DONTKNOW, 0);
@@ -682,6 +687,14 @@ char temp[256];
        else if ( ! override)
                globals.parse = DEFAULT_PARSE;
 
+       //      SQLCancel calls SQLFreeStmt in Driver Manager
+       SQLGetPrivateProfileString(section, INI_CANCELASFREESTMT, "", 
+                               temp, sizeof(temp), filename);
+       if ( temp[0] ) 
+               globals.cancel_as_freestmt = atoi(temp);
+       else if ( ! override)
+               globals.cancel_as_freestmt = DEFAULT_CANCELASFREESTMT;
+
        //      Readonly is stored in the driver section AND per datasource
        SQLGetPrivateProfileString(section, INI_READONLY, "", 
                                temp, sizeof(temp), filename);
@@ -818,6 +831,10 @@ char tmp[128];
        SQLWritePrivateProfileString(DBMS_NAME,
                INI_PARSE, tmp, ODBCINST_INI);
 
+       sprintf(tmp, "%d", globals.cancel_as_freestmt);
+       SQLWritePrivateProfileString(DBMS_NAME,
+               INI_CANCELASFREESTMT, tmp, ODBCINST_INI);
+
        sprintf(tmp, "%d", globals.max_varchar_size);
        SQLWritePrivateProfileString(DBMS_NAME,
                INI_MAXVARCHARSIZE, tmp, ODBCINST_INI);
index 829c9aeab7d0f56659b64992d2f9ca83e06c755e..02a77889967afde0faf0ff04c36939b51cebf919 100644 (file)
@@ -64,6 +64,8 @@
 #define INI_UNIQUEINDEX   "UniqueIndex"                /* Recognize unique indexes */
 #define INI_UNKNOWNSIZES  "UnknownSizes"       /* How to handle unknown result set sizes */
 
+#define INI_CANCELASFREESTMT  "CancelAsFreeStmt"
+
 #define INI_USEDECLAREFETCH "UseDeclareFetch"          /* Use Declare/Fetch cursors */
 
 /*     More ini stuff */
 #define DEFAULT_LIE                                            0
 #define DEFAULT_PARSE                                  0
 
+#define DEFAULT_CANCELASFREESTMT               0
+
 #define DEFAULT_EXTRASYSTABLEPREFIXES  "dd_;"
 
 /*  prototypes */
index f5c2798b5e7418b5933e40aa54b6382fbce96699..270524cf05b2172e6a646bc6e1efe2fd9d1bb1c7 100644 (file)
@@ -144,6 +144,14 @@ int status;
                     strcpy(szSqlState, "S1000");
                     // general error
                     break;  
+                               case STMT_ROW_OUT_OF_RANGE:
+                                       strcpy(szSqlState, "S1107");
+                                       break;
+
+                               case STMT_OPERATION_CANCELLED:
+                                       strcpy(szSqlState, "S1008");
+                                       break;
+
                 case STMT_NOT_IMPLEMENTED_ERROR:
                     strcpy(szSqlState, "S1C00"); // == 'driver not capable'
                     break;
@@ -171,7 +179,15 @@ int status;
                                case STMT_NO_CURSOR_NAME:
                     strcpy(szSqlState, "S1015");
                     break;
-                default:
+                case STMT_INVALID_ARGUMENT_NO:
+                    strcpy(szSqlState, "S1009");
+                    // invalid argument value
+                    break;
+                               case STMT_INVALID_CURSOR_POSITION:
+                    strcpy(szSqlState, "S1109");
+                    break;
+                
+                               default:
                     strcpy(szSqlState, "S1000");
                     // also a general error
                     break;
@@ -218,6 +234,10 @@ int status;
             
             if (NULL != szSqlState) 
                 switch(status) {
+                               case STMT_OPTION_VALUE_CHANGED:
+                               case CONN_OPTION_VALUE_CHANGED:
+                    strcpy(szSqlState, "01S02");
+                                       break;
                 case CONN_INIREAD_ERROR:
                     strcpy(szSqlState, "IM002");
                     // data source not found
@@ -254,6 +274,7 @@ int status;
                     strcpy(szSqlState, "S1001");
                     break;
                 case CONN_NOT_IMPLEMENTED_ERROR:
+                               case STMT_NOT_IMPLEMENTED_ERROR:
                     strcpy(szSqlState, "S1C00");
                     break;
                 default:
index d87859c9fc6971dcda3db7123f206d89a99d04ce..cad5a5ca15f215e072f033ff1a4dd79752ea7a46 100644 (file)
@@ -36,6 +36,8 @@
 #include "bind.h"
 #include "lobj.h"
 
+extern GLOBAL_VALUES globals;
+
 
 //      Perform a Prepare on the SQL statement
 RETCODE SQL_API SQLPrepare(HSTMT     hstmt,
@@ -342,7 +344,7 @@ int lf;
 
                mylog("SQLTransact: sending on conn %d '%s'\n", conn, stmt_string);
 
-               res = CC_send_query(conn, stmt_string, NULL, NULL);
+               res = CC_send_query(conn, stmt_string, NULL);
                CC_set_no_trans(conn);
 
                if ( ! res) {
@@ -364,12 +366,14 @@ int lf;
 
 //      -       -       -       -       -       -       -       -       -
 
-
 RETCODE SQL_API SQLCancel(
         HSTMT   hstmt)  // Statement to cancel.
 {
 static char *func="SQLCancel";
 StatementClass *stmt = (StatementClass *) hstmt;
+RETCODE result;
+HMODULE hmodule;
+FARPROC addr;
 
        mylog( "%s: entering...\n", func);
 
@@ -380,8 +384,35 @@ StatementClass *stmt = (StatementClass *) hstmt;
        }
 
        //      Not in the middle of SQLParamData/SQLPutData so cancel like a close.
-       if (stmt->data_at_exec < 0)
-               return SQLFreeStmt(hstmt, SQL_CLOSE);
+       if (stmt->data_at_exec < 0) {
+
+
+               /*      MAJOR HACK for Windows to reset the driver manager's cursor state:
+                       Because of what seems like a bug in the Odbc driver manager,
+                       SQLCancel does not act like a SQLFreeStmt(CLOSE), as many
+                       applications depend on this behavior.  So, this 
+                       brute force method calls the driver manager's function on
+                       behalf of the application.  
+               */
+
+#ifdef WIN32
+               if (globals.cancel_as_freestmt) {
+                       hmodule = GetModuleHandle("ODBC32");
+                       addr = GetProcAddress(hmodule, "SQLFreeStmt");
+                       result = addr( (char *) (stmt->phstmt) - 96, SQL_CLOSE);
+               }
+               else {
+                       result = SQLFreeStmt( hstmt, SQL_CLOSE);
+               }
+#else
+               result = SQLFreeStmt( hstmt, SQL_CLOSE);
+#endif
+
+               mylog("SQLCancel:  SQLFreeStmt returned %d\n", result);
+
+               SC_clear_error(hstmt);
+               return SQL_SUCCESS;
+       }
 
        //      In the middle of SQLParamData/SQLPutData, so cancel that.
        //      Note, any previous data-at-exec buffers will be freed in the recycle
index c8a1998ad8a09ed00a31306283be5603be43ceb7..49ec68700229fcf9fe91f0eb85c4b198b40bd5f0 100644 (file)
@@ -71,7 +71,7 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
 ConnInfo *ci;
 char *p;
 
-       mylog( "%s: entering...\n", func);
+       mylog( "%s: entering...fInfoType=%d\n", func, fInfoType);
 
        if ( ! conn) {
                CC_log_error(func, "", NULL);
@@ -224,7 +224,7 @@ char *p;
                break;
 
     case SQL_DBMS_NAME: /* ODBC 1.0 */
-        if (pcbInfoValue) *pcbInfoValue = 10;
+        if (pcbInfoValue) *pcbInfoValue = strlen(DBMS_NAME);
         strncpy_null((char *)rgbInfoValue, DBMS_NAME, (size_t)cbInfoValueMax);
         break;
 
@@ -238,7 +238,7 @@ char *p;
         // by direct experimentation they are not.  postgres forces
         // the newer transaction to wait before doing something that
         // would cause one of these problems.
-        *((DWORD *)rgbInfoValue) = SQL_TXN_SERIALIZABLE;
+        *((DWORD *)rgbInfoValue) = SQL_TXN_READ_COMMITTED; //SQL_TXN_SERIALIZABLE;
         if(pcbInfoValue) { *pcbInfoValue = 4; }
         break;
 
@@ -269,11 +269,12 @@ char *p;
 
     case SQL_FETCH_DIRECTION: /* ODBC 1.0 */
         // which fetch directions are supported? (bitmask)
-        *((DWORD *)rgbInfoValue) = globals.use_declarefetch ? 0 : (SQL_FD_FETCH_NEXT |
+        *((DWORD *)rgbInfoValue) = globals.use_declarefetch ? (SQL_FD_FETCH_NEXT) : (SQL_FD_FETCH_NEXT |
                                    SQL_FD_FETCH_FIRST |
                                    SQL_FD_FETCH_LAST |
                                    SQL_FD_FETCH_PRIOR |
-                                   SQL_FD_FETCH_ABSOLUTE);
+                                   SQL_FD_FETCH_ABSOLUTE |
+                                                                  SQL_FD_FETCH_RELATIVE);
         if(pcbInfoValue) { *pcbInfoValue = 4; }
         break;
 
@@ -285,7 +286,7 @@ char *p;
 
     case SQL_GETDATA_EXTENSIONS: /* ODBC 2.0 */
         // (bitmask)
-        *((DWORD *)rgbInfoValue) = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND);
+        *((DWORD *)rgbInfoValue) = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND | SQL_GD_BLOCK);
         if(pcbInfoValue) { *pcbInfoValue = 4; }
         break;
 
@@ -296,8 +297,9 @@ char *p;
         break;
 
     case SQL_IDENTIFIER_CASE: /* ODBC 1.0 */
-        // are identifiers case-sensitive (yes)
-        *((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE;
+        // are identifiers case-sensitive (yes, but only when quoted.  If not quoted, they
+               // default to lowercase)
+        *((WORD *)rgbInfoValue) = SQL_IC_LOWER;
         if(pcbInfoValue) { *pcbInfoValue = 2; }
         break;
 
@@ -324,8 +326,7 @@ char *p;
 
     case SQL_LOCK_TYPES: /* ODBC 2.0 */
         // which lock types does SQLSetPos support? (bitmask)
-        // SQLSetPos doesn't exist yet
-        *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : 0;
+        *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : SQL_LCK_NO_CHANGE;
         if(pcbInfoValue) { *pcbInfoValue = 4; }
         break;
 
@@ -531,8 +532,7 @@ char *p;
 
     case SQL_POS_OPERATIONS: /* ODBC 2.0 */
         // what functions does SQLSetPos support? (bitmask)
-        // SQLSetPos does not exist yet
-        *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : 0;
+        *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : (SQL_POS_POSITION | SQL_POS_REFRESH);
         if(pcbInfoValue) { *pcbInfoValue = 4; }
         break;
 
@@ -585,8 +585,7 @@ char *p;
         break;
 
     case SQL_QUOTED_IDENTIFIER_CASE: /* ODBC 2.0 */
-        // are "quoted" identifiers case-sensitive?
-        // well, we don't really let you quote identifiers, so...
+        // are "quoted" identifiers case-sensitive?  YES
         *((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE;
         if(pcbInfoValue) { *pcbInfoValue = 2; }
         break;
@@ -713,7 +712,7 @@ char *p;
     case SQL_TXN_ISOLATION_OPTION: /* ODBC 1.0 */
         // what transaction isolation options are available? (bitmask)
         // only the default--serializable transactions.
-        *((DWORD *)rgbInfoValue) = SQL_TXN_SERIALIZABLE;
+        *((DWORD *)rgbInfoValue) = SQL_TXN_READ_COMMITTED; // SQL_TXN_SERIALIZABLE;
         if(pcbInfoValue) { *pcbInfoValue = 4; }
         break;
 
@@ -751,7 +750,9 @@ static char *func = "SQLGetTypeInfo";
 StatementClass *stmt = (StatementClass *) hstmt;
 TupleNode *row;
 int i;
-Int4 type;
+// Int4 type;
+Int4 pgType; 
+Int2 sqlType;
 
        mylog("%s: entering...fSqlType = %d\n", func, fSqlType);
 
@@ -760,6 +761,7 @@ Int4 type;
                return SQL_INVALID_HANDLE;
        }
 
+
        stmt->manual_result = TRUE;
        stmt->result = QR_Constructor();
        if( ! stmt->result) {
@@ -786,25 +788,58 @@ Int4 type;
        QR_set_field_info(stmt->result, 13, "MINIMUM_SCALE", PG_TYPE_INT2, 2);
        QR_set_field_info(stmt->result, 14, "MAXIMUM_SCALE", PG_TYPE_INT2, 2);
 
+       for(i=0, sqlType = sqlTypes[0]; sqlType; sqlType = sqlTypes[++i]) {
+               pgType = sqltype_to_pgtype(sqlType);
+
+               if (fSqlType == SQL_ALL_TYPES || fSqlType == sqlType) {
+                       row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField));
+
+                       /*      These values can't be NULL */
+                       set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, pgType));
+                       set_tuplefield_int2(&row->tuple[1], (Int2) sqlType);
+                       set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, pgType));
+                       set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, pgType));
+                       set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, pgType));
+                       set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, pgType));
+
+                       /*      Localized data-source dependent data type name (always NULL) */
+                       set_tuplefield_null(&row->tuple[12]);   
+
+                       /*      These values can be NULL */
+                       set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, pgType, PG_STATIC, PG_STATIC));
+                       set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, pgType));
+                       set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, pgType));
+                       set_nullfield_string(&row->tuple[5], pgtype_create_params(stmt, pgType));
+                       set_nullfield_int2(&row->tuple[9], pgtype_unsigned(stmt, pgType));
+                       set_nullfield_int2(&row->tuple[11], pgtype_auto_increment(stmt, pgType));
+                       set_nullfield_int2(&row->tuple[13], pgtype_scale(stmt, pgType));
+                       set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, pgType));
+
+                       QR_add_tuple(stmt->result, row);
+               }
+       }
+
     // cycle through the types
-    for(i=0, type = pgtypes_defined[0]; type; type = pgtypes_defined[++i]) {
+//    for(i=0, type = pgtypes_defined[0]; type; type = pgtypes_defined[++i]) {
 
-               if(fSqlType == SQL_ALL_TYPES || fSqlType == pgtype_to_sqltype(stmt, type)) {
+//             if(fSqlType == SQL_ALL_TYPES || fSqlType == pgtype_to_sqltype(stmt, type)) {
 
-                       row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField));
+//                     row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField));
 
                        /*      These values can't be NULL */
+/*
                        set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, type));
                        set_tuplefield_int2(&row->tuple[1], pgtype_to_sqltype(stmt, type));
                        set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, type));
                        set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, type));
                        set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, type));
                        set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, type));
-
+*/
                        /*      Localized data-source dependent data type name (always NULL) */
-                       set_tuplefield_null(&row->tuple[12]);   
+//                     set_tuplefield_null(&row->tuple[12]);   
 
                        /*      These values can be NULL */
+/*
                        set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, type, PG_STATIC, PG_STATIC));
                        set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, type));
                        set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, type));
@@ -815,11 +850,13 @@ Int4 type;
                        set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, type));
 
                        QR_add_tuple(stmt->result, row);
-               }
-    }
+*/
+//             }
+//    }
 
     stmt->status = STMT_FINISHED;
     stmt->currTuple = -1;
+       stmt->rowset_start = -1;
        stmt->current_col = -1;
 
     return SQL_SUCCESS;
@@ -901,7 +938,7 @@ static char *func="SQLGetFunctions";
                        pfExists[SQL_API_SQLDATASOURCES]      = FALSE;  // only implemented by DM
                        pfExists[SQL_API_SQLDESCRIBEPARAM]    = FALSE;  // not properly implemented
                        pfExists[SQL_API_SQLDRIVERS]          = FALSE;  // only implemented by DM
-                       pfExists[SQL_API_SQLEXTENDEDFETCH]    = globals.use_declarefetch ? FALSE : TRUE;
+                       pfExists[SQL_API_SQLEXTENDEDFETCH]    = TRUE;
                        pfExists[SQL_API_SQLFOREIGNKEYS]      = TRUE;
                        pfExists[SQL_API_SQLMORERESULTS]      = TRUE;
                        pfExists[SQL_API_SQLNATIVESQL]        = TRUE;
@@ -910,8 +947,8 @@ static char *func="SQLGetFunctions";
                        pfExists[SQL_API_SQLPRIMARYKEYS]      = TRUE;
                        pfExists[SQL_API_SQLPROCEDURECOLUMNS] = FALSE;
                        pfExists[SQL_API_SQLPROCEDURES]       = FALSE;
-                       pfExists[SQL_API_SQLSETPOS]           = FALSE;
-                       pfExists[SQL_API_SQLSETSCROLLOPTIONS] = FALSE;  // odbc 1.0
+                       pfExists[SQL_API_SQLSETPOS]           = TRUE;
+                       pfExists[SQL_API_SQLSETSCROLLOPTIONS] = TRUE;   // odbc 1.0
                        pfExists[SQL_API_SQLTABLEPRIVILEGES]  = FALSE;
                }
     } else {
@@ -970,7 +1007,7 @@ static char *func="SQLGetFunctions";
                        case SQL_API_SQLDATASOURCES:      *pfExists = FALSE; break;  // only implemented by DM
                        case SQL_API_SQLDESCRIBEPARAM:    *pfExists = FALSE; break;  // not properly implemented
                        case SQL_API_SQLDRIVERS:          *pfExists = FALSE; break;  // only implemented by DM
-                       case SQL_API_SQLEXTENDEDFETCH:    *pfExists = globals.use_declarefetch ? FALSE : TRUE; break;
+                       case SQL_API_SQLEXTENDEDFETCH:    *pfExists = TRUE; break;
                        case SQL_API_SQLFOREIGNKEYS:      *pfExists = TRUE; break;
                        case SQL_API_SQLMORERESULTS:      *pfExists = TRUE; break;
                        case SQL_API_SQLNATIVESQL:        *pfExists = TRUE; break;
@@ -979,8 +1016,8 @@ static char *func="SQLGetFunctions";
                        case SQL_API_SQLPRIMARYKEYS:      *pfExists = TRUE; break;
                        case SQL_API_SQLPROCEDURECOLUMNS: *pfExists = FALSE; break;
                        case SQL_API_SQLPROCEDURES:       *pfExists = FALSE; break;
-                       case SQL_API_SQLSETPOS:           *pfExists = FALSE; break;
-                       case SQL_API_SQLSETSCROLLOPTIONS: *pfExists = FALSE; break;     // odbc 1.0
+                       case SQL_API_SQLSETPOS:           *pfExists = TRUE; break;
+                       case SQL_API_SQLSETSCROLLOPTIONS: *pfExists = TRUE; break;      // odbc 1.0
                        case SQL_API_SQLTABLEPRIVILEGES:  *pfExists = FALSE; break;
                        }
                }
@@ -1253,6 +1290,7 @@ mylog("%s: entering...stmt=%u\n", func, stmt);
 
     // set up the current tuple pointer for SQLFetch
        stmt->currTuple = -1;
+       stmt->rowset_start = -1;
        stmt->current_col = -1;
 
        SQLFreeStmt(htbl_stmt, SQL_DROP);
@@ -1600,6 +1638,7 @@ ConnInfo *ci;
 
     // set up the current tuple pointer for SQLFetch
     stmt->currTuple = -1;
+       stmt->rowset_start = -1;
        stmt->current_col = -1;
 
        SQLFreeStmt(hcol_stmt, SQL_DROP);
@@ -1683,6 +1722,7 @@ mylog("%s: entering...stmt=%u\n", func, stmt);
        }
     stmt->status = STMT_FINISHED;
     stmt->currTuple = -1;
+       stmt->rowset_start = -1;
        stmt->current_col = -1;
 
        mylog("SQLSpecialColumns(): EXIT,  stmt=%u\n", stmt);
@@ -2002,6 +2042,7 @@ mylog("%s: entering...stmt=%u\n", func, stmt);
 
     // set up the current tuple pointer for SQLFetch
     stmt->currTuple = -1;
+       stmt->rowset_start = -1;
        stmt->current_col = -1;
 
        error = FALSE;
@@ -2186,6 +2227,7 @@ Int2 result_cols;
 
     // set up the current tuple pointer for SQLFetch
     stmt->currTuple = -1;
+       stmt->rowset_start = -1;
        stmt->current_col = -1;
 
        mylog("SQLPrimaryKeys(): EXIT, stmt=%u\n", stmt);
@@ -2270,6 +2312,7 @@ Int2 result_cols;
 
     // set up the current tuple pointer for SQLFetch
     stmt->currTuple = -1;
+       stmt->rowset_start = -1;
        stmt->current_col = -1;
 
 
index 7e1501842c6ce98248887920a60dd0943b0a42bf..60baaa63f2d2b6e0101e04d4637cfba0d3097518 100644 (file)
@@ -44,13 +44,13 @@ generate_filename(char* dirname,char* prefix,char* filename)
                return;
 
        strcpy(filename,dirname);
-       strcat(filename,DIRSEPERATOR);
+       strcat(filename,DIRSEPARATOR);
        if(prefix != 0)
                strcat(filename,prefix);
 #ifndef WIN32
        strcat(filename,ptr->pw_name);
 #endif
-       sprintf(filename,"%s%d%s",filename,pid,".log");
+       sprintf(filename,"%s%u%s",filename,pid,".log");
        return;
 }
 
index 194a08161c85695c1a96cd33ae460f2b2eba77b6..8eca682d5aa2d2dd2325ea5822fbb91348a70257 100644 (file)
 
 
 #ifdef MY_LOG
-#define MYLOGFILE      "mylog_"
-#ifndef WIN32
-#define MYLOGDIR       "/tmp"
-#else
-#define MYLOGDIR       "c:"
-#endif
-void mylog();  /* prototype */
+  #define MYLOGFILE    "mylog_"
+  #ifndef WIN32
+    #define MYLOGDIR   "/tmp"
+  #else
+    #define MYLOGDIR   "c:"
+  #endif
+  void mylog();        /* prototype */
 #else
-#define mylog    // mylog
+  #ifndef WIN32
+    #define mylog(args...)     /* GNU convention for variable arguments */
+  #else
+    #define mylog    // mylog
+  #endif
 #endif
 
 #ifdef Q_LOG
-#define QLOGFILE       "psqlodbc_"
-#ifndef WIN32
-#define QLOGDIR                "/tmp"
-#else
-#define QLOGDIR                "c:"
-#endif
-void qlog();   /* prototype */
+  #define QLOGFILE     "psqlodbc_"
+  #ifndef WIN32
+    #define QLOGDIR            "/tmp"
+  #else
+    #define QLOGDIR            "c:"
+  #endif
+  void qlog(); /* prototype */
 #else
-#define qlog    // qlog
+  #ifndef WIN32
+    #define qlog(args...)      /* GNU convention for variable arguments */
+  #else
+    #define qlog    // qlog
+  #endif
 #endif
 
 #ifndef WIN32
-#define DIRSEPERATOR   "/"
+#define DIRSEPARATOR   "/"
 #else
-#define DIRSEPERATOR   "\\"
+#define DIRSEPARATOR   "\\"
 #endif
 
 void remove_newlines(char *string);
index ec653642841f6a7bc6eec1d8dc5adef948271b6f..a263c98c2dd84d37ea36df1d5d5f771f861194f1 100644 (file)
 extern GLOBAL_VALUES globals;
 
 
+RETCODE set_statement_option(ConnectionClass *conn, 
+                                                        StatementClass *stmt, 
+                                                        UWORD   fOption,
+                                                        UDWORD  vParam)
+{
+static char *func="set_statement_option";
+char changed = FALSE;
+
+
+       switch(fOption) {
+       case SQL_ASYNC_ENABLE:/* ignored */
+               break;
+
+       case SQL_BIND_TYPE:             
+               /* now support multi-column and multi-row binding */
+               if (conn) conn->stmtOptions.bind_size = vParam;
+               if (stmt) stmt->options.bind_size = vParam;
+               break;
+
+       case SQL_CONCURRENCY:
+               /*      positioned update isn't supported so cursor concurrency is read-only */
+
+               if (conn) conn->stmtOptions.scroll_concurrency = vParam;
+               if (stmt) stmt->options.scroll_concurrency = vParam;
+               break;
+
+               /*
+               if (globals.lie) {
+                       if (conn) conn->stmtOptions.scroll_concurrency = vParam;
+                       if (stmt) stmt->options.scroll_concurrency = vParam;
+               }
+               else {
+
+                       if (conn) conn->stmtOptions.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+                       if (stmt) stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+
+                       if (vParam != SQL_CONCUR_READ_ONLY)
+                               changed = TRUE;
+               }
+               break;
+               */
+               
+       case SQL_CURSOR_TYPE:
+               /*      if declare/fetch, then type can only be forward.
+                       otherwise, it can only be forward or static.
+               */
+               mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
+
+               if (globals.lie) {
+
+                       if (conn) conn->stmtOptions.cursor_type = vParam;
+                       if (stmt) stmt->options.cursor_type = vParam;
+
+               }
+               else {
+                       if (globals.use_declarefetch) {
+
+                               if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
+                               if (stmt) stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
+
+                               if (vParam != SQL_CURSOR_FORWARD_ONLY) 
+                                       changed = TRUE;
+                       }
+                       else {
+                               if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC) {
+
+                                       if (conn) conn->stmtOptions.cursor_type = vParam;               // valid type
+                                       if (stmt) stmt->options.cursor_type = vParam;           // valid type
+                               }
+                               else {
+
+                                       if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
+                                       if (stmt) stmt->options.cursor_type = SQL_CURSOR_STATIC;
+
+                                       changed = TRUE;
+                               }
+                       }
+               }
+               break;
+
+       case SQL_KEYSET_SIZE: /* ignored, but saved and returned  */
+               mylog("SetStmtOption(): SQL_KEYSET_SIZE, vParam = %d\n", vParam);
+
+               if (conn) conn->stmtOptions.keyset_size = vParam;
+               if (stmt) stmt->options.keyset_size = vParam;
+
+               break;
+
+               /*
+               if (globals.lie)
+                       stmt->keyset_size = vParam;
+               else {
+                       stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       stmt->errormsg = "Driver does not support keyset size option";
+                       SC_log_error(func, "", stmt);
+                       return SQL_ERROR;
+               }
+               */
+
+       case SQL_MAX_LENGTH:/* ignored, but saved */
+               mylog("SetStmtOption(): SQL_MAX_LENGTH, vParam = %d\n", vParam);
+               if (conn) conn->stmtOptions.maxLength = vParam;
+               if (stmt) stmt->options.maxLength = vParam;
+               break;
+
+       case SQL_MAX_ROWS: /* ignored, but saved */
+               mylog("SetStmtOption(): SQL_MAX_ROWS, vParam = %d\n", vParam);
+               if (conn) conn->stmtOptions.maxRows = vParam;
+               if (stmt) stmt->options.maxRows = vParam;
+               break;
+
+       case SQL_NOSCAN: /* ignored */
+               mylog("SetStmtOption: SQL_NOSCAN, vParam = %d\n", vParam);
+               break;
+
+       case SQL_QUERY_TIMEOUT: /* ignored */
+               mylog("SetStmtOption: SQL_QUERY_TIMEOUT, vParam = %d\n", vParam);
+               //      "0" returned in SQLGetStmtOption
+               break;
+
+       case SQL_RETRIEVE_DATA: /* ignored, but saved */
+               mylog("SetStmtOption(): SQL_RETRIEVE_DATA, vParam = %d\n", vParam);
+               if (conn) conn->stmtOptions.retrieve_data = vParam;
+               if (stmt) stmt->options.retrieve_data = vParam;
+               break;
+
+       case SQL_ROWSET_SIZE:
+               mylog("SetStmtOption(): SQL_ROWSET_SIZE, vParam = %d\n", vParam);
+
+
+               /*      Save old rowset size for SQLExtendedFetch purposes 
+                       If the rowset_size is being changed since the last call
+                       to fetch rows.
+               */
+
+               if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0 )
+                       stmt->save_rowset_size = stmt->options.rowset_size;
+
+               if (vParam < 1) {
+                       vParam = 1;
+                       changed = TRUE;
+               }
+
+               if (conn) conn->stmtOptions.rowset_size = vParam;
+               if (stmt) stmt->options.rowset_size = vParam;
+
+
+               break;
+
+       case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
+               if (stmt) {
+                       stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       stmt->errormsg = "Simulated positioned update/delete not supported.  Use the cursor library.";
+                       SC_log_error(func, "", stmt);
+               }
+               if (conn) {
+                       conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       conn->errormsg = "Simulated positioned update/delete not supported.  Use the cursor library.";
+                       CC_log_error(func, "", conn);
+               }
+               return SQL_ERROR;
+
+       case SQL_USE_BOOKMARKS: /* NOT SUPPORTED */
+               if (stmt) {
+                       stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       stmt->errormsg = "Driver does not support (SET) using bookmarks.";
+                       SC_log_error(func, "", stmt);
+               }
+               if (conn) {
+                       conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       conn->errormsg = "Driver does not support (SET) using bookmarks.";
+                       CC_log_error(func, "", conn);
+               }
+               return SQL_ERROR;
+
+    default:
+               {
+               char option[64];
+
+               if (stmt) {
+                       stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       stmt->errormsg = "Unknown statement option (Set)";
+                       sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
+                       SC_log_error(func, option, stmt);
+               }
+               if (conn) {
+                       conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       conn->errormsg = "Unknown statement option (Set)";
+                       sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
+                       CC_log_error(func, option, conn);
+               }
+
+        return SQL_ERROR;
+               }
+    }
+
+       if (changed) {
+               if (stmt) {
+                       stmt->errormsg = "Requested value changed.";
+                       stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
+               }
+               if (conn) {
+                       conn->errormsg = "Requested value changed.";
+                       conn->errornumber = STMT_OPTION_VALUE_CHANGED;
+               }
+               return SQL_SUCCESS_WITH_INFO;
+       }
+       else
+               return SQL_SUCCESS;
+}
+
+
+
 /* Implements only SQL_AUTOCOMMIT */
 RETCODE SQL_API SQLSetConnectOption(
         HDBC    hdbc,
@@ -45,6 +258,9 @@ RETCODE SQL_API SQLSetConnectOption(
 {
 static char *func="SQLSetConnectOption";
 ConnectionClass *conn = (ConnectionClass *) hdbc;
+char changed = FALSE;
+RETCODE retval;
+int i;
 
        mylog("%s: entering...\n", func);
 
@@ -55,6 +271,47 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
 
 
        switch (fOption) {
+       /* Statement Options --
+          (apply to all stmts on the connection and become defaults for new stmts)
+       */
+       case SQL_ASYNC_ENABLE:
+       case SQL_BIND_TYPE:             
+       case SQL_CONCURRENCY:
+       case SQL_CURSOR_TYPE:
+       case SQL_KEYSET_SIZE: 
+       case SQL_MAX_LENGTH:
+       case SQL_MAX_ROWS:
+       case SQL_NOSCAN: 
+       case SQL_QUERY_TIMEOUT:
+       case SQL_RETRIEVE_DATA:
+       case SQL_ROWSET_SIZE:
+       case SQL_SIMULATE_CURSOR:
+       case SQL_USE_BOOKMARKS:
+
+               /*      Affect all current Statements */
+               for (i = 0; i < conn->num_stmts; i++) {
+                       if ( conn->stmts[i]) {
+                               set_statement_option(NULL, conn->stmts[i], fOption, vParam);
+                       }
+               }
+
+               /*      Become the default for all future statements on this connection */
+               retval = set_statement_option(conn, NULL, fOption, vParam);
+
+               if (retval == SQL_SUCCESS_WITH_INFO)
+                       changed = TRUE;
+               else if (retval == SQL_ERROR)
+                       return SQL_ERROR;
+
+               break;
+
+       /**********************************/
+       /*****  Connection Options  *******/    
+       /**********************************/
+
+       case SQL_ACCESS_MODE: /* ignored */
+               break;
+
        case SQL_AUTOCOMMIT:
 
                /*  Since we are almost always in a transaction, this is now ok.
@@ -89,19 +346,34 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
 
                break;
 
-       case SQL_LOGIN_TIMEOUT:
+       case SQL_CURRENT_QUALIFIER: /* ignored */
+               break;
+
+       case SQL_LOGIN_TIMEOUT: /* ignored */
+               break;
+
+       case SQL_PACKET_SIZE:   /* ignored */
                break;
 
-       case SQL_ACCESS_MODE:
+       case SQL_QUIET_MODE:    /* ignored */
                break;
 
-       case SQL_TXN_ISOLATION:
+       case SQL_TXN_ISOLATION: /* ignored */
+               break;
+               
+       /*      These options should be handled by driver manager */
+       case SQL_ODBC_CURSORS:
+       case SQL_OPT_TRACE:
+       case SQL_OPT_TRACEFILE:
+       case SQL_TRANSLATE_DLL:
+       case SQL_TRANSLATE_OPTION:
+               CC_log_error(func, "This connect option (Set) is only used by the Driver Manager", conn);
                break;
 
        default:
                { 
                char option[64];
-               conn->errormsg = "Driver does not support setting this connect option";
+               conn->errormsg = "Unknown connect option (Set)";
                conn->errornumber = CONN_UNSUPPORTED_OPTION;
                sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
                CC_log_error(func, option, conn);
@@ -109,7 +381,14 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
                }
 
        }    
-       return SQL_SUCCESS;
+
+       if (changed) {
+               conn->errornumber = CONN_OPTION_VALUE_CHANGED;
+               conn->errormsg = "Requested value changed.";
+               return SQL_SUCCESS_WITH_INFO;
+       }
+       else
+               return SQL_SUCCESS;
 }
 
 //      -       -       -       -       -       -       -       -       -
@@ -131,22 +410,50 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
        }
 
        switch (fOption) {
+       case SQL_ACCESS_MODE:/* NOT SUPPORTED */
+               *((UDWORD *) pvParam) = SQL_MODE_READ_WRITE;
+               break;
+
        case SQL_AUTOCOMMIT:
                *((UDWORD *)pvParam) = (UDWORD)( CC_is_in_autocommit(conn) ?
                                                SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
                break;
 
-       /* don't use qualifiers */
-       case SQL_CURRENT_QUALIFIER:
+       case SQL_CURRENT_QUALIFIER:     /* don't use qualifiers */
                if(pvParam)
                        strcpy(pvParam, "");
 
                break;
 
+       case SQL_LOGIN_TIMEOUT: /* NOT SUPPORTED */
+               *((UDWORD *) pvParam) = 0;
+               break;
+
+       case SQL_PACKET_SIZE: /* NOT SUPPORTED */
+               *((UDWORD *) pvParam) = globals.socket_buffersize;
+               break;
+
+       case SQL_QUIET_MODE:/* NOT SUPPORTED */
+               *((UDWORD *) pvParam) = (UDWORD) NULL;
+               break;
+
+       case SQL_TXN_ISOLATION:/* NOT SUPPORTED */
+               *((UDWORD *) pvParam) = SQL_TXN_SERIALIZABLE;
+               break;
+
+       /*      These options should be handled by driver manager */
+       case SQL_ODBC_CURSORS:
+       case SQL_OPT_TRACE:
+       case SQL_OPT_TRACEFILE:
+       case SQL_TRANSLATE_DLL:
+       case SQL_TRANSLATE_OPTION:
+               CC_log_error(func, "This connect option (Get) is only used by the Driver Manager", conn);
+               break;
+
        default:
                {
                char option[64];
-               conn->errormsg = "Driver does not support getting this connect option";
+               conn->errormsg = "Unknown connect option (Get)";
                conn->errornumber = CONN_UNSUPPORTED_OPTION;
                sprintf(option, "fOption=%d", fOption);
                CC_log_error(func, option, conn);
@@ -181,104 +488,7 @@ char changed = FALSE;
                return SQL_INVALID_HANDLE;
        }
 
-       switch(fOption) {
-       case SQL_QUERY_TIMEOUT:
-               mylog("SetStmtOption: vParam = %d\n", vParam);
-               //      "0" returned in SQLGetStmtOption
-               break;
-
-       case SQL_MAX_LENGTH:
-               //      "4096" returned in SQLGetStmtOption
-               break;
-
-       case SQL_MAX_ROWS:
-               mylog("SetStmtOption(): SQL_MAX_ROWS = %d, returning success\n", vParam);
-               stmt->maxRows = vParam;
-               return SQL_SUCCESS;
-               break;
-
-       case SQL_ROWSET_SIZE:
-               mylog("SetStmtOption(): SQL_ROWSET_SIZE = %d\n", vParam);
-
-               stmt->rowset_size = 1;          // only support 1 row at a time
-               if (vParam != 1) 
-                       changed = TRUE;
-
-               break;
-
-       case SQL_KEYSET_SIZE:
-               mylog("SetStmtOption(): SQL_KEYSET_SIZE = %d\n", vParam);
-               if (globals.lie)
-                       stmt->keyset_size = vParam;
-               else {
-                       stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
-                       stmt->errormsg = "Driver does not support keyset size option";
-                       SC_log_error(func, "", stmt);
-                       return SQL_ERROR;
-               }
-               break;
-
-       case SQL_CONCURRENCY:
-               //      positioned update isn't supported so cursor concurrency is read-only
-               mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
-
-               if (globals.lie)
-                       stmt->scroll_concurrency = vParam;
-               else {
-                       stmt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
-                       if (vParam != SQL_CONCUR_READ_ONLY)
-                               changed = TRUE;
-               }
-               break;
-               
-       case SQL_CURSOR_TYPE:
-               //      if declare/fetch, then type can only be forward.
-               //      otherwise, it can only be forward or static.
-               mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
-
-               if (globals.lie)
-                       stmt->cursor_type = vParam;
-               else {
-                       if (globals.use_declarefetch) {
-                               stmt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
-                               if (vParam != SQL_CURSOR_FORWARD_ONLY) 
-                                       changed = TRUE;
-                       }
-                       else {
-                               if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC)
-                                       stmt->cursor_type = vParam;             // valid type
-                               else {
-                                       stmt->cursor_type = SQL_CURSOR_STATIC;
-                                       changed = TRUE;
-                               }
-                       }
-               }
-               break;
-
-       case SQL_SIMULATE_CURSOR:
-               stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
-               stmt->errormsg = "Simulated positioned update/delete not supported.  Use the cursor library.";
-               SC_log_error(func, "", stmt);
-               return SQL_ERROR;
-
-    default:
-               {
-               char option[64];
-               stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
-               stmt->errormsg = "Driver does not support setting this statement option";
-               sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
-               SC_log_error(func, option, stmt);
-        return SQL_ERROR;
-               }
-    }
-
-       if (changed) {
-               stmt->errormsg = "Requested value changed.";
-               stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
-               return SQL_SUCCESS_WITH_INFO;
-       }
-       else
-               return SQL_SUCCESS;
+       return set_statement_option(NULL, stmt, fOption, vParam);
 }
 
 
@@ -304,54 +514,78 @@ StatementClass *stmt = (StatementClass *) hstmt;
        }
 
        switch(fOption) {
-       case SQL_QUERY_TIMEOUT:
-               // how long we wait on a query before returning to the
-               // application (0 == forever)
-               *((SDWORD *)pvParam) = 0;
+       case SQL_GET_BOOKMARK:/* NOT SUPPORTED */
+               stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+               stmt->errormsg = "Driver does not support getting bookmarks.";
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
                break;
 
-       case SQL_MAX_LENGTH:
-               // what is the maximum length that will be returned in
-               // a single column
-               *((SDWORD *)pvParam) = 4096;
+       case SQL_ROW_NUMBER:
+               *((SDWORD *) pvParam) = stmt->currTuple + 1;
                break;
 
-       case SQL_MAX_ROWS:
-               *((SDWORD *)pvParam) = stmt->maxRows;
-               mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->maxRows);
+       case SQL_ASYNC_ENABLE:  /* NOT SUPPORTED */
+               *((SDWORD *) pvParam) = SQL_ASYNC_ENABLE_OFF;
                break;
 
-       case SQL_ROWSET_SIZE:
-               mylog("GetStmtOption(): SQL_ROWSET_SIZE\n");
-               *((SDWORD *)pvParam) = stmt->rowset_size;
+       case SQL_BIND_TYPE:
+               *((SDWORD *) pvParam) = stmt->options.bind_size;
                break;
 
-       case SQL_KEYSET_SIZE:
+       case SQL_CONCURRENCY: /* NOT REALLY SUPPORTED */
+               mylog("GetStmtOption(): SQL_CONCURRENCY\n");
+               *((SDWORD *)pvParam) = stmt->options.scroll_concurrency;
+               break;
+
+       case SQL_CURSOR_TYPE: /* PARTIAL SUPPORT */
+               mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
+               *((SDWORD *)pvParam) = stmt->options.cursor_type;
+               break;
+
+       case SQL_KEYSET_SIZE: /* NOT SUPPORTED, but saved */
                mylog("GetStmtOption(): SQL_KEYSET_SIZE\n");
-               *((SDWORD *)pvParam) = stmt->keyset_size;
+               *((SDWORD *)pvParam) = stmt->options.keyset_size;
                break;
 
-       case SQL_CONCURRENCY:
-               mylog("GetStmtOption(): SQL_CONCURRENCY\n");
-               *((SDWORD *)pvParam) = stmt->scroll_concurrency;
+       case SQL_MAX_LENGTH: /* NOT SUPPORTED, but saved */
+               *((SDWORD *)pvParam) = stmt->options.maxLength;
                break;
 
-       case SQL_CURSOR_TYPE:
-               mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
-               *((SDWORD *)pvParam) = stmt->cursor_type;
+       case SQL_MAX_ROWS: /* NOT SUPPORTED, but saved */
+               *((SDWORD *)pvParam) = stmt->options.maxRows;
+               mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->options.maxRows);
                break;
 
-       case SQL_SIMULATE_CURSOR:
-               stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
-               stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
-               SC_log_error(func, "", stmt);
-               return SQL_ERROR;
+       case SQL_NOSCAN:/* NOT SUPPORTED */
+               *((SDWORD *) pvParam) = SQL_NOSCAN_ON;
+               break;
+
+       case SQL_QUERY_TIMEOUT: /* NOT SUPPORTED */
+               *((SDWORD *) pvParam) = 0;
+               break;
+
+       case SQL_RETRIEVE_DATA: /* NOT SUPPORTED, but saved */
+               *((SDWORD *) pvParam) = stmt->options.retrieve_data;
+               break;
+
+       case SQL_ROWSET_SIZE:
+               *((SDWORD *) pvParam) = stmt->options.rowset_size;
+               break;
+
+       case SQL_SIMULATE_CURSOR:/* NOT SUPPORTED */
+               *((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE;
+               break;
+
+       case SQL_USE_BOOKMARKS:/* NOT SUPPORTED */
+               *((SDWORD *) pvParam) = SQL_UB_OFF;
+               break;
 
        default:
                {
                char option[64];
                stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
-               stmt->errormsg = "Driver does not support getting this statement option";
+               stmt->errormsg = "Unknown statement option (Get)";
                sprintf(option, "fOption=%d", fOption);
                SC_log_error(func, option, stmt);
                return SQL_ERROR;
index cdd9fc2654a7fada8b16d4b93ee1ec51fceadb86..169f9346ca8dd7817e226857b76c15582fa25c4c 100644 (file)
@@ -73,7 +73,104 @@ Int4 pgtypes_defined[]  = {
                                PG_TYPE_BYTEA,
                                PG_TYPE_LO,
                            0 };
-                         
+
+/*     These are the SQL Types reported in SQLGetTypeInfo.  */
+Int2 sqlTypes [] = {
+       SQL_BIGINT,
+       /* SQL_BINARY, */
+       SQL_BIT,
+       SQL_CHAR,
+       SQL_DATE,
+       SQL_DECIMAL,
+       SQL_DOUBLE,
+       SQL_FLOAT,
+       SQL_INTEGER,
+       SQL_LONGVARBINARY,
+       SQL_LONGVARCHAR,
+       SQL_NUMERIC,
+       SQL_REAL,
+       SQL_SMALLINT,
+       SQL_TIME,
+       SQL_TIMESTAMP,
+       SQL_TINYINT,
+       SQL_VARBINARY,
+       SQL_VARCHAR,
+       0
+};
+
+Int4 sqltype_to_pgtype(SWORD fSqlType)
+{
+Int4 pgType;
+
+       switch(fSqlType) {
+
+       case SQL_BINARY:
+               pgType = PG_TYPE_BYTEA;
+               break;
+
+       case SQL_CHAR:
+               pgType = PG_TYPE_BPCHAR;
+               break;
+
+       case SQL_BIT:
+               pgType = globals.bools_as_char ? PG_TYPE_CHAR : PG_TYPE_BOOL;
+               break;
+
+       case SQL_DATE:
+               pgType = PG_TYPE_DATE;
+               break;
+
+       case SQL_DOUBLE:
+       case SQL_FLOAT:
+               pgType = PG_TYPE_FLOAT8;
+               break;
+
+       case SQL_INTEGER:
+       case SQL_BIGINT:
+       case SQL_NUMERIC:
+       case SQL_DECIMAL:
+               pgType = PG_TYPE_INT4;
+               break;
+
+       case SQL_LONGVARBINARY:
+               pgType = PG_TYPE_LO;
+               break;
+
+       case SQL_LONGVARCHAR:
+               pgType = globals.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR;
+               break;
+
+       case SQL_REAL:
+               pgType = PG_TYPE_FLOAT4;
+               break;
+
+       case SQL_SMALLINT:
+       case SQL_TINYINT:
+               pgType = PG_TYPE_INT2;
+               break;
+
+       case SQL_TIME:
+               pgType = PG_TYPE_TIME;
+               break;
+
+       case SQL_TIMESTAMP:
+               pgType = PG_TYPE_DATETIME;
+               break;
+
+       case SQL_VARBINARY:
+               pgType = PG_TYPE_BYTEA;
+               break;
+
+       case SQL_VARCHAR:
+               pgType = PG_TYPE_VARCHAR;
+               break;
+
+       default:
+               break;
+       }
+
+       return pgType;
+}
 
 /*     There are two ways of calling this function:  
        1.      When going through the supported PG types (SQLGetTypeInfo)
@@ -94,7 +191,7 @@ Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type)
        case PG_TYPE_CHAR16:
        case PG_TYPE_NAME:              return SQL_CHAR;        
 
-       case PG_TYPE_BPCHAR:            return SQL_CHAR;                // temporary?
+       case PG_TYPE_BPCHAR:            return SQL_CHAR;
 
        case PG_TYPE_VARCHAR:           return SQL_VARCHAR;
 
@@ -165,7 +262,7 @@ char *pgtype_to_name(StatementClass *stmt, Int4 type)
        case PG_TYPE_CHAR8:         return "char8";
        case PG_TYPE_CHAR16:            return "char16";
        case PG_TYPE_VARCHAR:       return "varchar";
-       case PG_TYPE_BPCHAR:        return "bpchar";
+       case PG_TYPE_BPCHAR:        return "char";
        case PG_TYPE_TEXT:          return "text";
        case PG_TYPE_NAME:          return "name";
        case PG_TYPE_INT2:          return "int2";
index e83ec040abd4a72f9df0ee748d0b2fb47c90b933..b1da5aa2538aec949306639cc56012d5376b445d 100644 (file)
 #define PG_TYPE_TIMESTAMP  1296
 
 extern Int4 pgtypes_defined[];
+extern Int2 sqlTypes[];
 
 /*     Defines for pgtype_precision */
 #define PG_STATIC              -1      
 
+Int4 sqltype_to_pgtype(Int2 fSqlType);
 
 Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type);
 Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type);
index 5a3527c759f25ba76bc133d2bde3e4eac97a6cef..0df56ee1e7614cfab454ae9a304f80a98a02d26f 100644 (file)
@@ -126,4 +126,3 @@ RETCODE SQL_API SQLDummyOrdinal(void)
        return SQL_SUCCESS;
 }
 
-
index d00c67dd82bed37cd784f79d1e781fdb48370f73..b0356b1b05149fe37447557955fe5989cc002469 100644 (file)
@@ -40,7 +40,7 @@ typedef UInt4 Oid;
 #define MAX_CONNECT_STRING                     4096
 #define ERROR_MSG_LENGTH                       4096
 #define FETCH_MAX                                      100             /* default number of rows to cache for declare/fetch */
-#define FETCH_INCR                                     1000
+#define TUPLE_MALLOC_INC                       100
 #define SOCK_BUFFER_SIZE                       4096    /* default socket buffer size */
 #define MAX_CONNECTIONS                                128             /* conns per environment (arbitrary)  */
 #define MAX_FIELDS                                     512
@@ -70,8 +70,8 @@ typedef UInt4 Oid;
 /* Driver stuff */
 #define DRIVERNAME             "PostgreSQL ODBC"
 #define DBMS_NAME              "PostgreSQL"
-#define DBMS_VERSION           "06.40.0001 PostgreSQL 6.4"
-#define POSTGRESDRIVERVERSION  "06.40.0001"
+#define DBMS_VERSION           "06.40.0002 PostgreSQL 6.4"
+#define POSTGRESDRIVERVERSION  "06.40.0002"
 
 #ifdef WIN32
 #define DRIVER_FILE_NAME               "PSQLODBC.DLL"
@@ -116,12 +116,31 @@ typedef struct GlobalValues_
        char                            bools_as_char;
        char                            lie;
        char                            parse;
+       char                            cancel_as_freestmt;
        char                            extra_systable_prefixes[MEDIUM_REGISTRY_LEN];
        char                            conn_settings[LARGE_REGISTRY_LEN];
        FILE*                           mylogFP;
        FILE*                           qlogFP; 
 } GLOBAL_VALUES;
 
+typedef struct StatementOptions_ {
+       int maxRows;
+       int maxLength;
+       int rowset_size;
+       int keyset_size;
+       int cursor_type;
+       int scroll_concurrency;
+       int retrieve_data;
+       int bind_size;                  /* size of each structure if using Row Binding */
+} StatementOptions;
+
+/*     Used to pass extra query info to send_query */
+typedef struct QueryInfo_ {
+       int                             row_size;
+       QResultClass    *result_in;
+       char                    *cursor;
+} QueryInfo;
+
 
 #define PG_TYPE_LO                             -999    /* hack until permanent type available */
 #define PG_TYPE_LO_NAME                        "lo"
index 779e82fea6e9c442f8dca83d3a6b301ee14a54f7..b6874da6faaa431b534917c8fa296480d251af99 100644 (file)
@@ -101,6 +101,8 @@ BEGIN
                     WS_TABSTOP,140,35,80,10
     CONTROL         "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
                     BS_AUTOCHECKBOX | WS_TABSTOP,15,50,80,10
+    CONTROL         "Cancel as FreeStmt (Exp)",DRV_CANCELASFREESTMT,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,140,50,105,10
     GROUPBOX        "Unknown Sizes",IDC_STATIC,10,65,175,25
     CONTROL         "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON | 
                     WS_GROUP | WS_TABSTOP,15,76,45,10
@@ -202,8 +204,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 6,40,0,1
- PRODUCTVERSION 6,40,0,1
+ FILEVERSION 6,40,0,2
+ PRODUCTVERSION 6,40,0,2
  FILEFLAGSMASK 0x3L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -221,12 +223,12 @@ BEGIN
             VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0"
             VALUE "CompanyName", "Insight Distribution Systems\0"
             VALUE "FileDescription", "PostgreSQL Driver\0"
-            VALUE "FileVersion", " 6.40.0001\0"
+            VALUE "FileVersion", " 6.40.0002\0"
             VALUE "InternalName", "psqlodbc\0"
             VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation.  Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0"
             VALUE "OriginalFilename", "psqlodbc.dll\0"
             VALUE "ProductName", "Microsoft Open Database Connectivity\0"
-            VALUE "ProductVersion", " 6.40.0001\0"
+            VALUE "ProductVersion", " 6.40.0002\0"
         END
     END
     BLOCK "VarFileInfo"
index d5ee8407c80670f8410cecaff4080c62ad6b9967..8e5ffdf55c28e059d4a3f678fda19a7fb81df34b 100644 (file)
@@ -48,6 +48,30 @@ QR_set_num_fields(QResultClass *self, int new_num_fields)
        mylog("exit QR_set_num_fields\n");
 }
 
+void 
+QR_set_position(QResultClass *self, int pos)
+{
+       self->tupleField = self->backend_tuples + ((self->base + pos) * self->num_fields);
+}
+
+void
+QR_set_cache_size(QResultClass *self, int cache_size)
+{
+       self->cache_size = cache_size;
+}
+
+void 
+QR_set_rowset_size(QResultClass *self, int rowset_size)
+{
+       self->rowset_size = rowset_size;
+}
+
+void
+QR_inc_base(QResultClass *self, int base_inc)
+{
+       self->base += base_inc;
+}
+
 /************************************/
 /* CLASS QResult                    */
 /************************************/
@@ -77,9 +101,15 @@ QResultClass *rv;
                rv->inTuples = FALSE;
                rv->fcount = 0;
                rv->fetch_count = 0;
+               rv->base = 0;
+               rv->currTuple = -1;
                rv->num_fields = 0;
                rv->tupleField = NULL;
                rv->cursor = NULL;
+
+               rv->cache_size = globals.fetch_max;
+               rv->rowset_size = 1;
+
        }
 
        mylog("exit QR_Constructor\n");
@@ -178,6 +208,8 @@ int num_fields = self->num_fields;
 char
 QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
 {
+int tuple_size;
+
        //      If called from send_query the first time (conn != NULL), 
        //      then set the inTuples state,
        //      and read the tuples.  If conn is NULL,
@@ -186,7 +218,7 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
        if (conn != NULL) {
                self->conn = conn;
 
-               mylog("QR_fetch_tuples: cursor = '%s', self->cursor=%u\n", cursor, self->cursor);
+               mylog("QR_fetch_tuples: cursor = '%s', self->cursor=%u\n",  (cursor==NULL)?"":cursor, self->cursor);
 
                if (self->cursor)
                        free(self->cursor);
@@ -199,7 +231,7 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
                        }
                        self->cursor = strdup(cursor);
                }
-
                //      Read the field attributes.
                //      $$$$ Should do some error control HERE! $$$$
                if ( CI_read_fields(self->fields, self->conn)) {
@@ -214,9 +246,14 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
 
                mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields);
 
+               if (globals.use_declarefetch) 
+                       tuple_size = self->cache_size;
+               else
+                       tuple_size = TUPLE_MALLOC_INC;
+
                /* allocate memory for the tuple cache */
-               mylog("MALLOC: fetch_max = %d, size = %d\n", globals.fetch_max, self->num_fields * sizeof(TupleField) * globals.fetch_max);
-               self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * globals.fetch_max);
+               mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
+               self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size);
                if ( ! self->backend_tuples) {
                        self->status = PGRES_FATAL_ERROR; 
                        QR_set_message(self, "Could not get memory for tuple cache.");
@@ -225,13 +262,16 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
 
                self->inTuples = TRUE;
 
+
                /*  Force a read to occur in next_tuple */
-               self->fcount = globals.fetch_max+1;
-               self->fetch_count = globals.fetch_max+1;
+               self->fcount = tuple_size+1;
+               self->fetch_count = tuple_size+1;
+               self->base = 0;
 
                return QR_next_tuple(self);
        }
        else {
+
                //      Always have to read the field attributes.
                //      But we dont have to reallocate memory for them!
 
@@ -257,9 +297,10 @@ QResultClass *res;
                sprintf(buf, "close %s", self->cursor);
                mylog("QResult: closing cursor: '%s'\n", buf);
 
-               res = CC_send_query(self->conn, buf, NULL, NULL);
+               res = CC_send_query(self->conn, buf, NULL);
 
                self->inTuples = FALSE;
+               self->currTuple = -1;
 
                free(self->cursor);
                self->cursor = NULL;
@@ -274,7 +315,7 @@ QResultClass *res;
                if (CC_cursor_count(self->conn) == 0) {
                        mylog("QResult: END transaction on conn=%u\n", self->conn);
 
-                       res = CC_send_query(self->conn, "END", NULL, NULL);
+                       res = CC_send_query(self->conn, "END", NULL);
 
                        CC_set_no_trans(self->conn);
 
@@ -300,9 +341,14 @@ SocketClass *sock;
 /* Speed up access */
 int fetch_count = self->fetch_count;
 int fcount = self->fcount;
+int fetch_size, offset= 0;
+int end_tuple = self->rowset_size + self->base;
+char corrected = FALSE;
 TupleField *the_tuples = self->backend_tuples;
 static char msgbuffer[MAX_MESSAGE_LEN+1];
 char cmdbuffer[MAX_MESSAGE_LEN+1];     // QR_set_command() dups this string so dont need static
+char fetch[128];
+QueryInfo qi;
 
        if (fetch_count < fcount) {     /* return a row from cache */
                mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, fcount);
@@ -310,9 +356,9 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];  // QR_set_command() dups this string so dont
                self->fetch_count++;
                return TRUE;
        }
-       else if (self->fcount < globals.fetch_max) {   /* last row from cache */
-                       //      We are done because we didn't even get FETCH_MAX tuples
-                 mylog("next_tuple: fcount < FETCH_MAX: fcount = %d, fetch_count = %d\n", fcount, fetch_count);
+       else if (self->fcount < self->cache_size) {   /* last row from cache */
+                       //      We are done because we didn't even get CACHE_SIZE tuples
+                 mylog("next_tuple: fcount < CACHE_SIZE: fcount = %d, fetch_count = %d\n", fcount, fetch_count);
                  self->tupleField = NULL;
                  self->status = PGRES_END_TUPLES;
                  return -1;    /* end of tuples */
@@ -324,8 +370,8 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];  // QR_set_command() dups this string so dont
                        and read the tuples.
                */
                self->tupleField = NULL;
+
                if ( ! self->inTuples) {
-                       char fetch[128];
 
                        if ( ! globals.use_declarefetch) {
                                mylog("next_tuple: ALL_ROWS: done, fcount = %d, fetch_count = %d\n", fcount, fetch_count);
@@ -334,20 +380,54 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];        // QR_set_command() dups this string so dont
                                return -1;      /* end of tuples */
                        }
 
-                       sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor);
+                       if (self->base == fcount) {             /* not a correction */
+
+                               /*      Determine the optimum cache size.  */
+                               if (globals.fetch_max % self->rowset_size == 0)
+                                       fetch_size = globals.fetch_max;
+                               else if (self->rowset_size < globals.fetch_max)
+                                       fetch_size = (globals.fetch_max / self->rowset_size) * self->rowset_size;
+                               else
+                                       fetch_size = self->rowset_size;
+
+                               self->cache_size = fetch_size;
+                               self->fetch_count = 1;          
+                       } 
+                       else {  /* need to correct */
+
+                               corrected = TRUE;
+
+                               fetch_size = end_tuple - fcount;
+
+                               self->cache_size += fetch_size;
+
+                               offset = self->fetch_count;
+                               self->fetch_count++;
+
+                       }
+
+
+                       self->backend_tuples = (TupleField *) realloc(self->backend_tuples, self->num_fields * sizeof(TupleField) * self->cache_size);
+                       if ( ! self->backend_tuples) {
+                               self->status = PGRES_FATAL_ERROR; 
+                               QR_set_message(self, "Out of memory while reading tuples.");
+                               return FALSE;
+                       }
+                       sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor);
 
-                       mylog("next_tuple: sending actual fetch (%d) query '%s'\n", globals.fetch_max, fetch);
+                       mylog("next_tuple: sending actual fetch (%d) query '%s'\n", fetch_size, fetch);
 
                        //      don't read ahead for the next tuple (self) !
-                       res = CC_send_query(self->conn, fetch, self, NULL);
+                       qi.row_size = self->cache_size;
+                       qi.result_in = self;
+                       qi.cursor = NULL;
+                       res = CC_send_query(self->conn, fetch, &qi);
                        if (res == NULL) {
                                self->status = PGRES_FATAL_ERROR;
                                QR_set_message(self, "Error fetching next group.");
                                return FALSE;
                        }
                        self->inTuples = TRUE;
-                       /* This is a true fetch, like SQLFetch() */
-                       self->fetch_count = 1;          
                }
                else {
                        mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->fcount, self->fetch_count);
@@ -357,11 +437,14 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];        // QR_set_command() dups this string so dont
                        */
                        self->fetch_count = 0;
                }
-               // fall through and read the next group
+       }
+
+       if ( ! corrected) {
+               self->base = 0;
+               self->fcount = 0;
        }
 
 
-       self->fcount = 0;
        sock = CC_get_socket(self->conn);
        self->tupleField = NULL;
 
@@ -377,11 +460,11 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];        // QR_set_command() dups this string so dont
                case 'B': /* Tuples in binary format */
                case 'D': /* Tuples in ASCII format  */
 
-                       if ( ! globals.use_declarefetch && self->fcount > 0 && ! (self->fcount % globals.fetch_max)) {
+                       if ( ! globals.use_declarefetch && self->fcount > 0 && ! (self->fcount % TUPLE_MALLOC_INC)) {
                                size_t old_size = self->fcount * self->num_fields * sizeof(TupleField);
                                mylog("REALLOC: old_size = %d\n", old_size);
 
-                               self->backend_tuples = (TupleField *) realloc(self->backend_tuples, old_size + (self->num_fields * sizeof(TupleField) * globals.fetch_max));
+                               self->backend_tuples = (TupleField *) realloc(self->backend_tuples, old_size + (self->num_fields * sizeof(TupleField) * TUPLE_MALLOC_INC));
                                if ( ! self->backend_tuples) {
                                        self->status = PGRES_FATAL_ERROR; 
                                        QR_set_message(self, "Out of memory while reading tuples.");
@@ -412,7 +495,7 @@ char cmdbuffer[MAX_MESSAGE_LEN+1];  // QR_set_command() dups this string so dont
                                mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->fcount);
 
                                /*  set to first row */
-                               self->tupleField = self->backend_tuples;        // the_tuples;
+                               self->tupleField = self->backend_tuples + (offset * self->num_fields);
                                return TRUE;
                        } 
                        else { //       We are surely done here (we read 0 tuples)
@@ -532,5 +615,6 @@ ColumnInfoClass *flds;
                } else
                        bmp <<= 1;
        }
+       self->currTuple++;
        return TRUE;
 }
index 7cc2303173b6303cb098ae893591464c4c6d1fde..f26381fc2156065330723cf2e0fbb67e404f7e4c 100644 (file)
@@ -44,8 +44,13 @@ struct QResultClass_ {
        //      Stuff for declare/fetch tuples
        int fetch_count;                                        // logical rows read so far
        int fcount;                                                     // actual rows read in the fetch
+       int currTuple;
+       int base;
 
        int num_fields;                                         // number of fields in the result
+       int cache_size;
+       int rowset_size;
+
     QueryResultCode status;
 
     char *message;
@@ -93,7 +98,7 @@ struct QResultClass_ {
 #define QR_get_status(self)                            (self->status)
 
 //     Core Functions
-QResultClass *QR_Constructor(void);
+QResultClass *QR_Constructor();
 void QR_Destructor(QResultClass *self);
 char QR_read_tuple(QResultClass *self, char binary);
 int QR_next_tuple(QResultClass *self);
@@ -104,5 +109,10 @@ void QR_set_command(QResultClass *self, char *msg);
 void QR_set_notice(QResultClass *self, char *msg);
 
 void QR_set_num_fields(QResultClass *self, int new_num_fields); /* manual result only */
+
+void QR_inc_base(QResultClass *self, int base_inc);
+void QR_set_cache_size(QResultClass *self, int cache_size);
+void QR_set_rowset_size(QResultClass *self, int rowset_size);
+void QR_set_position(QResultClass *self, int pos);
  
 #endif
index 16db18bbd2caa1beb54c0c54b891c952c67f23ee..81e3c9961ff0c92eacef4408b5e59e2b81e86557 100644 (file)
@@ -44,6 +44,7 @@
 #define DRV_EXTRASYSTABLEPREFIXES       1051
 #define DS_ROWVERSIONING                1052
 #define DRV_PARSE                       1052
+#define DRV_CANCELASFREESTMT            1053
 #define IDC_OPTIONS                     1054
 #define DRV_KSQO                        1055
 #define DS_PG64                         1057
index 5d9dfc30fbb85b66e11b366c3e247d36c5e2f85c..7f18f6a205fcbb8251975aa625b0fcd9fede12ba 100644 (file)
@@ -8,7 +8,7 @@
  *
  * API functions:   SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
  *                  SQLGetData, SQLFetch, SQLExtendedFetch, 
- *                  SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI),
+ *                  SQLMoreResults(NI), SQLSetPos, SQLSetScrollOptions(NI),
  *                  SQLSetCursorName, SQLGetCursorName
  *
  * Comments:        See "notice.txt" for copyright and license information.
@@ -57,13 +57,18 @@ char *msg, *ptr;
                SC_log_error(func, "", NULL);
                return SQL_INVALID_HANDLE;
        }
+       if (stmt->manual_result) {
+               if (pcrow)
+                       *pcrow = -1;
+               return SQL_SUCCESS;
+       }
 
        if(stmt->statement_type == STMT_TYPE_SELECT) {
                if (stmt->status == STMT_FINISHED) {
                        res = SC_get_Result(stmt);
 
                        if(res && pcrow) {
-                               *pcrow = globals.use_declarefetch ? 0 : QR_get_num_tuples(res);
+                               *pcrow = globals.use_declarefetch ? -1 : QR_get_num_tuples(res);
                                return SQL_SUCCESS;
                        }
                }
@@ -176,6 +181,7 @@ Int4 fieldtype = 0;
 int precision = 0;
 ConnInfo *ci;
 char parse_ok;
+char buf[255];
 
        mylog("%s: entering...\n", func);
 
@@ -247,7 +253,8 @@ char parse_ok;
                if (icol >= QR_NumResultCols(result)) {
                        stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
                        stmt->errormsg = "Invalid column number in DescribeCol.";
-                       SC_log_error(func, "", stmt);
+                       sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(result));
+                       SC_log_error(func, buf, stmt);
                        return SQL_ERROR;
                }
 
@@ -610,7 +617,6 @@ int num_cols, num_rows;
 Int4 field_type;
 void *value;
 int result;
-char multiple;
 
 
 mylog("SQLGetData: enter, stmt=%u\n", stmt);
@@ -674,7 +680,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
                mylog("     value = '%s'\n", value);
        }
        else { /* its a SOCKET result (backend data) */
-               if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) {
+               if (stmt->currTuple == -1 || ! res || ! res->tupleField) {
                        stmt->errormsg = "Not positioned on a valid row for GetData.";
                        stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
                        SC_log_error(func, "", stmt);
@@ -690,14 +696,12 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
 
        mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
 
-       /*      Is this another call for the same column to retrieve more data? */
-       multiple = (icol == stmt->current_col) ? TRUE : FALSE;
+       stmt->current_col = icol;
 
     result = copy_and_convert_field(stmt, field_type, value, 
-                                    fCType, rgbValue, cbValueMax, pcbValue, multiple);
-
+                                    fCType, rgbValue, cbValueMax, pcbValue);
 
-       stmt->current_col = icol;
+       stmt->current_col = -1;
 
        switch(result) {
        case COPY_OK:
@@ -725,7 +729,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
                return SQL_ERROR;
 
        case COPY_NO_DATA_FOUND:
-               SC_log_error(func, "no data found", stmt);
+               /* SC_log_error(func, "no data found", stmt); */
                return SQL_NO_DATA_FOUND;
 
     default:
@@ -736,70 +740,27 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
     }
 }
 
-//      Returns data for bound columns in the current row ("hstmt->iCursor"),
-//      advances the cursor.
-
-RETCODE SQL_API SQLFetch(
-        HSTMT   hstmt)
+RETCODE
+SC_fetch(StatementClass *stmt)
 {
-static char *func = "SQLFetch";
-StatementClass *stmt = (StatementClass *) hstmt;   
-QResultClass *res;
-int retval;
+static char *func = "SC_fetch";
+QResultClass *res = stmt->result;
+int retval, result;
 Int2 num_cols, lf;
 Oid type;
 char *value;
 ColumnInfoClass *ci;
 // TupleField *tupleField;
 
-mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
-
-       if ( ! stmt) {
-               SC_log_error(func, "", NULL);
-               return SQL_INVALID_HANDLE;
-       }
-
-       SC_clear_error(stmt);
-
-       if ( ! (res = stmt->result)) {
-               stmt->errormsg = "Null statement result in SQLFetch.";
-               stmt->errornumber = STMT_SEQUENCE_ERROR;
-               SC_log_error(func, "", stmt);
-               return SQL_ERROR;
-       }
-
+       stmt->last_fetch_count = 0;
        ci = QR_get_fields(res);                /* the column info */
 
-       if (stmt->status == STMT_EXECUTING) {
-               stmt->errormsg = "Can't fetch while statement is still executing.";
-               stmt->errornumber = STMT_SEQUENCE_ERROR;
-               SC_log_error(func, "", stmt);
-               return SQL_ERROR;
-       }
-
-
-       if (stmt->status != STMT_FINISHED) {
-               stmt->errornumber = STMT_STATUS_ERROR;
-               stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
-               SC_log_error(func, "", stmt);
-               return SQL_ERROR;
-       }
-
-       if (stmt->bindings == NULL) {
-               // just to avoid a crash if the user insists on calling this
-               // function even if SQL_ExecDirect has reported an Error
-               stmt->errormsg = "Bindings were not allocated properly.";
-               stmt->errornumber = STMT_SEQUENCE_ERROR;
-               SC_log_error(func, "", stmt);
-               return SQL_ERROR;
-       }
-
        mylog("manual_result = %d, use_declarefetch = %d\n", stmt->manual_result, globals.use_declarefetch);
  
        if ( stmt->manual_result || ! globals.use_declarefetch) {
 
                if (stmt->currTuple >= QR_get_num_tuples(res) -1 || 
-                       (stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1)) {
+                       (stmt->options.maxRows > 0 && stmt->currTuple == stmt->options.maxRows - 1)) {
 
                        /*      if at the end of the tuples, return "no data found" 
                                and set the cursor past the end of the result set 
@@ -833,10 +794,16 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
 
        num_cols = QR_NumResultCols(res);
 
+       result = SQL_SUCCESS;
+       stmt->last_fetch_count = 1;
+
        for (lf=0; lf < num_cols; lf++) {
 
                mylog("fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u\n", num_cols, lf, stmt, stmt->bindings, stmt->bindings[lf].buffer);
 
+               /*      reset for SQLGetData */
+               stmt->bindings[lf].data_left = -1;
+
                if (stmt->bindings[lf].buffer != NULL) {
             // this column has a binding
 
@@ -855,13 +822,12 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
                                value = QR_get_value_backend_row(res, stmt->currTuple, lf);
                        }
 
-                       mylog("value = '%s'\n", value);
+                       mylog("value = '%s'\n",  (value==NULL)?"<NULL>":value);
 
                        retval = copy_and_convert_field_bindinfo(stmt, type, value, lf);
 
                        mylog("copy_and_convert: retval = %d\n", retval);
 
-
                        switch(retval) {
                        case COPY_OK:
                                break;  /*      OK, do next bound column */
@@ -870,37 +836,99 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
                                stmt->errormsg = "Received an unsupported type from Postgres.";
                                stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
                                SC_log_error(func, "", stmt);
-                               return SQL_ERROR;
+                               result = SQL_ERROR;
+                               break;
 
                        case COPY_UNSUPPORTED_CONVERSION:
                                stmt->errormsg = "Couldn't handle the necessary data type conversion.";
                                stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
                                SC_log_error(func, "", stmt);
-                               return SQL_ERROR;
+                               result = SQL_ERROR;
+                               break;
 
                        case COPY_RESULT_TRUNCATED:
                                stmt->errornumber = STMT_TRUNCATED;
                                stmt->errormsg = "The buffer was too small for the result.";
-                               return SQL_SUCCESS_WITH_INFO;
+                               result = SQL_SUCCESS_WITH_INFO;
+                               break;
 
                        case COPY_GENERAL_ERROR:        /* error msg already filled in */
                                SC_log_error(func, "", stmt);
-                               return SQL_ERROR;
+                               result = SQL_ERROR;
+                               break;
 
+                       /*  This would not be meaningful in SQLFetch. */
                        case COPY_NO_DATA_FOUND:
-                               SC_log_error(func, "no data found", stmt);
-                               return SQL_NO_DATA_FOUND;
+                               break;
 
                        default:
                                stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
                                stmt->errornumber = STMT_INTERNAL_ERROR;
                                SC_log_error(func, "", stmt);
-                               return SQL_ERROR;
+                               result = SQL_ERROR;
+                               break;
                        }
                }
        }
 
-       return SQL_SUCCESS;
+       return result;
+}
+
+
+//      Returns data for bound columns in the current row ("hstmt->iCursor"),
+//      advances the cursor.
+
+RETCODE SQL_API SQLFetch(
+        HSTMT   hstmt)
+{
+static char *func = "SQLFetch";
+StatementClass *stmt = (StatementClass *) hstmt;   
+QResultClass *res;
+
+mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
+
+       if ( ! stmt) {
+               SC_log_error(func, "", NULL);
+               return SQL_INVALID_HANDLE;
+       }
+
+       SC_clear_error(stmt);
+
+       if ( ! (res = stmt->result)) {
+               stmt->errormsg = "Null statement result in SQLFetch.";
+               stmt->errornumber = STMT_SEQUENCE_ERROR;
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       if (stmt->status == STMT_EXECUTING) {
+               stmt->errormsg = "Can't fetch while statement is still executing.";
+               stmt->errornumber = STMT_SEQUENCE_ERROR;
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+
+       if (stmt->status != STMT_FINISHED) {
+               stmt->errornumber = STMT_STATUS_ERROR;
+               stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       if (stmt->bindings == NULL) {
+               // just to avoid a crash if the user insists on calling this
+               // function even if SQL_ExecDirect has reported an Error
+               stmt->errormsg = "Bindings were not allocated properly.";
+               stmt->errornumber = STMT_SEQUENCE_ERROR;
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       QR_set_rowset_size(res, 1);
+       QR_inc_base(res, stmt->last_fetch_count);       
+
+       return SC_fetch(stmt);
 }
 
 //      This fetchs a block of data (rowset).
@@ -914,9 +942,10 @@ RETCODE SQL_API SQLExtendedFetch(
 {
 static char *func = "SQLExtendedFetch";
 StatementClass *stmt = (StatementClass *) hstmt;
-int num_tuples;
+QResultClass *res;
+int num_tuples, i, save_rowset_size;
 RETCODE result;
-
+char truncated, error;
 
 mylog("SQLExtendedFetch: stmt=%u\n", stmt);
 
@@ -925,43 +954,108 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
                return SQL_INVALID_HANDLE;
        }
 
-       if ( globals.use_declarefetch) {
-               SC_log_error(func, "SQLExtendedFetch with UseDeclareFetch not yet supported", stmt);
+       if ( globals.use_declarefetch && ! stmt->manual_result) {
+               if ( fFetchType != SQL_FETCH_NEXT) {
+                       stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+                       stmt->errormsg = "Unsupported fetch type for SQLExtendedFetch with UseDeclareFetch option.";
+                       return SQL_ERROR;
+               }
+       }
+
+       SC_clear_error(stmt);
+
+       if ( ! (res = stmt->result)) {
+               stmt->errormsg = "Null statement result in SQLExtendedFetch.";
+               stmt->errornumber = STMT_SEQUENCE_ERROR;
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       if (stmt->status == STMT_EXECUTING) {
+               stmt->errormsg = "Can't fetch while statement is still executing.";
+               stmt->errornumber = STMT_SEQUENCE_ERROR;
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       if (stmt->status != STMT_FINISHED) {
+               stmt->errornumber = STMT_STATUS_ERROR;
+               stmt->errormsg = "ExtendedFetch can only be called after the successful execution on a SQL statement";
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       if (stmt->bindings == NULL) {
+               // just to avoid a crash if the user insists on calling this
+               // function even if SQL_ExecDirect has reported an Error
+               stmt->errormsg = "Bindings were not allocated properly.";
+               stmt->errornumber = STMT_SEQUENCE_ERROR;
+               SC_log_error(func, "", stmt);
                return SQL_ERROR;
        }
 
        /*      Initialize to no rows fetched */
        if (rgfRowStatus)
-               *rgfRowStatus = SQL_ROW_NOROW;
+               for (i = 0; i < stmt->options.rowset_size; i++)
+                       *(rgfRowStatus + i) = SQL_ROW_NOROW;
+
        if (pcrow)
                *pcrow = 0;
 
-       num_tuples = QR_get_num_tuples(stmt->result);
+       num_tuples = QR_get_num_tuples(res);
+
+       /*      Save and discard the saved rowset size */
+       save_rowset_size = stmt->save_rowset_size;
+       stmt->save_rowset_size = -1;
 
        switch (fFetchType)  {
        case SQL_FETCH_NEXT:
+
+               /*      From the odbc spec... If positioned before the start of the RESULT SET,
+                       then this should be equivalent to SQL_FETCH_FIRST.
+               */
+
+               if (stmt->rowset_start < 0)
+                       stmt->rowset_start = 0;
+
+               else {
+                       
+                       stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : stmt->options.rowset_size);
+               }
+
                mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
                break;
 
        case SQL_FETCH_PRIOR:
                mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
 
-               /*      If already before result set, return no data found */
-               if (stmt->currTuple <= 0)
-                       return SQL_NO_DATA_FOUND;
 
-               stmt->currTuple -= 2;
+               /*      From the odbc spec... If positioned after the end of the RESULT SET,
+                       then this should be equivalent to SQL_FETCH_LAST.
+               */
+
+               if (stmt->rowset_start >= num_tuples) {
+                       stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
+
+               }
+               else {
+
+                       stmt->rowset_start -= stmt->options.rowset_size;
+
+               }
+
                break;
 
        case SQL_FETCH_FIRST:
                mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
 
-               stmt->currTuple = -1;
+               stmt->rowset_start = 0;
                break;
 
        case SQL_FETCH_LAST:
                mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
-               stmt->currTuple = num_tuples <= 0 ? -1 : (num_tuples - 2);
+
+               stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size) ;
                break;
 
        case SQL_FETCH_ABSOLUTE:
@@ -969,17 +1063,31 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
 
                /*      Position before result set, but dont fetch anything */
                if (irow == 0) {
+                       stmt->rowset_start = -1;
                        stmt->currTuple = -1;
                        return SQL_NO_DATA_FOUND;
                }
                /*      Position before the desired row */
                else if (irow > 0) {
-                       stmt->currTuple = irow-2;
+                       stmt->rowset_start = irow - 1;
                }
                /*      Position with respect to the end of the result set */
                else {
-                       stmt->currTuple = num_tuples + irow - 1;
+                       stmt->rowset_start = num_tuples + irow;
                }    
+
+               break;
+
+       case SQL_FETCH_RELATIVE:
+               
+               /*      Refresh the current rowset -- not currently implemented, but lie anyway */
+               if (irow == 0) {
+                       break;
+               }
+
+               stmt->rowset_start += irow;
+
+               
                break;
 
        default:
@@ -988,18 +1096,95 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
 
        }           
 
+
+       /***********************************/
+       /*      CHECK FOR PROPER CURSOR STATE  */
+       /***********************************/
+       /*      Handle Declare Fetch style specially because the end is not really the end... */
+       if ( globals.use_declarefetch && ! stmt->manual_result) {
+               if (QR_end_tuples(res)) {
+                       return SQL_NO_DATA_FOUND;
+               }
+       }
+       else {
+               /*      If *new* rowset is after the result_set, return no data found */
+               if (stmt->rowset_start >= num_tuples) {
+                       stmt->rowset_start = num_tuples;
+                       return SQL_NO_DATA_FOUND;
+               }
+       }
+
+       /*      If *new* rowset is prior to result_set, return no data found */
+       if (stmt->rowset_start < 0) {
+               if (stmt->rowset_start + stmt->options.rowset_size <= 0) {
+                       stmt->rowset_start = -1;
+                       return SQL_NO_DATA_FOUND;
+               }
+               else {  /*      overlap with beginning of result set, so get first rowset */
+                       stmt->rowset_start = 0;
+               }
+       }
+
+       /*      currTuple is always 1 row prior to the rowset */
+       stmt->currTuple = stmt->rowset_start - 1;
+
+       /*      increment the base row in the tuple cache */
+       QR_set_rowset_size(res, stmt->options.rowset_size);
+       QR_inc_base(res, stmt->last_fetch_count);       
+               
+       /*      Physical Row advancement occurs for each row fetched below */
+
        mylog("SQLExtendedFetch: new currTuple = %d\n", stmt->currTuple);
 
-       result = SQLFetch(hstmt);
+       truncated = error = FALSE;
+       for (i = 0; i < stmt->options.rowset_size; i++) {
 
-       if (result == SQL_SUCCESS) {
-               if (rgfRowStatus)
-                       *rgfRowStatus = SQL_ROW_SUCCESS;
-               if (pcrow)
-                       *pcrow = 1;
+               stmt->bind_row = i;             // set the binding location
+               result = SC_fetch(stmt);
+
+               /*      Determine Function status */
+               if (result == SQL_NO_DATA_FOUND)
+                       break;
+               else if (result == SQL_SUCCESS_WITH_INFO)
+                       truncated = TRUE;
+               else if (result == SQL_ERROR)
+                       error = TRUE;
+
+               /*      Determine Row Status */
+               if (rgfRowStatus) {
+                       if (result == SQL_ERROR) 
+                               *(rgfRowStatus + i) = SQL_ROW_ERROR;
+                       else
+                               *(rgfRowStatus + i)= SQL_ROW_SUCCESS;
+               }
        }
 
-       return result;
+       /*      Save the fetch count for SQLSetPos */
+       stmt->last_fetch_count= i;
+
+       /*      Reset next binding row */
+       stmt->bind_row = 0;
+
+       /*      Move the cursor position to the first row in the result set. */
+       stmt->currTuple = stmt->rowset_start;
+
+       /*      For declare/fetch, need to reset cursor to beginning of rowset */
+       if (globals.use_declarefetch && ! stmt->manual_result) {
+               QR_set_position(res, 0);
+       }
+
+       /*      Set the number of rows retrieved */
+       if (pcrow)
+               *pcrow = i;
+
+       if (i == 0)
+               return SQL_NO_DATA_FOUND;               /*      Only DeclareFetch should wind up here */
+       else if (error)
+               return SQL_ERROR;
+       else if (truncated)
+               return SQL_SUCCESS_WITH_INFO;
+       else
+               return SQL_SUCCESS;
 
 }
 
@@ -1014,8 +1199,8 @@ RETCODE SQL_API SQLMoreResults(
        return SQL_NO_DATA_FOUND;
 }
 
-//      This positions the cursor within a block of data.
-
+//     This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
+//        This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.      
 RETCODE SQL_API SQLSetPos(
         HSTMT   hstmt,
         UWORD   irow,
@@ -1023,12 +1208,57 @@ RETCODE SQL_API SQLSetPos(
         UWORD   fLock)
 {
 static char *func = "SQLSetPos";
-char buf[128];
+StatementClass *stmt = (StatementClass *) hstmt;
+QResultClass *res;
+int num_cols, i;
+BindInfoClass *bindings = stmt->bindings;
 
-       sprintf(buf, "SQLSetPos not implemented: irow=%d, fOption=%d, fLock=%d\n", irow, fOption, fLock);
+       if ( ! stmt) {
+               SC_log_error(func, "", NULL);
+               return SQL_INVALID_HANDLE;
+       }
+
+       if (fOption != SQL_POSITION && fOption != SQL_REFRESH) {
+               stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
+               stmt->errormsg = "Only SQL_POSITION/REFRESH is supported for SQLSetPos";
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       if ( ! (res = stmt->result)) {
+               stmt->errormsg = "Null statement result in SQLSetPos.";
+               stmt->errornumber = STMT_SEQUENCE_ERROR;
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+       num_cols = QR_NumResultCols(res);
+
+       if (irow == 0) {
+               stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
+               stmt->errormsg = "Driver does not support Bulk operations.";
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       if (irow > stmt->last_fetch_count) {
+               stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
+               stmt->errormsg = "Row value out of range";
+               SC_log_error(func, "", stmt);
+               return SQL_ERROR;
+       }
+
+       irow--;
+
+       /*      Reset for SQLGetData */
+       for (i = 0; i < num_cols; i++)
+               bindings[i].data_left = -1;
+
+       QR_set_position(res, irow);
+
+       stmt->currTuple = stmt->rowset_start + irow;
+
+       return SQL_SUCCESS;
 
-       SC_log_error(func, buf, (StatementClass *) hstmt);
-       return SQL_ERROR;
 }
 
 //      Sets options that control the behavior of cursors.
index d57d012a48fb2c69f6d48c90a1da8fab85689dfa..0b4b9101d6fa483b19a54e6e80f0048e572fa102 100644 (file)
 #include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+
 #define closesocket(xxx) close(xxx)
 #define SOCKETFD int
+
+#ifndef       INADDR_NONE
+#ifndef _IN_ADDR_T
+#define _IN_ADDR_T
+typedef unsigned int    in_addr_t;
+#endif
+#define INADDR_NONE ((in_addr_t)-1)
+#endif
+
 #else
 #include <winsock.h>
 #define SOCKETFD SOCKET
index db81491591c8689513f4fd0a44e9a96314688a0b..f185e2220a4ae4b07f7c0e0c1add4d5a05452cfe 100644 (file)
@@ -97,6 +97,14 @@ StatementClass *stmt;
 
        *phstmt = (HSTMT) stmt;
 
+       /*      Copy default statement options based from Connection options
+       */
+       stmt->options = conn->stmtOptions;
+
+
+       /*      Save the handle for later */
+       stmt->phstmt = phstmt;
+
     return SQL_SUCCESS;
 }
 
@@ -166,6 +174,18 @@ StatementClass *stmt = (StatementClass *) hstmt;
 /**********************************************************************
  * StatementClass implementation
  */
+void
+InitializeStatementOptions(StatementOptions *opt)
+{
+       opt->maxRows = 0;                       // driver returns all rows
+       opt->maxLength = 0;                     // driver returns all data for char/binary
+       opt->rowset_size = 1;
+       opt->keyset_size = 0;           // fully keyset driven is the default
+       opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
+       opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
+       opt->bind_size = 0;                     /* default is to bind by column */
+       opt->retrieve_data = SQL_RD_ON;
+}
 
 StatementClass *
 SC_Constructor(void)
@@ -175,40 +195,51 @@ StatementClass *rv;
        rv = (StatementClass *) malloc(sizeof(StatementClass));
        if (rv) {
                rv->hdbc = NULL;       /* no connection associated yet */
+               rv->phstmt = NULL;
                rv->result = NULL;
                rv->manual_result = FALSE;
                rv->prepare = FALSE;
                rv->status = STMT_ALLOCATED;
-               rv->maxRows = 0;                        // driver returns all rows
-               rv->rowset_size = 1;
-               rv->keyset_size = 0;            // fully keyset driven is the default
-               rv->scroll_concurrency = SQL_CONCUR_READ_ONLY;
-               rv->cursor_type = SQL_CURSOR_FORWARD_ONLY;
+               rv->internal = FALSE;
+
                rv->errormsg = NULL;
                rv->errornumber = 0;
                rv->errormsg_created = FALSE;
+
                rv->statement = NULL;
                rv->stmt_with_params[0] = '\0';
                rv->statement_type = STMT_TYPE_UNKNOWN;
+
                rv->bindings = NULL;
                rv->bindings_allocated = 0;
+
                rv->parameters_allocated = 0;
                rv->parameters = 0;
+
                rv->currTuple = -1;
+               rv->rowset_start = -1;
                rv->current_col = -1;
-               rv->result = 0;
+               rv->bind_row = 0;
+               rv->last_fetch_count = 0;
+               rv->save_rowset_size = -1;
+
                rv->data_at_exec = -1;
                rv->current_exec_param = -1;
                rv->put_data = FALSE;
+
                rv->lobj_fd = -1;
-               rv->internal = FALSE;
                rv->cursor_name[0] = '\0';
 
+               /*      Parse Stuff */
                rv->ti = NULL;
                rv->fi = NULL;
                rv->ntab = 0;
                rv->nfld = 0;
                rv->parse_status = STMT_PARSE_NONE;
+
+
+               /*  Clear Statement Options -- defaults will be set in AllocStmt */
+               memset(&rv->options, 0, sizeof(StatementOptions));
        }
        return rv;
 }
@@ -361,7 +392,7 @@ mylog("recycle statement: self= %u\n", self);
                conn = SC_get_conn(self);
                if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) {             
 
-                       CC_send_query(conn, "ABORT", NULL, NULL);
+                       CC_send_query(conn, "ABORT", NULL);
                        CC_set_no_trans(conn);
                }
                break;
@@ -405,11 +436,18 @@ mylog("recycle statement: self= %u\n", self);
                self->result = NULL;
        }
 
+       /****************************************************************/
+       /*      Reset only parameters that have anything to do with results */
+       /****************************************************************/
+
        self->status = STMT_READY;
        self->manual_result = FALSE;    // very important
 
        self->currTuple = -1;
+       self->rowset_start = -1;
        self->current_col = -1;
+       self->bind_row = 0;
+       self->last_fetch_count = 0;
 
        self->errormsg = NULL;
        self->errornumber = 0;
@@ -451,6 +489,7 @@ SC_unbind_cols(StatementClass *self)
 Int2 lf;
 
        for(lf = 0; lf < self->bindings_allocated; lf++) {
+               self->bindings[lf].data_left = -1;
                self->bindings[lf].buflen = 0;
                self->bindings[lf].buffer = NULL;
                self->bindings[lf].used = NULL;
@@ -534,6 +573,7 @@ ConnectionClass *conn;
 QResultClass *res;
 char ok, was_ok, was_nonfatal;
 Int2 oldstatus, numcols;
+QueryInfo qi;
 
 
        conn = SC_get_conn(self);
@@ -545,7 +585,7 @@ Int2 oldstatus, numcols;
        if ( ! self->internal && ! CC_is_in_trans(conn) && (globals.use_declarefetch || STMT_UPDATE(self))) {
 
                mylog("   about to begin a transaction on statement = %u\n", self);
-               res = CC_send_query(conn, "BEGIN", NULL, NULL);
+               res = CC_send_query(conn, "BEGIN", NULL);
                if ( ! res) {
                        self->errormsg = "Could not begin a transaction";
                        self->errornumber = STMT_EXEC_ERROR;
@@ -587,14 +627,26 @@ Int2 oldstatus, numcols;
 
 
                /*      send the declare/select */
-               self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
+               self->result = CC_send_query(conn, self->stmt_with_params, NULL);
 
                if (globals.use_declarefetch && self->result != NULL) {
+
+                       QR_Destructor(self->result);
+
                        /*      That worked, so now send the fetch to start getting data back */
-                       sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor_name);
+                       qi.result_in = NULL;
+                       qi.cursor = self->cursor_name;
+                       qi.row_size = globals.fetch_max;
+
+                       /*      Most likely the rowset size will not be set by the application until 
+                               after the statement     is executed, so might as well use the cache size.  
+                               The qr_next_tuple() function will correct for any discrepancies in 
+                               sizes and adjust the cache accordingly.
+                       */
+
+                       sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
                        
-                       //      Save the cursor in the result for later use
-                       self->result = CC_send_query( conn, fetch, NULL, self->cursor_name);
+                       self->result = CC_send_query( conn, fetch, &qi);
                }
 
                mylog("     done sending the query:\n");
@@ -604,11 +656,11 @@ Int2 oldstatus, numcols;
        }
        else  { // not a SELECT statement so don't use a cursor                  
                mylog("      its NOT a select statement: stmt=%u\n", self);
-               self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
+               self->result = CC_send_query(conn, self->stmt_with_params, NULL);
                
                //      If we are in autocommit, we must send the commit.
                if ( ! self->internal && CC_is_in_autocommit(conn) && STMT_UPDATE(self)) {
-                       CC_send_query(conn, "COMMIT", NULL, NULL);
+                       CC_send_query(conn, "COMMIT", NULL);
                        CC_set_no_trans(conn);
                }
                
@@ -630,6 +682,7 @@ Int2 oldstatus, numcols;
                
                self->currTuple = -1; /* set cursor before the first tuple in the list */
                self->current_col = -1;
+               self->rowset_start = -1;
                
                /* see if the query did return any result columns */
                numcols = QR_NumResultCols(self->result);
@@ -692,7 +745,7 @@ SC_log_error(char *func, char *desc, StatementClass *self)
                qlog("                 stmt_with_params='%s'\n", self->stmt_with_params);
                qlog("                 data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);
                qlog("                 currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);
-               qlog("                 maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->maxRows, self->rowset_size, self->keyset_size, self->cursor_type, self->scroll_concurrency);
+               qlog("                 maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->options.maxRows, self->options.rowset_size, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);
                qlog("                 cursor_name='%s'\n", self->cursor_name);
 
                qlog("                 ----------------QResult Info -------------------------------\n");
index e9661ad83ad8fd9af5e0bf813b483086ea6ee190..32a93a29659c390cc607c7e7c060e4e53f8e93f8 100644 (file)
@@ -66,6 +66,10 @@ typedef enum {
 #define STMT_CREATE_TABLE_ERROR 17
 #define STMT_NO_CURSOR_NAME 18
 #define STMT_INVALID_CURSOR_NAME 19
+#define STMT_INVALID_ARGUMENT_NO 20
+#define STMT_ROW_OUT_OF_RANGE 21
+#define STMT_OPERATION_CANCELLED 22
+#define STMT_INVALID_CURSOR_POSITION 23
 
 /* statement types */
 enum {
@@ -93,6 +97,13 @@ enum {
        STMT_PARSE_FATAL,
 };
 
+/*     Result style */
+enum {
+       STMT_FETCH_NONE = 0,
+       STMT_FETCH_NORMAL,
+       STMT_FETCH_EXTENDED,
+};
+
 typedef struct {
        COL_INFO                *col_info;              /* cached SQLColumns info for this table */
        char                    name[MAX_TABLE_LEN+1];
@@ -117,21 +128,16 @@ typedef struct {
 } FIELD_INFO;
 
 
-
 /********      Statement Handle        ***********/
 struct StatementClass_ {
     ConnectionClass *hdbc;             /* pointer to ConnectionClass this statement belongs to */
-
     QResultClass *result;              /* result of the current statement */
+       HSTMT FAR *phstmt;
+       StatementOptions options;
 
     STMT_Status status;
     char *errormsg;
     int errornumber;
-       int maxRows;
-       int rowset_size;
-       int keyset_size;
-       int cursor_type;
-       int scroll_concurrency;
 
     /* information on bindings */
     BindInfoClass *bindings;   /* array to store the binding information */
@@ -141,7 +147,11 @@ struct StatementClass_ {
     int parameters_allocated;
     ParameterInfoClass *parameters;
 
-       Int4 currTuple;
+       Int4 currTuple;                         /* current absolute row number (GetData, SetPos, SQLFetch) */
+       int  save_rowset_size;          /* saved rowset size in case of change/FETCH_NEXT */
+       int  rowset_start;                      /* start of rowset (an absolute row number) */
+       int  bind_row;                          /* current offset for Multiple row/column binding */
+       int  last_fetch_count;      /* number of rows retrieved in last fetch/extended fetch */
        int  current_col;                       /* current column for GetData -- used to handle multiple calls */
        int  lobj_fd;                           /* fd of the current large object */
 
@@ -181,6 +191,7 @@ struct StatementClass_ {
 
 /*     Statement prototypes */
 StatementClass *SC_Constructor(void);
+void InitializeStatementOptions(StatementOptions *opt);
 char SC_Destructor(StatementClass *self);
 int statement_type(char *statement);
 char parse_statement(StatementClass *stmt);