diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c index 4635706a86..397d984ec6 100644 --- a/src/interfaces/odbc/bind.c +++ b/src/interfaces/odbc/bind.c @@ -17,6 +17,7 @@ #include "environ.h" #include "statement.h" +#include "descriptor.h" #include "qresult.h" #include "pgtypes.h" #include @@ -41,6 +42,7 @@ PGAPI_BindParameter( { StatementClass *stmt = (StatementClass *) hstmt; static char *func = "PGAPI_BindParameter"; + APDFields *opts; mylog("%s: entering...\n", func); @@ -51,103 +53,54 @@ PGAPI_BindParameter( } SC_clear_error(stmt); - if (stmt->parameters_allocated < ipar) - { - ParameterInfoClass *old_parameters; - int i, - old_parameters_allocated; - - old_parameters = stmt->parameters; - old_parameters_allocated = stmt->parameters_allocated; - - stmt->parameters = (ParameterInfoClass *) malloc(sizeof(ParameterInfoClass) * (ipar)); - if (!stmt->parameters) - { - stmt->errornumber = STMT_NO_MEMORY_ERROR; - stmt->errormsg = "Could not allocate memory for statement parameters"; - SC_log_error(func, "", stmt); - return SQL_ERROR; - } - - stmt->parameters_allocated = ipar; - - /* copy the old parameters over */ - for (i = 0; i < old_parameters_allocated; i++) - { - /* a structure copy should work */ - stmt->parameters[i] = old_parameters[i]; - } - - /* get rid of the old parameters, if there were any */ - if (old_parameters) - free(old_parameters); - - /* - * zero out the newly allocated parameters (in case they skipped - * some, - */ - /* so we don't accidentally try to use them later) */ - for (; i < stmt->parameters_allocated; i++) - { - stmt->parameters[i].buflen = 0; - stmt->parameters[i].buffer = 0; - stmt->parameters[i].used = 0; - stmt->parameters[i].paramType = 0; - stmt->parameters[i].CType = 0; - stmt->parameters[i].SQLType = 0; - stmt->parameters[i].precision = 0; - stmt->parameters[i].scale = 0; - stmt->parameters[i].data_at_exec = FALSE; - stmt->parameters[i].lobj_oid = 0; - stmt->parameters[i].EXEC_used = NULL; - stmt->parameters[i].EXEC_buffer = NULL; - } - } + opts = SC_get_APD(stmt); + if (opts->allocated < ipar) + extend_parameter_bindings(opts, ipar); /* use zero based column numbers for the below part */ ipar--; /* store the given info */ - stmt->parameters[ipar].buflen = cbValueMax; - stmt->parameters[ipar].buffer = rgbValue; - stmt->parameters[ipar].used = pcbValue; - stmt->parameters[ipar].paramType = fParamType; - stmt->parameters[ipar].CType = fCType; - stmt->parameters[ipar].SQLType = fSqlType; - stmt->parameters[ipar].precision = cbColDef; - stmt->parameters[ipar].scale = ibScale; + opts->parameters[ipar].buflen = cbValueMax; + opts->parameters[ipar].buffer = rgbValue; + opts->parameters[ipar].used = pcbValue; + opts->parameters[ipar].paramType = fParamType; + opts->parameters[ipar].CType = fCType; + opts->parameters[ipar].SQLType = fSqlType; + opts->parameters[ipar].precision = cbColDef; + opts->parameters[ipar].scale = ibScale; /* * If rebinding a parameter that had data-at-exec stuff in it, then * free that stuff */ - if (stmt->parameters[ipar].EXEC_used) + if (opts->parameters[ipar].EXEC_used) { - free(stmt->parameters[ipar].EXEC_used); - stmt->parameters[ipar].EXEC_used = NULL; + free(opts->parameters[ipar].EXEC_used); + opts->parameters[ipar].EXEC_used = NULL; } - if (stmt->parameters[ipar].EXEC_buffer) + if (opts->parameters[ipar].EXEC_buffer) { - if (stmt->parameters[ipar].SQLType != SQL_LONGVARBINARY) - free(stmt->parameters[ipar].EXEC_buffer); - stmt->parameters[ipar].EXEC_buffer = NULL; + if (opts->parameters[ipar].SQLType != SQL_LONGVARBINARY) + free(opts->parameters[ipar].EXEC_buffer); + opts->parameters[ipar].EXEC_buffer = NULL; } - if (pcbValue && stmt->options.param_offset_ptr) - pcbValue += (*stmt->options.param_offset_ptr >> 2); + if (pcbValue && opts->param_offset_ptr) + pcbValue += (*opts->param_offset_ptr >> 2); /* Data at exec macro only valid for C char/binary data */ if (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC || *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)) - stmt->parameters[ipar].data_at_exec = TRUE; + opts->parameters[ipar].data_at_exec = TRUE; else - stmt->parameters[ipar].data_at_exec = FALSE; + opts->parameters[ipar].data_at_exec = FALSE; /* Clear premature result */ if (stmt->status == STMT_PREMATURE) SC_recycle_statement(stmt); - mylog("PGAPI_BindParamater: 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); + mylog("PGAPI_BindParamater: 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, opts->parameters[ipar].data_at_exec); return SQL_SUCCESS; } @@ -165,6 +118,7 @@ PGAPI_BindCol( { StatementClass *stmt = (StatementClass *) hstmt; static char *func = "PGAPI_BindCol"; + ARDFields *opts; mylog("%s: entering...\n", func); @@ -178,8 +132,8 @@ PGAPI_BindCol( } - SC_clear_error(stmt); + opts = SC_get_ARD(stmt); if (stmt->status == STMT_EXECUTING) { stmt->errormsg = "Can't bind columns while statement is still executing."; @@ -188,13 +142,14 @@ PGAPI_BindCol( return SQL_ERROR; } + SC_clear_error(stmt); /* If the bookmark column is being bound, then just save it */ if (icol == 0) { if (rgbValue == NULL) { - stmt->bookmark.buffer = NULL; - stmt->bookmark.used = NULL; + opts->bookmark->buffer = NULL; + opts->bookmark->used = NULL; } else { @@ -215,8 +170,8 @@ inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); return SQL_ERROR; } - stmt->bookmark.buffer = rgbValue; - stmt->bookmark.used = pcbValue; + opts->bookmark->buffer = rgbValue; + opts->bookmark->used = pcbValue; } return SQL_SUCCESS; } @@ -226,11 +181,11 @@ inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); * execution of a statement would have setup the necessary bindings. * But some apps call BindCol before any statement is executed. */ - if (icol > stmt->bindings_allocated) - extend_bindings(stmt, icol); + if (icol > opts->allocated) + extend_column_bindings(opts, icol); /* check to see if the bindings were allocated */ - if (!stmt->bindings) + if (!opts->bindings) { stmt->errormsg = "Could not allocate memory for bindings."; stmt->errornumber = STMT_NO_MEMORY_ERROR; @@ -242,25 +197,29 @@ inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); icol--; /* Reset for SQLGetData */ - stmt->bindings[icol].data_left = -1; + opts->bindings[icol].data_left = -1; if (rgbValue == NULL) { /* we have to unbind the column */ - stmt->bindings[icol].buflen = 0; - stmt->bindings[icol].buffer = NULL; - stmt->bindings[icol].used = NULL; - stmt->bindings[icol].returntype = SQL_C_CHAR; + opts->bindings[icol].buflen = 0; + opts->bindings[icol].buffer = NULL; + opts->bindings[icol].used = NULL; + opts->bindings[icol].returntype = SQL_C_CHAR; + if (opts->bindings[icol].ttlbuf) + free(opts->bindings[icol].ttlbuf); + opts->bindings[icol].ttlbuf = NULL; + opts->bindings[icol].ttlbuflen = 0; } else { /* ok, bind that column */ - stmt->bindings[icol].buflen = cbValueMax; - stmt->bindings[icol].buffer = rgbValue; - stmt->bindings[icol].used = pcbValue; - stmt->bindings[icol].returntype = fCType; + opts->bindings[icol].buflen = cbValueMax; + opts->bindings[icol].buffer = rgbValue; + opts->bindings[icol].used = pcbValue; + opts->bindings[icol].returntype = fCType; - mylog(" bound buffer[%d] = %u\n", icol, stmt->bindings[icol].buffer); + mylog(" bound buffer[%d] = %u\n", icol, opts->bindings[icol].buffer); } return SQL_SUCCESS; @@ -286,6 +245,7 @@ PGAPI_DescribeParam( { StatementClass *stmt = (StatementClass *) hstmt; static char *func = "PGAPI_DescribeParam"; + APDFields *opts; mylog("%s: entering...\n", func); @@ -296,7 +256,8 @@ PGAPI_DescribeParam( } SC_clear_error(stmt); - if ((ipar < 1) || (ipar > stmt->parameters_allocated)) + opts = SC_get_APD(stmt); + if ((ipar < 1) || (ipar > opts->allocated)) { stmt->errormsg = "Invalid parameter number for PGAPI_DescribeParam."; stmt->errornumber = STMT_BAD_PARAMETER_NUMBER_ERROR; @@ -312,16 +273,16 @@ PGAPI_DescribeParam( */ /* parameter markers, not bound parameters. */ if (pfSqlType) - *pfSqlType = stmt->parameters[ipar].SQLType; + *pfSqlType = opts->parameters[ipar].SQLType; if (pcbColDef) - *pcbColDef = stmt->parameters[ipar].precision; + *pcbColDef = opts->parameters[ipar].precision; if (pibScale) - *pibScale = stmt->parameters[ipar].scale; + *pibScale = opts->parameters[ipar].scale; if (pfNullable) - *pfNullable = pgtype_nullable(stmt, stmt->parameters[ipar].paramType); + *pfNullable = pgtype_nullable(stmt, opts->parameters[ipar].paramType); return SQL_SUCCESS; } @@ -336,11 +297,13 @@ PGAPI_ParamOptions( { static char *func = "PGAPI_ParamOptions"; StatementClass *stmt = (StatementClass *) hstmt; + APDFields *opts; mylog("%s: entering... %d %x\n", func, crow, pirow); - stmt->options.paramset_size = crow; - stmt->options.param_processed_ptr = (SQLUINTEGER *)pirow; + opts = SC_get_APD(stmt); + opts->paramset_size = crow; + SC_get_IPD(stmt)->param_processed_ptr = (UInt4 *) pirow; return SQL_SUCCESS; } @@ -433,46 +396,156 @@ create_empty_bindings(int num_columns) return new_bindings; } - void -extend_bindings(StatementClass *stmt, int num_columns) +extend_parameter_bindings(APDFields *self, int num_params) { - static char *func = "extend_bindings"; - BindInfoClass *new_bindings; - int i; + static char *func = "extend_parameter_bindings"; + ParameterInfoClass *new_bindings; - mylog("%s: entering ... stmt=%u, bindings_allocated=%d, num_columns=%d\n", func, stmt, stmt->bindings_allocated, num_columns); + mylog("%s: entering ... self=%u, parameters_allocated=%d, num_params=%d\n", func, self, self->allocated, num_params); /* * if we have too few, allocate room for more, and copy the old * entries into the new structure */ - if (stmt->bindings_allocated < num_columns) + if (self->allocated < num_params) + { + new_bindings = (ParameterInfoClass *) realloc(self->parameters, sizeof(ParameterInfoClass) * num_params); + if (!new_bindings) + { + mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_params, self->allocated); + + self->parameters = NULL; + self->allocated = 0; + return; + } + memset(&new_bindings[self->allocated], 0, + sizeof(ParameterInfoClass) * (num_params - self->allocated)); + + self->parameters = new_bindings; + self->allocated = num_params; + } + + mylog("exit extend_parameter_bindings\n"); +} + +void +reset_a_parameter_binding(APDFields *self, int ipar) +{ + static char *func = "reset_a_parameter_binding"; + + mylog("%s: entering ... self=%u, parameters_allocated=%d, ipar=%d\n", func, self, self->allocated, ipar); + + if (ipar < 1 || ipar > self->allocated) + return; + + ipar--; + self->parameters[ipar].buflen = 0; + self->parameters[ipar].buffer = 0; + self->parameters[ipar].used = 0; + self->parameters[ipar].paramType = 0; + self->parameters[ipar].CType = 0; + if (self->parameters[ipar].EXEC_used) + { + free(self->parameters[ipar].EXEC_used); + self->parameters[ipar].EXEC_used = NULL; + } + + if (self->parameters[ipar].EXEC_buffer) + { + if (self->parameters[ipar].SQLType != SQL_LONGVARBINARY) + free(self->parameters[ipar].EXEC_buffer); + self->parameters[ipar].EXEC_buffer = NULL; + } + self->parameters[ipar].SQLType = 0; + self->parameters[ipar].precision = 0; + self->parameters[ipar].scale = 0; + self->parameters[ipar].data_at_exec = FALSE; + self->parameters[ipar].lobj_oid = 0; +} + +/* + * Free parameters and free the memory. + */ +void +APD_free_params(APDFields *self, char option) +{ + int i; + + mylog("APD_free_params: ENTER, self=%d\n", self); + + if (!self->parameters) + return; + + for (i = 0; i < self->allocated; i++) + { + if (self->parameters[i].data_at_exec) + { + if (self->parameters[i].EXEC_used) + { + free(self->parameters[i].EXEC_used); + self->parameters[i].EXEC_used = NULL; + } + + if (self->parameters[i].EXEC_buffer) + { + if (self->parameters[i].SQLType != SQL_LONGVARBINARY) + free(self->parameters[i].EXEC_buffer); + self->parameters[i].EXEC_buffer = NULL; + } + } + } + + if (option == STMT_FREE_PARAMS_ALL) + { + if (self->parameters); + free(self->parameters); + self->parameters = NULL; + self->allocated = 0; + } + + mylog("APD_free_params: EXIT\n"); +} + +void +extend_column_bindings(ARDFields *self, int num_columns) +{ + static char *func = "extend_column_bindings"; + BindInfoClass *new_bindings; + int i; + + mylog("%s: entering ... self=%u, bindings_allocated=%d, num_columns=%d\n", func, self, self->allocated, num_columns); + + /* + * if we have too few, allocate room for more, and copy the old + * entries into the new structure + */ + if (self->allocated < num_columns) { new_bindings = create_empty_bindings(num_columns); if (!new_bindings) { - mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_columns, stmt->bindings_allocated); + mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_columns, self->allocated); - if (stmt->bindings) + if (self->bindings) { - free(stmt->bindings); - stmt->bindings = NULL; + free(self->bindings); + self->bindings = NULL; } - stmt->bindings_allocated = 0; + self->allocated = 0; return; } - if (stmt->bindings) + if (self->bindings) { - for (i = 0; i < stmt->bindings_allocated; i++) - new_bindings[i] = stmt->bindings[i]; + for (i = 0; i < self->allocated; i++) + new_bindings[i] = self->bindings[i]; - free(stmt->bindings); + free(self->bindings); } - stmt->bindings = new_bindings; - stmt->bindings_allocated = num_columns; + self->bindings = new_bindings; + self->allocated = num_columns; } /* @@ -485,5 +558,53 @@ extend_bindings(StatementClass *stmt, int num_columns) /* SQLExecDirect(...) # returns 5 cols */ /* SQLExecDirect(...) # returns 10 cols (now OK) */ - mylog("exit extend_bindings\n"); + mylog("exit extend_column_bindings\n"); } + +void +reset_a_column_binding(ARDFields *self, int icol) +{ + static char *func = "reset_a_column_binding"; + + mylog("%s: entering ... self=%u, bindings_allocated=%d, icol=%d\n", func, self, self->allocated, icol); + + if (icol > self->allocated) + return; + + /* use zero based col numbers from here out */ + if (0 == icol) + { + self->bookmark->buffer = NULL; + self->bookmark->used = NULL; + } + else + { + icol--; + + /* we have to unbind the column */ + self->bindings[icol].buflen = 0; + self->bindings[icol].buffer = NULL; + self->bindings[icol].used = NULL; + self->bindings[icol].data_left = -1; + self->bindings[icol].returntype = SQL_C_CHAR; + if (self->bindings[icol].ttlbuf) + free(self->bindings[icol].ttlbuf); + self->bindings[icol].ttlbuf = NULL; + self->bindings[icol].ttlbuflen = 0; + } +} + +void ARD_unbind_cols(ARDFields *self, BOOL freeall) +{ + Int2 lf; + + for (lf = 1; lf <= self->allocated; lf++) + reset_a_column_binding(self, lf); + if (freeall) + { + if (self->bindings) + free(self->bindings); + self->bindings = NULL; + self->allocated = 0; + } +} diff --git a/src/interfaces/odbc/bind.h b/src/interfaces/odbc/bind.h index 34b9e1d399..16d9f84b6e 100644 --- a/src/interfaces/odbc/bind.h +++ b/src/interfaces/odbc/bind.h @@ -50,6 +50,9 @@ struct ParameterInfoClass_ }; BindInfoClass *create_empty_bindings(int num_columns); -void extend_bindings(StatementClass *stmt, int num_columns); +void extend_column_bindings(ARDFields *opts, int num_columns); +void reset_a_column_binding(ARDFields *opts, int icol); +void extend_parameter_bindings(APDFields *opts, int num_columns); +void reset_a_parameter_binding(APDFields *opts, int ipar); #endif diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c index a26913db76..db237b3f95 100644 --- a/src/interfaces/odbc/connection.c +++ b/src/interfaces/odbc/connection.c @@ -298,8 +298,8 @@ CC_Constructor() /* Statements under this conn will inherit these options */ InitializeStatementOptions(&rv->stmtOptions); - - + InitializeARDFields(&rv->ardOptions); + InitializeAPDFields(&rv->apdOptions); } return rv; } @@ -381,8 +381,6 @@ CC_begin(ConnectionClass *self) { ret = QR_command_successful(res); QR_Destructor(res); - if (ret) - CC_set_in_trans(self); } else return FALSE; @@ -403,9 +401,6 @@ CC_commit(ConnectionClass *self) { QResultClass *res = CC_send_query(self, "COMMIT", NULL, CLEAR_RESULT_ON_ABORT); mylog("CC_commit: sending COMMIT!\n"); - - CC_set_no_trans(self); - if (res != NULL) { ret = QR_command_successful(res); @@ -429,9 +424,6 @@ CC_abort(ConnectionClass *self) { QResultClass *res = CC_send_query(self, "ROLLBACK", NULL, CLEAR_RESULT_ON_ABORT); mylog("CC_abort: sending ABORT!\n"); - - CC_set_no_trans(self); - if (res != NULL) QR_Destructor(res); else @@ -1118,6 +1110,23 @@ CC_get_error(ConnectionClass *self, int *number, char **message) } +void CC_on_commit(ConnectionClass *conn, BOOL set_no_trans) +{ + if (CC_is_in_trans(conn)) + { + if (set_no_trans) + CC_set_no_trans(conn); + } +} +void CC_on_abort(ConnectionClass *conn, BOOL set_no_trans) +{ + if (CC_is_in_trans(conn)) + { + if (set_no_trans) + CC_set_no_trans(conn); + } +} + /* * The "result_in" is only used by QR_next_tuple() to fetch another group of rows into * the same existing QResultClass (this occurs when the tuple cache is depleted and @@ -1134,7 +1143,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) *retres = NULL, *res = NULL; BOOL clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0), - create_keyset = ((flag & CREATE_KEYSET) != 0); + create_keyset = ((flag & CREATE_KEYSET) != 0), + issue_begin = ((flag & GO_INTO_TRANSACTION) != 0 && !CC_is_in_trans(self)); char swallow, *wq; int id; @@ -1146,7 +1156,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) query_completed = FALSE, before_64 = PG_VERSION_LT(self, 6.4), aborted = FALSE, - used_passed_result_object = FALSE; + used_passed_result_object = FALSE, + set_no_trans; /* ERROR_MSG_LENGTH is suffcient */ static char msgbuffer[ERROR_MSG_LENGTH + 1]; @@ -1173,7 +1184,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send Query to backend"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); return NULL; } @@ -1182,10 +1193,12 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send Query to backend"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); return NULL; } + if (issue_begin) + SOCK_put_n_char(sock, "begin;", 6); SOCK_put_string(sock, query); SOCK_flush_output(sock); @@ -1193,7 +1206,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send Query to backend"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); return NULL; } @@ -1230,7 +1243,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) self->errormsg = "No response from the backend"; mylog("send_query: 'id' - %s\n", self->errormsg); - CC_set_no_trans(self); + CC_on_abort(self, TRUE); ReadyToReturn = TRUE; retres = NULL; break; @@ -1254,7 +1267,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) self->errornumber = CONNECTION_NO_RESPONSE; self->errormsg = "No response from backend while receiving a portal query command"; mylog("send_query: 'C' - %s\n", self->errormsg); - CC_set_no_trans(self); + CC_on_abort(self, TRUE); ReadyToReturn = TRUE; retres = NULL; } @@ -1270,6 +1283,24 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) mylog("send_query: setting cmdbuffer = '%s'\n", cmdbuffer); + if (strnicmp(cmdbuffer, "BEGIN", 5) == 0) + { + CC_set_in_trans(self); + if (issue_begin) + { + issue_begin = FALSE; + continue; + } + } + else if (strnicmp(cmdbuffer, "COMMIT", 6) == 0) + CC_on_commit(self, TRUE); + else if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0) + CC_on_abort(self, TRUE); + else if (strnicmp(cmdbuffer, "END", 3) == 0) + CC_on_commit(self, TRUE); + else if (strnicmp(cmdbuffer, "ABORT", 5) == 0) + CC_on_abort(self, TRUE); + if (QR_command_successful(res)) QR_set_status(res, PGRES_COMMAND_OK); QR_set_command(res, cmdbuffer); @@ -1352,14 +1383,15 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) qlog("ERROR from backend during send_query: '%s'\n", msgbuffer); /* We should report that an error occured. Zoltan */ - + set_no_trans = FALSE; if (!strncmp(msgbuffer, "FATAL", 5)) { self->errornumber = CONNECTION_SERVER_REPORTED_ERROR; - CC_set_no_trans(self); + set_no_trans = TRUE; } else self->errornumber = CONNECTION_SERVER_REPORTED_WARNING; + CC_on_abort(self, set_no_trans); QR_set_status(res, PGRES_FATAL_ERROR); QR_set_message(res, msgbuffer); QR_set_aborted(res, TRUE); @@ -1377,9 +1409,6 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) if (query_completed) { res->next = QR_Constructor(); - if (create_keyset) - QR_set_haskeyset(res->next); - mylog("send_query: 'T' no result_in: res = %u\n", res->next); if (!res->next) { self->errornumber = CONNECTION_COULD_NOT_RECEIVE; @@ -1388,6 +1417,9 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) retres = NULL; break; } + if (create_keyset) + QR_set_haskeyset(res->next); + mylog("send_query: 'T' no result_in: res = %u\n", res->next); res = res->next; if (qi) @@ -1448,7 +1480,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) default: self->errornumber = CONNECTION_BACKEND_CRAZY; self->errormsg = "Unexpected protocol character from backend (send_query)"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); mylog("send_query: error - %s\n", self->errormsg); ReadyToReturn = TRUE; @@ -1536,7 +1568,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send function to backend"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); return FALSE; } @@ -1545,7 +1577,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send function to backend"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); return FALSE; } @@ -1594,6 +1626,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ case 'E': SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); self->errormsg = msgbuffer; + CC_on_abort(self, FALSE); mylog("send_function(V): 'E' - %s\n", self->errormsg); qlog("ERROR from backend during send_function: '%s'\n", self->errormsg); @@ -1606,7 +1639,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ default: self->errornumber = CONNECTION_BACKEND_CRAZY; self->errormsg = "Unexpected protocol character from backend (send_function, args)"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); mylog("send_function: error - %s\n", self->errormsg); return FALSE; @@ -1640,7 +1673,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ case 'E': SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); self->errormsg = msgbuffer; - + CC_on_abort(self, FALSE); mylog("send_function(G): 'E' - %s\n", self->errormsg); qlog("ERROR from backend during send_function: '%s'\n", self->errormsg); @@ -1661,7 +1694,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ default: self->errornumber = CONNECTION_BACKEND_CRAZY; self->errormsg = "Unexpected protocol character from backend (send_function, result)"; - CC_set_no_trans(self); + CC_on_abort(self, TRUE); mylog("send_function: error - %s\n", self->errormsg); return FALSE; diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h index ee19d27dcb..67411edd67 100644 --- a/src/interfaces/odbc/connection.h +++ b/src/interfaces/odbc/connection.h @@ -13,6 +13,7 @@ #include #include +#include "descriptor.h" typedef enum @@ -242,6 +243,8 @@ struct ConnectionClass_ HENV henv; /* environment this connection was created * on */ StatementOptions stmtOptions; + ARDFields ardOptions; + APDFields apdOptions; char *errormsg; int errornumber; CONN_Status status; @@ -315,8 +318,11 @@ void CC_lookup_pg_version(ConnectionClass *conn); void CC_initialize_pg_version(ConnectionClass *conn); void CC_log_error(const char *func, const char *desc, const ConnectionClass *self); int CC_get_max_query_len(const ConnectionClass *self); +void CC_on_commit(ConnectionClass *conn, BOOL set_no_trans); +void CC_on_abort(ConnectionClass *conn, BOOL set_no_trans); /* CC_send_query_options */ #define CLEAR_RESULT_ON_ABORT 1L #define CREATE_KEYSET (1L << 1) /* create keyset for updatable curosrs */ +#define GO_INTO_TRANSACTION (1L << 2) /* issue begin in advance */ #endif diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c index 94cd26c918..c0a2706ef6 100644 --- a/src/interfaces/odbc/convert.c +++ b/src/interfaces/odbc/convert.c @@ -107,7 +107,7 @@ char *mapFuncs[][2] = { {"DAYNAME", "to_char($1, 'Day')" }, {"DAYOFMONTH", "cast(extract(day from $1) as integer)" }, {"DAYOFWEEK", "(cast(extract(dow from $1) as integer) + 1)" }, - {"DAYOFYEAR", "cast(extract(doy from $1) as integer)" }, + {"DAYOFYEAR", "cast(extract(doy from $1) as integer)" }, {"HOUR", "cast(extract(hour from $1) as integer)" }, {"MINUTE", "cast(extract(minute from $1) as integer)" }, {"MONTH", "cast(extract(month from $1) as integer)" }, @@ -311,8 +311,9 @@ stime2timestamp(const SIMPLE_TIME *st, char *str, BOOL bZone, BOOL precision) int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col) { - BindInfoClass *bic = &(stmt->bindings[col]); - UInt4 offset = stmt->options.row_offset_ptr ? *stmt->options.row_offset_ptr : 0; + ARDFields *opts = SC_get_ARD(stmt); + BindInfoClass *bic = &(opts->bindings[col]); + UInt4 offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; return copy_and_convert_field(stmt, field_type, value, (Int2) bic->returntype, (PTR) (bic->buffer + offset), (SDWORD) bic->buflen, (SDWORD *) (bic->used + (offset >> 2))); @@ -325,6 +326,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue) { static char *func = "copy_and_convert_field"; + ARDFields *opts = SC_get_ARD(stmt); Int4 len = 0, copy_len = 0; SIMPLE_TIME st; @@ -335,7 +337,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 char *rgbValueBindRow; const char *ptr; int bind_row = stmt->bind_row; - int bind_size = stmt->options.bind_size; + int bind_size = opts->bind_size; int result = COPY_OK; BOOL changed, true_is_minus1 = FALSE; const char *neut_str = value; @@ -349,7 +351,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 if (stmt->current_col >= 0) { - pbic = &stmt->bindings[stmt->current_col]; + pbic = &opts->bindings[stmt->current_col]; if (pbic->data_left == -2) pbic->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be * * needed for ADO ? */ @@ -404,7 +406,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 else { stmt->errornumber = STMT_RETURN_NULL_WITHOUT_INDICATOR; - stmt->errormsg = "StrLen_or_IndPtr was a null pointer and NULL data was retrieved"; + stmt->errormsg = "StrLen_or_IndPtr was a null pointer and NULL data was retrieved"; SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -651,7 +653,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 pbic->data_left = -1; } else - pbic = &stmt->bindings[stmt->current_col]; + pbic = &opts->bindings[stmt->current_col]; if (pbic->data_left < 0) { BOOL lf_conv = SC_get_conn(stmt)->connInfo.lf_conversion; @@ -759,7 +761,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 if (cbValueMax < 2 * (SDWORD) ucount) result = COPY_RESULT_TRUNCATED; len = ucount * 2; - free(str); + free(str); } else { @@ -923,6 +925,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 case SQL_C_ULONG: len = 4; +inolog("rgb=%x + %d, pcb=%x, set %s\n", rgbValue, bind_row * bind_size, pcbValue, neut_str); if (bind_size > 0) *(UDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(neut_str); else @@ -960,7 +963,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 pbic->data_left = -1; } else - pbic = &stmt->bindings[stmt->current_col]; + pbic = &opts->bindings[stmt->current_col]; if (!pbic->ttlbuf) pbic->ttlbuflen = 0; if (len = strlen(neut_str), len >= (int) pbic->ttlbuflen) @@ -1026,7 +1029,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len; if (result == COPY_OK && stmt->current_col >= 0) - stmt->bindings[stmt->current_col].data_left = 0; + opts->bindings[stmt->current_col].data_left = 0; return result; } @@ -1283,16 +1286,14 @@ copy_statement_with_parameters(StatementClass *stmt) char token_save[64]; int token_len; BOOL prev_token_end; - UInt4 offset = stmt->options.param_offset_ptr ? *stmt->options.param_offset_ptr : 0; + APDFields *opts = SC_get_APD(stmt); + UInt4 offset = opts->param_offset_ptr ? *opts->param_offset_ptr : 0; UInt4 current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; BOOL lf_conv = ci->lf_conversion; #ifdef MULTIBYTE encoded_str encstr; #endif /* MULTIBYTE */ -#ifdef DRIVER_CURSOR_IMPLEMENT - BOOL search_from_pos = FALSE; -#endif /* DRIVER_CURSOR_IMPLEMENT */ Int4 from_pos = -1, where_pos = -1; if (ci->disallow_premature) @@ -1324,17 +1325,20 @@ copy_statement_with_parameters(StatementClass *stmt) { if (stmt->parse_status == STMT_PARSE_NONE) parse_statement(stmt); - if (stmt->parse_status != STMT_PARSE_COMPLETE) + /*if (stmt->parse_status != STMT_PARSE_COMPLETE) stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - else if (!stmt->ti || stmt->ntab != 1) + else*/ if (!stmt->updatable) stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; else { - /** search_from_pos = TRUE; **/ from_pos = stmt->from_pos; where_pos = stmt->where_pos; } } +#else + stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; + if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) + stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY; #endif /* DRIVER_CURSOR_IMPLEMENT */ /* If the application hasn't set a cursor name, then generate one */ @@ -1433,8 +1437,7 @@ copy_statement_with_parameters(StatementClass *stmt) */ else if (oldchar == '{') { - char *begin = &old_statement[opos]; - const char *end; + const char *begin = &old_statement[opos], *end; /* procedure calls */ if (stmt->statement_type == STMT_TYPE_PROCCALL) @@ -1466,8 +1469,8 @@ copy_statement_with_parameters(StatementClass *stmt) proc_no_param = FALSE; continue; } - if (convert_escape(begin, stmt, &npos, &new_stsize, &end) != - CONVERT_ESCAPE_OK) + if (convert_escape(begin, stmt, &npos, &new_stsize, &end +) != CONVERT_ESCAPE_OK) { stmt->errormsg = "ODBC escape convert error"; stmt->errornumber = STMT_EXEC_ERROR; @@ -1522,15 +1525,6 @@ copy_statement_with_parameters(StatementClass *stmt) memmove(new_statement, new_statement + declare_pos, npos - declare_pos); npos -= declare_pos; } -#ifdef DRIVER_CURSOR_IMPLEMENT - else if (search_from_pos && /* where's from clause */ - strnicmp(token_save, "from", 4) == 0) - { - search_from_pos = FALSE; - npos -= 5; - CVT_APPEND_STR(", CTID, OID from"); - } -#endif /* DRIVER_CURSOR_IMPLEMENT */ } if (token_len == 3) { @@ -1574,7 +1568,7 @@ copy_statement_with_parameters(StatementClass *stmt) */ param_number++; - if (param_number >= stmt->parameters_allocated) + if (param_number >= opts->allocated) { if (stmt->pre_executing) { @@ -1590,34 +1584,34 @@ copy_statement_with_parameters(StatementClass *stmt) } /* Assign correct buffers based on data at exec param or not */ - if (stmt->parameters[param_number].data_at_exec) + if (opts->parameters[param_number].data_at_exec) { - used = stmt->parameters[param_number].EXEC_used ? *stmt->parameters[param_number].EXEC_used : SQL_NTS; - buffer = stmt->parameters[param_number].EXEC_buffer; + used = opts->parameters[param_number].EXEC_used ? *opts->parameters[param_number].EXEC_used : SQL_NTS; + buffer = opts->parameters[param_number].EXEC_buffer; } else { - UInt4 bind_size = stmt->options.param_bind_type; + UInt4 bind_size = opts->param_bind_type; UInt4 ctypelen; - buffer = stmt->parameters[param_number].buffer + offset; + buffer = opts->parameters[param_number].buffer + offset; if (current_row > 0) { if (bind_size > 0) buffer += (bind_size * current_row); - else if (ctypelen = ctype_length(stmt->parameters[param_number].CType), ctypelen > 0) + else if (ctypelen = ctype_length(opts->parameters[param_number].CType), ctypelen > 0) buffer += current_row * ctypelen; - else - buffer += current_row * stmt->parameters[param_number].buflen; + else + buffer += current_row * opts->parameters[param_number].buflen; } - if (stmt->parameters[param_number].used) + if (opts->parameters[param_number].used) { UInt4 p_offset = offset; if (bind_size > 0) p_offset = offset + bind_size * current_row; else p_offset = offset + sizeof(SDWORD) * current_row; - used = *(SDWORD *)((char *)stmt->parameters[param_number].used + p_offset); + used = *(SDWORD *)((char *)opts->parameters[param_number].used + p_offset); } else used = SQL_NTS; @@ -1649,8 +1643,8 @@ copy_statement_with_parameters(StatementClass *stmt) } } - param_ctype = stmt->parameters[param_number].CType; - param_sqltype = stmt->parameters[param_number].SQLType; + param_ctype = opts->parameters[param_number].CType; + param_sqltype = opts->parameters[param_number].SQLType; mylog("copy_statement_with_params: from(fcType)=%d, to(fSqlType)=%d\n", param_ctype, param_sqltype); @@ -1907,8 +1901,8 @@ copy_statement_with_parameters(StatementClass *stmt) case SQL_LONGVARBINARY: - if (stmt->parameters[param_number].data_at_exec) - lobj_oid = stmt->parameters[param_number].lobj_oid; + if (opts->parameters[param_number].data_at_exec) + lobj_oid = opts->parameters[param_number].lobj_oid; else { /* begin transaction if needed */ @@ -2048,13 +2042,14 @@ copy_statement_with_parameters(StatementClass *stmt) } #ifdef DRIVER_CURSOR_IMPLEMENT - if (search_from_pos) - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; if (!stmt->load_statement && from_pos >=0) { stmt->load_statement = malloc(npos + 1); memcpy(stmt->load_statement, new_statement, npos); - stmt->load_statement[npos] = '\0'; + if (stmt->load_statement[npos - 1] == ';') + stmt->load_statement[npos - 1] = '\0'; + else + stmt->load_statement[npos] = '\0'; } #endif /* DRIVER_CURSOR_IMPLEMENT */ if (prepare_dummy_cursor && SC_is_pre_executable(stmt)) @@ -2083,7 +2078,7 @@ mapFunction(const char *func, int param_count) if (mapFuncs[i][0][0] == '%') { if (mapFuncs[i][0][1] - '0' == param_count && - !stricmp(mapFuncs[i][0] + 2, func)) + !stricmp(mapFuncs[i][0] + 2, func)) return mapFuncs[i][1]; } else if (!stricmp(mapFuncs[i][0], func)) @@ -2101,7 +2096,7 @@ static int processParameters(const ConnectionClass *conn, const char *value, cha * inner_convert_escape() * work with embedded escapes sequences */ - + static int inner_convert_escape(const ConnectionClass *conn, const char *value, char *result, UInt4 maxLen, const char **input_resume, @@ -2114,7 +2109,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, const char *valptr; UInt4 vlen, prtlen, input_consumed, param_consumed, extra_len; Int4 param_pos[16][2]; - + valptr = value; if (*valptr == '{') /* skip the first { */ valptr++; @@ -2126,7 +2121,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, valptr++; while ((*valptr != '\0') && isspace((unsigned char) *valptr)) valptr++; - + if (end = my_strchr(conn, valptr, '}'), NULL == end) { mylog("%s couldn't find the ending }\n",func); @@ -2138,7 +2133,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, valnts[vlen] = '\0'; *input_resume = valptr + vlen; /* resume from the last } */ mylog("%s: key='%s', val='%s'\n", func, key, valnts); - + extra_len = 0; if (isalnum(result[-1])) /* Avoid the concatenation of the function name with the previous word. Aceto */ { @@ -2175,7 +2170,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, { /* Literal; return the escape part as-is */ strncpy(result, valnts, maxLen); - prtlen = vlen; + prtlen = vlen; } else if (strcmp(key, "fn") == 0) { @@ -2186,7 +2181,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, char *funcEnd = valnts; char svchar; const char *mapExpr; - + params[sizeof(params)-1] = '\0'; while ((*funcEnd != '\0') && (*funcEnd != '(') && @@ -2200,7 +2195,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, funcEnd++; /* - * We expect left parenthesis here, else return fn body as-is + * We expect left parenthesis here, else return fn body as-is * since it is one of those "function constants". */ if (*funcEnd != '(') @@ -2216,7 +2211,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, */ valptr += (UInt4)(funcEnd - valnts); - if (subret = processParameters(conn, valptr, params, sizeof(params) - 1, &input_consumed, ¶m_consumed, param_pos), CONVERT_ESCAPE_OK != subret) + if (subret = processParameters(conn, valptr, params, sizeof(params) - 1, &input_consumed, ¶m_consumed, param_pos), CONVERT_ESCAPE_OK != subret) return CONVERT_ESCAPE_ERROR; for (param_count = 0;; param_count++) @@ -2225,9 +2220,9 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, break; } if (param_count == 1 && - param_pos[0][1] < param_pos[0][0]) + param_pos[0][1] < param_pos[0][0]) param_count = 0; - + mapExpr = mapFunction(key, param_count); if (mapExpr == NULL) prtlen = snprintf(result, maxLen, "%s%s", key, params); @@ -2259,7 +2254,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, { pidx = *mapptr - '0' - 1; if (pidx < 0 || - param_pos[pidx][0] <0) + param_pos[pidx][0] < 0) { qlog("%s %dth param not found for the expression %s\n", pidx + 1, mapExpr); return CONVERT_ESCAPE_ERROR; @@ -2294,7 +2289,7 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, /* Bogus key, leave untranslated */ return CONVERT_ESCAPE_ERROR; } - + if (count) *count = prtlen + extra_len; if (prtlen < 0 || prtlen >= maxLen) /* buffer overflow */ @@ -2304,12 +2299,12 @@ int inner_convert_escape(const ConnectionClass *conn, const char *value, } return CONVERT_ESCAPE_OK; } - + /* * processParameters() * Process function parameters and work with embedded escapes sequences. */ - + static int processParameters(const ConnectionClass *conn, const char *value, char *result, UInt4 maxLen, UInt4 *input_consumed, @@ -2324,7 +2319,7 @@ int processParameters(const ConnectionClass *conn, const char *value, #ifdef MULTIBYTE encoded_str encstr; #endif /* MULTIBYTE */ - + buf[sizeof(buf)-1] = '\0'; innerParenthesis = 0; in_quote = in_dquote = in_escape = leadingSpace = FALSE; @@ -2403,7 +2398,7 @@ int processParameters(const ConnectionClass *conn, const char *value, } innerParenthesis++; break; - + case ')': innerParenthesis--; if (0 == innerParenthesis) @@ -2414,18 +2409,18 @@ int processParameters(const ConnectionClass *conn, const char *value, param_pos[param_count][1] = -1; } break; - + case '}': stop = TRUE; break; - + case '{': if (subret = inner_convert_escape(conn, valptr, buf, sizeof(buf) - 1, &valptr, &inner_count), CONVERT_ESCAPE_OK != subret) return CONVERT_ESCAPE_ERROR; - + if (inner_count + count >= maxLen) return CONVERT_ESCAPE_OVERFLOW; - memcpy(&result[count], buf, inner_count); + memcpy(&result[count], buf, inner_count); count += inner_count; ipos = (UInt4) (valptr - value); continue; @@ -2445,15 +2440,14 @@ int processParameters(const ConnectionClass *conn, const char *value, *output_count = count; return CONVERT_ESCAPE_OK; } - + /* * convert_escape() * This function returns a pointer to static memory! */ - + int -convert_escape(const char *value, StatementClass *stmt, int *npos, int *stsize, - const char **val_resume) +convert_escape(const char *value, StatementClass *stmt, int *npos, int *stsize, const char **val_resume) { int ret, pos = *npos; UInt4 count; @@ -2920,12 +2914,13 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, BindInfoClass *bindInfo = NULL; ConnectionClass *conn = SC_get_conn(stmt); ConnInfo *ci = &(conn->connInfo); + ARDFields *opts = SC_get_ARD(stmt); int factor = (fCType == SQL_C_CHAR ? 2 : 1); /* If using SQLGetData, then current_col will be set */ if (stmt->current_col >= 0) { - bindInfo = &stmt->bindings[stmt->current_col]; + bindInfo = &opts->bindings[stmt->current_col]; left = bindInfo->data_left; } diff --git a/src/interfaces/odbc/descriptor.h b/src/interfaces/odbc/descriptor.h new file mode 100644 index 0000000000..58036914ad --- /dev/null +++ b/src/interfaces/odbc/descriptor.h @@ -0,0 +1,96 @@ +/* File: descriptor.h + * + * Description: This file contains defines and declarations that are related to + * the entire driver. + * + * Comments: See "notice.txt" for copyright and license information. + * + * $Id: descriptor.h,v 1.1 2002/03/28 08:08:02 inoue Exp $ + * + */ + +#ifndef __DESCRIPTOR_H__ +#define __DESCRIPTOR_H__ + +#include "psqlodbc.h" + +typedef struct +{ + COL_INFO *col_info; /* cached SQLColumns info for this table */ + char name[MAX_TABLE_LEN + 1]; + char alias[MAX_TABLE_LEN + 1]; +} TABLE_INFO; + +typedef struct +{ + TABLE_INFO *ti; /* resolve to explicit table names */ + int column_size; /* precision in 2.x */ + int decimal_digits; /* scale in 2.x */ + int display_size; + int length; + int type; + char nullable; + char func; + char expr; + char quote; + char dquote; + char numeric; + char updatable; + char dot[MAX_TABLE_LEN + 1]; + char name[MAX_COLUMN_LEN + 1]; + char alias[MAX_COLUMN_LEN + 1]; +} FIELD_INFO; + +struct ARDFields_ +{ + StatementClass *stmt; + int rowset_size; + int bind_size; /* size of each structure if using Row + * Binding */ + UInt2 *row_operation_ptr; + UInt4 *row_offset_ptr; + BindInfoClass *bookmark; + BindInfoClass *bindings; + int allocated; +}; + +struct APDFields_ +{ + StatementClass *stmt; + int paramset_size; + int param_bind_type; /* size of each structure if using Param + * Binding */ + UInt2 *param_operation_ptr; + UInt4 *param_offset_ptr; + ParameterInfoClass *parameters; + int allocated; +}; + +struct IRDFields_ +{ + StatementClass *stmt; + UInt4 *rowsFetched; + UInt2 *rowStatusArray; + UInt4 nfields; + FIELD_INFO **fi; +}; + +struct IPDFields_ +{ + StatementClass *stmt; + UInt4 *param_processed_ptr; + UInt2 *param_status_ptr; +}; + +void InitializeARDFields(ARDFields *self); +void InitializeAPDFields(APDFields *self); +/* void InitializeIRDFields(IRDFields *self); +void InitializeIPDFiedls(IPDFields *self); */ +void ARDFields_free(ARDFields *self); +void APDFields_free(APDFields *self); +void IRDFields_free(IRDFields *self); +void IPDFields_free(IPDFields *self); +void ARD_unbind_cols(ARDFields *self, BOOL freeall); +void APD_free_params(APDFields *self, char option); + +#endif diff --git a/src/interfaces/odbc/dlg_specific.c b/src/interfaces/odbc/dlg_specific.c index a95c67634c..a4423e43b4 100644 --- a/src/interfaces/odbc/dlg_specific.c +++ b/src/interfaces/odbc/dlg_specific.c @@ -120,6 +120,9 @@ driver_optionsDraw(HWND hdlg, const ConnInfo *ci, int src, BOOL enable) } CheckDlgButton(hdlg, DRV_COMMLOG, comval->commlog); +#ifndef Q_LOG + EnableWindow(GetDlgItem(hdlg, DRV_COMMLOG), FALSE); +#endif /* Q_LOG */ CheckDlgButton(hdlg, DRV_OPTIMIZER, comval->disable_optimizer); CheckDlgButton(hdlg, DRV_KSQO, comval->ksqo); CheckDlgButton(hdlg, DRV_UNIQUEINDEX, comval->unique_index); @@ -153,6 +156,9 @@ driver_optionsDraw(HWND hdlg, const ConnInfo *ci, int src, BOOL enable) CheckDlgButton(hdlg, DRV_PARSE, comval->parse); CheckDlgButton(hdlg, DRV_CANCELASFREESTMT, comval->cancel_as_freestmt); CheckDlgButton(hdlg, DRV_DEBUG, comval->debug); +#ifndef MY_LOG + EnableWindow(GetDlgItem(hdlg, DRV_DEBUG), FALSE); +#endif /* MY_LOG */ SetDlgItemInt(hdlg, DRV_CACHE_SIZE, comval->fetch_max, FALSE); SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, comval->max_varchar_size, FALSE); SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, comval->max_longvarchar_size, TRUE); @@ -221,7 +227,7 @@ driver_options_update(HWND hdlg, ConnInfo *ci, BOOL updateProfile) int CALLBACK driver_optionsProc(HWND hdlg, - WORD wMsg, + UINT wMsg, WPARAM wParam, LPARAM lParam) { @@ -293,7 +299,7 @@ driver_optionsProc(HWND hdlg, int CALLBACK ds_optionsProc(HWND hdlg, - WORD wMsg, + UINT wMsg, WPARAM wParam, LPARAM lParam) { @@ -334,6 +340,10 @@ ds_optionsProc(HWND hdlg, CheckDlgButton(hdlg, DS_DISALLOWPREMATURE, ci->disallow_premature); CheckDlgButton(hdlg, DS_LFCONVERSION, ci->lf_conversion); CheckDlgButton(hdlg, DS_TRUEISMINUS1, ci->true_is_minus1); + CheckDlgButton(hdlg, DS_UPDATABLECURSORS, ci->updatable_cursors); +#ifndef DRIVER_CURSOR_IMPLEMENT + EnableWindow(GetDlgItem(hdlg, DS_UPDATABLECURSORS), FALSE); +#endif /* DRIVER_CURSOR_IMPLEMENT */ EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), atoi(ci->show_oid_column)); @@ -371,6 +381,9 @@ ds_optionsProc(HWND hdlg, ci->disallow_premature = IsDlgButtonChecked(hdlg, DS_DISALLOWPREMATURE); ci->lf_conversion = IsDlgButtonChecked(hdlg, DS_LFCONVERSION); ci->true_is_minus1 = IsDlgButtonChecked(hdlg, DS_TRUEISMINUS1); +#ifdef DRIVER_CURSOR_IMPLEMENT + ci->updatable_cursors = IsDlgButtonChecked(hdlg, DS_UPDATABLECURSORS); +#endif /* DRIVER_CURSOR_IMPLEMENT */ /* OID Options */ sprintf(ci->fake_oid_index, "%d", IsDlgButtonChecked(hdlg, DS_FAKEOIDINDEX)); diff --git a/src/interfaces/odbc/dlg_specific.h b/src/interfaces/odbc/dlg_specific.h index f293079d35..98ecac0bbc 100644 --- a/src/interfaces/odbc/dlg_specific.h +++ b/src/interfaces/odbc/dlg_specific.h @@ -169,11 +169,11 @@ void SetDlgStuff(HWND hdlg, const ConnInfo *ci); void GetDlgStuff(HWND hdlg, ConnInfo *ci); int CALLBACK driver_optionsProc(HWND hdlg, - WORD wMsg, + UINT wMsg, WPARAM wParam, LPARAM lParam); int CALLBACK ds_optionsProc(HWND hdlg, - WORD wMsg, + UINT wMsg, WPARAM wParam, LPARAM lParam); #endif /* WIN32 */ diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c index 618c5ef003..4fdfe74c9d 100644 --- a/src/interfaces/odbc/environ.c +++ b/src/interfaces/odbc/environ.c @@ -94,7 +94,7 @@ PGAPI_StmtError( HSTMT hstmt, StatementClass *stmt = (StatementClass *) hstmt; char *msg; int status; - BOOL partial_ok = ((flag & PODBC_ALLOW_PARTIAL_EXTRACT) != 0), + BOOL partial_ok = ((flag & PODBC_ALLOW_PARTIAL_EXTRACT) != 0), clear_str = ((flag & PODBC_ERROR_CLEAR) != 0); SWORD msglen, stapos, wrtlen, pcblen; @@ -275,6 +275,9 @@ PGAPI_StmtError( HSTMT hstmt, case STMT_OPERATION_INVALID: strcpy(szSqlState, "S1011"); break; + case STMT_INVALID_DESCRIPTOR_IDENTIFIER: + strcpy(szSqlState, "HY091"); + break; case STMT_INVALID_OPTION_IDENTIFIER: strcpy(szSqlState, "HY092"); break; @@ -313,6 +316,7 @@ PGAPI_ConnectError( HDBC hdbc, BOOL once_again = FALSE; SWORD msglen; + mylog("**** PGAPI_ConnectError: hdbc=%u <%d>\n", hdbc, cbErrorMsgMax); if (RecNumber != 1) return SQL_NO_DATA_FOUND; if (cbErrorMsgMax < 0) @@ -361,6 +365,7 @@ PGAPI_ConnectError( HDBC hdbc, strcpy(szSqlState, "IM002"); /* data source not found */ break; + case CONNECTION_SERVER_NOT_REACHED: case CONN_OPENDB_ERROR: strcpy(szSqlState, "08001"); /* unable to connect to data source */ @@ -413,6 +418,7 @@ PGAPI_ConnectError( HDBC hdbc, break; } + mylog(" szSqlState = '%s',len=%d, szError='%s'\n", szSqlState, msglen, szErrorMsg); if (once_again) { conn->errornumber = status; @@ -436,6 +442,7 @@ PGAPI_EnvError( HENV henv, char *msg; int status; + mylog("**** PGAPI_EnvError: henv=%u <%d>\n", henv, cbErrorMsgMax); if (RecNumber != 1) return SQL_NO_DATA_FOUND; if (cbErrorMsgMax < 0) @@ -567,7 +574,7 @@ EN_Destructor(EnvironmentClass *self) mylog("exit EN_Destructor: rv = %d\n", rv); #ifdef _MEMORY_DEBUG_ - debug_memory_inouecheck(); + debug_memory_check(); #endif /* _MEMORY_DEBUG_ */ return rv; } diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c index 8f05024f45..11893321f8 100644 --- a/src/interfaces/odbc/execute.c +++ b/src/interfaces/odbc/execute.c @@ -206,9 +206,12 @@ PGAPI_Execute( { static char *func = "PGAPI_Execute"; StatementClass *stmt = (StatementClass *) hstmt; + APDFields *opts; + IPDFields *ipdopts; ConnectionClass *conn; int i, retval, start_row, end_row; + int cursor_type, scroll_concurrency; mylog("%s: entering...\n", func); @@ -219,6 +222,9 @@ PGAPI_Execute( return SQL_INVALID_HANDLE; } + opts = SC_get_APD(stmt); + cursor_type = stmt->options.cursor_type; + scroll_concurrency = stmt->options.scroll_concurrency; /* * If the statement is premature, it means we already executed it from * an SQLPrepare/SQLDescribeCol type of scenario. So just return @@ -297,24 +303,25 @@ PGAPI_Execute( if (start_row = stmt->exec_start_row, start_row < 0) start_row = 0; if (end_row = stmt->exec_end_row, end_row < 0) - end_row = stmt->options.paramset_size - 1; + end_row = opts->paramset_size - 1; if (stmt->exec_current_row < 0) stmt->exec_current_row = start_row; + ipdopts = SC_get_IPD(stmt); if (stmt->exec_current_row == start_row) { - if (stmt->options.param_processed_ptr) - *stmt->options.param_processed_ptr = 0; + if (ipdopts->param_processed_ptr) + *ipdopts->param_processed_ptr = 0; SC_recycle_statement(stmt); } next_param_row: #if (ODBCVER >= 0x0300) - if (stmt->options.param_operation_ptr) + if (opts->param_operation_ptr) { - while (stmt->options.param_operation_ptr[stmt->exec_current_row] == SQL_PARAM_IGNORE) + while (opts->param_operation_ptr[stmt->exec_current_row] == SQL_PARAM_IGNORE) { - if (stmt->options.param_status_ptr) - stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_UNUSED; + if (ipdopts->param_status_ptr) + ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_UNUSED; if (stmt->exec_current_row >= end_row) { stmt->exec_current_row = -1; @@ -335,16 +342,16 @@ next_param_row: * execute of this statement? Therefore check for params and * re-copy. */ - UInt4 offset = stmt->options.param_offset_ptr ? *stmt->options.param_offset_ptr : 0; - Int4 bind_size = stmt->options.param_bind_type; + UInt4 offset = opts->param_offset_ptr ? *opts->param_offset_ptr : 0; + Int4 bind_size = opts->param_bind_type; Int4 current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; stmt->data_at_exec = -1; - for (i = 0; i < stmt->parameters_allocated; i++) + for (i = 0; i < opts->allocated; i++) { - Int4 *pcVal = stmt->parameters[i].used; + Int4 *pcVal = opts->parameters[i].used; - stmt->parameters[i].data_at_exec = FALSE; + opts->parameters[i].data_at_exec = FALSE; if (pcVal) { if (bind_size > 0) @@ -352,10 +359,10 @@ next_param_row: else pcVal = (Int4 *)((char *)pcVal + offset + sizeof(SDWORD) * current_row); if (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET) - stmt->parameters[i].data_at_exec = TRUE; + opts->parameters[i].data_at_exec = TRUE; } /* Check for data at execution parameters */ - if (stmt->parameters[i].data_at_exec) + if (opts->parameters[i].data_at_exec) { if (stmt->data_at_exec < 0) stmt->data_at_exec = 1; @@ -394,22 +401,22 @@ next_param_row: retval = SC_execute(stmt); if (retval != SQL_ERROR) { - if (stmt->options.param_processed_ptr) - (*stmt->options.param_processed_ptr)++; + if (ipdopts->param_processed_ptr) + (*ipdopts->param_processed_ptr)++; } #if (ODBCVER >= 0x0300) - if (stmt->options.param_status_ptr) + if (ipdopts->param_status_ptr) { switch (retval) { case SQL_SUCCESS: - stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; + ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; break; case SQL_SUCCESS_WITH_INFO: - stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; + ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; break; default: - stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; + ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; break; } } @@ -447,7 +454,6 @@ next_param_row: } } /* we are now in a transaction */ - CC_set_in_trans(conn); res = CC_send_query(conn, stmt->stmt_with_params, NULL, CLEAR_RESULT_ON_ABORT); if (!res) { @@ -464,13 +470,18 @@ next_param_row: { if (issued_begin) CC_commit(conn); - else if (!in_trans && begin_included) - CC_set_no_trans(conn); } stmt->status = STMT_FINISHED; return SQL_SUCCESS; } - else + else if (stmt->options.cursor_type != cursor_type || + stmt->options.scroll_concurrency != scroll_concurrency) + { + stmt->errornumber = STMT_OPTION_VALUE_CHANGED; + stmt->errormsg = "cursor updatability changed"; + return SQL_SUCCESS_WITH_INFO; + } + else return SQL_SUCCESS; } @@ -534,11 +545,10 @@ PGAPI_Transact( mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string); res = CC_send_query(conn, stmt_string, NULL, CLEAR_RESULT_ON_ABORT); - CC_set_no_trans(conn); - if (!res) { /* error msg will be in the connection */ + CC_on_abort(conn, TRUE); CC_log_error(func, "", conn); return SQL_ERROR; } @@ -548,6 +558,7 @@ PGAPI_Transact( if (!ok) { + CC_on_abort(conn, TRUE); CC_log_error(func, "", conn); return SQL_ERROR; } @@ -697,6 +708,8 @@ PGAPI_ParamData( { static char *func = "PGAPI_ParamData"; StatementClass *stmt = (StatementClass *) hstmt; + APDFields *opts; + IPDFields *ipdopts; int i, retval; ConnInfo *ci; @@ -709,8 +722,9 @@ PGAPI_ParamData( return SQL_INVALID_HANDLE; } ci = &(SC_get_conn(stmt)->connInfo); + opts = SC_get_APD(stmt); - mylog("%s: data_at_exec=%d, params_alloc=%d\n", func, stmt->data_at_exec, stmt->parameters_allocated); + mylog("%s: data_at_exec=%d, params_alloc=%d\n", func, stmt->data_at_exec, opts->allocated); if (stmt->data_at_exec < 0) { @@ -720,7 +734,7 @@ PGAPI_ParamData( return SQL_ERROR; } - if (stmt->data_at_exec > stmt->parameters_allocated) + if (stmt->data_at_exec > opts->allocated) { stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errormsg = "Too many execution-time parameters were present"; @@ -748,6 +762,7 @@ PGAPI_ParamData( } /* Done, now copy the params and then execute the statement */ + ipdopts = SC_get_IPD(stmt); if (stmt->data_at_exec == 0) { int end_row; @@ -761,28 +776,29 @@ PGAPI_ParamData( retval = SC_execute(stmt); if (retval != SQL_ERROR) { - if (stmt->options.param_processed_ptr) - (*stmt->options.param_processed_ptr)++; + if (ipdopts->param_processed_ptr) + (*ipdopts->param_processed_ptr)++; } #if (ODBCVER >= 0x0300) - if (stmt->options.param_status_ptr) + if (ipdopts->param_status_ptr) { switch (retval) { case SQL_SUCCESS: - stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; + ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; break; case SQL_SUCCESS_WITH_INFO: - stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; + ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; break; default: - stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; + ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; break; } } #endif /* ODBCVER */ - if (stmt->exec_end_row < 0) - end_row = stmt->options.paramset_size - 1; + end_row = stmt->exec_end_row; + if (end_row < 0) + end_row = opts->paramset_size - 1; if (retval == SQL_ERROR || stmt->exec_current_row >= end_row) { @@ -800,14 +816,14 @@ PGAPI_ParamData( i = stmt->current_exec_param >= 0 ? stmt->current_exec_param + 1 : 0; /* At least 1 data at execution parameter, so Fill in the token value */ - for (; i < stmt->parameters_allocated; i++) + for (; i < opts->allocated; i++) { - if (stmt->parameters[i].data_at_exec) + if (opts->parameters[i].data_at_exec) { stmt->data_at_exec--; stmt->current_exec_param = i; stmt->put_data = FALSE; - *prgbValue = stmt->parameters[i].buffer; /* token */ + *prgbValue = opts->parameters[i].buffer; /* token */ break; } } @@ -828,6 +844,7 @@ PGAPI_PutData( { static char *func = "PGAPI_PutData"; StatementClass *stmt = (StatementClass *) hstmt; + APDFields *opts; int old_pos, retval; ParameterInfoClass *current_param; @@ -841,6 +858,7 @@ PGAPI_PutData( return SQL_INVALID_HANDLE; } + opts = SC_get_APD(stmt); if (stmt->current_exec_param < 0) { stmt->errornumber = STMT_SEQUENCE_ERROR; @@ -849,7 +867,7 @@ PGAPI_PutData( return SQL_ERROR; } - current_param = &(stmt->parameters[stmt->current_exec_param]); + current_param = &(opts->parameters[stmt->current_exec_param]); if (!stmt->put_data) { /* first call */ diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index 99b1420ed2..a2aba6cf1e 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -107,7 +107,7 @@ PGAPI_GetInfo( case SQL_BOOKMARK_PERSISTENCE: /* ODBC 2.0 */ /* very simple bookmark support */ len = 4; - value = ci->drivers.use_declarefetch ? 0 : (SQL_BP_SCROLL); + value = ci->drivers.use_declarefetch ? 0 : (SQL_BP_SCROLL | SQL_BP_DELETE | SQL_BP_UPDATE | SQL_BP_TRANSACTION); break; case SQL_COLUMN_ALIAS: /* ODBC 2.0 */ @@ -495,8 +495,10 @@ PGAPI_GetInfo( case SQL_POS_OPERATIONS: /* ODBC 2.0 */ len = 4; value = ci->drivers.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : (SQL_POS_POSITION | SQL_POS_REFRESH); +#ifdef DRIVER_CURSOR_IMPLEMENT if (ci->updatable_cursors) value |= (SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD); +#endif /* DRIVER_CURSOR_IMPLEMENT */ break; case SQL_POSITIONED_STATEMENTS: /* ODBC 2.0 */ @@ -554,8 +556,10 @@ PGAPI_GetInfo( SQL_SCCO_OPT_ROWVER | SQL_SCCO_OPT_VALUES) : (SQL_SCCO_READ_ONLY); +#ifdef DRIVER_CURSOR_IMPLEMENT if (ci->updatable_cursors) value |= SQL_SCCO_OPT_ROWVER; +#endif /* DRIVER_CURSOR_IMPLEMENT */ break; case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */ @@ -588,8 +592,10 @@ PGAPI_GetInfo( case SQL_STATIC_SENSITIVITY: /* ODBC 2.0 */ len = 4; value = ci->drivers.lie ? (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES) : 0; +#ifdef DRIVER_CURSOR_IMPLEMENT if (ci->updatable_cursors) value |= (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES); +#endif /* DRIVER_CURSOR_IMPLEMENT */ break; case SQL_STRING_FUNCTIONS: /* ODBC 1.0 */ @@ -753,7 +759,11 @@ PGAPI_GetTypeInfo( } SC_set_Result(stmt, res); - extend_bindings(stmt, 15); +#if (ODBCVER >= 0x0399) + extend_column_bindings(SC_get_ARD(stmt), 19); +#else + extend_column_bindings(SC_get_ARD(stmt), 15); +#endif /* ODBCVER */ QR_set_num_fields(res, 15); QR_set_field_info(res, 0, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING); @@ -771,6 +781,12 @@ PGAPI_GetTypeInfo( QR_set_field_info(res, 12, "LOCAL_TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING); QR_set_field_info(res, 13, "MINIMUM_SCALE", PG_TYPE_INT2, 2); QR_set_field_info(res, 14, "MAXIMUM_SCALE", PG_TYPE_INT2, 2); +#if (ODBCVER >=0x0399) + QR_set_field_info(res, 15, "SQL_DATA_TYPE", PG_TYPE_INT2, 2); + QR_set_field_info(res, 16, "SQL_DATATIME_SUB", PG_TYPE_INT2, 2); + QR_set_field_info(res, 17, "NUM_PREC_RADIX", PG_TYPE_INT4, 4); + QR_set_field_info(res, 18, "INTERVAL_PRECISION", PG_TYPE_INT2, 2); +#endif /* ODBCVER */ for (i = 0, sqlType = sqlTypes[0]; sqlType; sqlType = sqlTypes[++i]) { @@ -795,7 +811,7 @@ PGAPI_GetTypeInfo( 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_int4(&row->tuple[2], pgtype_column_size(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)); @@ -803,6 +819,12 @@ PGAPI_GetTypeInfo( set_nullfield_int2(&row->tuple[11], pgtype_auto_increment(stmt, pgType)); set_nullfield_int2(&row->tuple[13], pgtype_scale(stmt, pgType, PG_STATIC)); set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, pgType, PG_STATIC)); +#if (ODBCVER >=0x0399) + set_nullfield_int2(&row->tuple[15], pgtype_sqldesctype(stmt, pgType)); + set_nullfield_int2(&row->tuple[16], pgtype_datetime_sub(stmt, pgType)); + set_nullfield_int4(&row->tuple[17], pgtype_radix(stmt, pgType)); + set_nullfield_int4(&row->tuple[18], 0); +#endif /* ODBCVER */ QR_add_tuple(res, row); } @@ -1317,7 +1339,7 @@ PGAPI_Tables( * a statement is actually executed, so we'll have to do this * ourselves. */ - extend_bindings(stmt, 5); + extend_column_bindings(SC_get_ARD(stmt), 5); /* set the field names */ QR_set_num_fields(res, 5); @@ -1523,13 +1545,15 @@ PGAPI_Columns( field_name[MAX_INFO_STRING], field_type_name[MAX_INFO_STRING]; Int2 field_number, sqltype, + reserved_cols, result_cols, - scale; + decimal_digits; Int4 field_type, the_type, field_length, mod_length, - precision; + column_size, + ordinal; char useStaticPrecision; char not_null[MAX_INFO_STRING], relhasrules[MAX_INFO_STRING]; @@ -1735,11 +1759,12 @@ PGAPI_Columns( * ourselves. */ #if (ODBCVER >= 0x0300) - result_cols = 18; + reserved_cols = 18; #else - result_cols = 14; + reserved_cols = 12; #endif /* ODBCVER */ - extend_bindings(stmt, result_cols); + result_cols = reserved_cols + 2; + extend_column_bindings(SC_get_ARD(stmt), result_cols); /* set the field names */ QR_set_num_fields(res, result_cols); @@ -1749,9 +1774,9 @@ PGAPI_Columns( QR_set_field_info(res, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING); QR_set_field_info(res, 4, "DATA_TYPE", PG_TYPE_INT2, 2); QR_set_field_info(res, 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING); - QR_set_field_info(res, 6, "PRECISION", PG_TYPE_INT4, 4); - QR_set_field_info(res, 7, "LENGTH", PG_TYPE_INT4, 4); - QR_set_field_info(res, 8, "SCALE", PG_TYPE_INT2, 2); + QR_set_field_info(res, 6, "PRECISION", PG_TYPE_INT4, 4); /* COLUMN_SIZE */ + QR_set_field_info(res, 7, "LENGTH", PG_TYPE_INT4, 4); /* BUFFER_LENGTH */ + QR_set_field_info(res, 8, "SCALE", PG_TYPE_INT2, 2); /* DECIMAL_DIGITS ***/ QR_set_field_info(res, 9, "RADIX", PG_TYPE_INT2, 2); QR_set_field_info(res, 10, "NULLABLE", PG_TYPE_INT2, 2); QR_set_field_info(res, 11, "REMARKS", PG_TYPE_TEXT, 254); @@ -1764,11 +1789,11 @@ PGAPI_Columns( QR_set_field_info(res, 15, "CHAR_OCTET_LENGTH", PG_TYPE_INT4, 4); QR_set_field_info(res, 16, "ORDINAL_POSITION", PG_TYPE_INT4, 4); QR_set_field_info(res, 17, "IS_NULLABLE", PG_TYPE_TEXT, 254); -#else - QR_set_field_info(res, 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4); - QR_set_field_info(res, 13, "FIELD_TYPE", PG_TYPE_INT4, 4); #endif /* ODBCVER */ + QR_set_field_info(res, reserved_cols, "DISPLAY_SIZE", PG_TYPE_INT4, 4); + QR_set_field_info(res, reserved_cols + 1, "FIELD_TYPE", PG_TYPE_INT4, 4); + ordinal = 1; result = PGAPI_Fetch(hcol_stmt); /* @@ -1794,14 +1819,13 @@ PGAPI_Columns( set_tuplefield_string(&row->tuple[1], ""); set_tuplefield_string(&row->tuple[2], table_name); set_tuplefield_string(&row->tuple[3], "oid"); - sqltype = pgtype_to_sqltype(stmt, the_type); + sqltype = pgtype_to_concise_type(stmt, the_type); set_tuplefield_int2(&row->tuple[4], sqltype); set_tuplefield_string(&row->tuple[5], "OID"); - set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC)); - - set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type, PG_STATIC)); + set_tuplefield_int4(&row->tuple[6], pgtype_column_size(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[7], pgtype_buffer_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_nullfield_int2(&row->tuple[8], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type)); set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS); set_tuplefield_string(&row->tuple[11], ""); @@ -1810,15 +1834,15 @@ PGAPI_Columns( set_tuplefield_null(&row->tuple[12]); set_tuplefield_int2(&row->tuple[13], sqltype); set_tuplefield_null(&row->tuple[14]); - set_tuplefield_int4(&row->tuple[15], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[16], 0); + set_tuplefield_int4(&row->tuple[15], pgtype_transfer_octet_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[16], ordinal); set_tuplefield_string(&row->tuple[17], "No"); -#else - set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[13], the_type); #endif /* ODBCVER */ + set_tuplefield_int4(&row->tuple[reserved_cols], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[reserved_cols + 1], the_type); QR_add_tuple(res, row); + ordinal++; } } @@ -1834,7 +1858,7 @@ PGAPI_Columns( set_tuplefield_string(&row->tuple[1], ""); set_tuplefield_string(&row->tuple[2], table_name); set_tuplefield_string(&row->tuple[3], field_name); - sqltype = pgtype_to_sqltype(stmt, field_type); + sqltype = pgtype_to_concise_type(stmt, field_type); set_tuplefield_int2(&row->tuple[4], sqltype); set_tuplefield_string(&row->tuple[5], field_type_name); @@ -1845,15 +1869,15 @@ PGAPI_Columns( * VARCHAR - the length is stored in the pg_attribute.atttypmod field * BPCHAR - the length is also stored as varchar is * - * NUMERIC - the scale is stored in atttypmod as follows: + * NUMERIC - the decimal_digits is stored in atttypmod as follows: * - * precision =((atttypmod - VARHDRSZ) >> 16) & 0xffff - * scale = (atttypmod - VARHDRSZ) & 0xffff + * column_size =((atttypmod - VARHDRSZ) >> 16) & 0xffff + * decimal_digits = (atttypmod - VARHDRSZ) & 0xffff * *---------- */ - qlog("PGAPI_Columns: table='%s',field_name='%s',type=%d,sqltype=%d,name='%s'\n", - table_name, field_name, field_type, pgtype_to_sqltype, field_type_name); + qlog("PGAPI_Columns: table='%s',field_name='%s',type=%d,name='%s'\n", + table_name, field_name, field_type, field_type_name); useStaticPrecision = TRUE; @@ -1866,15 +1890,18 @@ PGAPI_Columns( { useStaticPrecision = FALSE; - precision = (mod_length >> 16) & 0xffff; - scale = mod_length & 0xffff; + column_size = (mod_length >> 16) & 0xffff; + decimal_digits = mod_length & 0xffff; - mylog("%s: field type is NUMERIC: field_type = %d, mod_length=%d, precision=%d, scale=%d\n", func, field_type, mod_length, precision, scale); + mylog("%s: field type is NUMERIC: field_type = %d, mod_length=%d, precision=%d, scale=%d\n", func, field_type, mod_length, column_size, decimal_digits); - set_tuplefield_int4(&row->tuple[7], precision + 2); /* sign+dec.point */ - set_tuplefield_int4(&row->tuple[6], precision); - set_tuplefield_int4(&row->tuple[12], precision + 2); /* sign+dec.point */ - set_nullfield_int2(&row->tuple[8], scale); + set_tuplefield_int4(&row->tuple[6], column_size); + set_tuplefield_int4(&row->tuple[7], column_size + 2); /* sign+dec.point */ + set_nullfield_int2(&row->tuple[8], decimal_digits); +#if (ODBCVER >= 0x0300) + set_tuplefield_null(&row->tuple[15]); +#endif /* ODBCVER */ + set_tuplefield_int4(&row->tuple[reserved_cols], column_size + 2); /* sign+dec.point */ } } @@ -1891,54 +1918,42 @@ PGAPI_Columns( mylog("%s: field type is VARCHAR,BPCHAR: field_type = %d, mod_length = %d\n", func, field_type, mod_length); - set_tuplefield_int4(&row->tuple[7], mod_length); set_tuplefield_int4(&row->tuple[6], mod_length); - set_tuplefield_int4(&row->tuple[12], mod_length); - set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, field_type, PG_STATIC)); + set_tuplefield_int4(&row->tuple[7], mod_length); + set_nullfield_int2(&row->tuple[8], pgtype_decimal_digits(stmt, field_type, PG_STATIC)); +#if (ODBCVER >= 0x0300) + set_tuplefield_int4(&row->tuple[15], pgtype_transfer_octet_length(stmt, field_type, PG_STATIC, PG_STATIC)); +#endif /* ODBCVER */ + set_tuplefield_int4(&row->tuple[reserved_cols], mod_length); } if (useStaticPrecision) { - mylog("%s: field type is OTHER: field_type = %d, pgtype_length = %d\n", func, field_type, pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC)); + mylog("%s: field type is OTHER: field_type = %d, pgtype_length = %d\n", func, field_type, pgtype_buffer_length(stmt, field_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, field_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, field_type, PG_STATIC, PG_STATIC)); - set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, field_type, PG_STATIC)); + set_tuplefield_int4(&row->tuple[6], pgtype_column_size(stmt, field_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[7], pgtype_buffer_length(stmt, field_type, PG_STATIC, PG_STATIC)); + set_nullfield_int2(&row->tuple[8], pgtype_decimal_digits(stmt, field_type, PG_STATIC)); +#if (ODBCVER >= 0x0300) + set_tuplefield_null(&row->tuple[15]); +#endif /* ODBCVER */ + set_tuplefield_int4(&row->tuple[reserved_cols], pgtype_display_size(stmt, field_type, PG_STATIC, PG_STATIC)); } set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, field_type)); set_tuplefield_int2(&row->tuple[10], (Int2) (not_null[0] == '1' ? SQL_NO_NULLS : pgtype_nullable(stmt, field_type))); set_tuplefield_string(&row->tuple[11], ""); #if (ODBCVER >= 0x0300) - switch (sqltype) - { - case SQL_TYPE_DATE: - set_tuplefield_int2(&row->tuple[13], SQL_DATETIME); - set_tuplefield_int2(&row->tuple[14], SQL_CODE_DATE); - break; - case SQL_TYPE_TIME: - set_tuplefield_int2(&row->tuple[13], SQL_DATETIME); - set_tuplefield_int2(&row->tuple[14], SQL_CODE_TIME); - break; - case SQL_TYPE_TIMESTAMP: - set_tuplefield_int2(&row->tuple[13], SQL_DATETIME); - set_tuplefield_int2(&row->tuple[14], SQL_CODE_TIMESTAMP); - break; - default: - set_tuplefield_int2(&row->tuple[13], sqltype); - set_tuplefield_null(&row->tuple[14]); - break; - } - set_tuplefield_int4(&row->tuple[15], pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[16], field_number); + set_tuplefield_null(&row->tuple[12]); + set_tuplefield_int2(&row->tuple[13], pgtype_to_sqldesctype(stmt, field_type)); + set_nullfield_int2(&row->tuple[14], pgtype_to_datetime_sub(stmt, field_type)); + set_tuplefield_int4(&row->tuple[16], ordinal); set_tuplefield_null(&row->tuple[17]); -#else - set_tuplefield_int4(&row->tuple[13], field_type); #endif /* ODBCVER */ + set_tuplefield_int4(&row->tuple[reserved_cols + 1], field_type); QR_add_tuple(res, row); - + ordinal++; result = PGAPI_Fetch(hcol_stmt); @@ -1968,12 +1983,12 @@ PGAPI_Columns( set_tuplefield_string(&row->tuple[1], ""); set_tuplefield_string(&row->tuple[2], table_name); set_tuplefield_string(&row->tuple[3], "xmin"); - sqltype = pgtype_to_sqltype(stmt, the_type); + sqltype = pgtype_to_concise_type(stmt, the_type); set_tuplefield_int2(&row->tuple[4], sqltype); set_tuplefield_string(&row->tuple[5], pgtype_to_name(stmt, the_type)); - set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC)); - set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type, PG_STATIC)); + set_tuplefield_int4(&row->tuple[6], pgtype_column_size(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[7], pgtype_buffer_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_nullfield_int2(&row->tuple[8], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type)); set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS); set_tuplefield_string(&row->tuple[11], ""); @@ -1981,15 +1996,15 @@ PGAPI_Columns( set_tuplefield_null(&row->tuple[12]); set_tuplefield_int2(&row->tuple[13], sqltype); set_tuplefield_null(&row->tuple[14]); - set_tuplefield_int4(&row->tuple[15], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[16], 0); + set_tuplefield_int4(&row->tuple[15], pgtype_transfer_octet_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[16], ordinal); set_tuplefield_string(&row->tuple[17], "No"); -#else - set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[13], the_type); #endif /* ODBCVER */ + set_tuplefield_int4(&row->tuple[reserved_cols], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[reserved_cols + 1], the_type); QR_add_tuple(res, row); + ordinal++; } /* @@ -2096,7 +2111,7 @@ PGAPI_SpecialColumns( res = QR_Constructor(); SC_set_Result(stmt, res); - extend_bindings(stmt, 8); + extend_column_bindings(SC_get_ARD(stmt), 8); QR_set_num_fields(res, 8); QR_set_field_info(res, 0, "SCOPE", PG_TYPE_INT2, 2); @@ -2117,11 +2132,11 @@ PGAPI_SpecialColumns( set_tuplefield_int2(&row->tuple[0], SQL_SCOPE_SESSION); set_tuplefield_string(&row->tuple[1], "oid"); - set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, PG_TYPE_OID)); + set_tuplefield_int2(&row->tuple[2], pgtype_to_concise_type(stmt, PG_TYPE_OID)); set_tuplefield_string(&row->tuple[3], "OID"); - set_tuplefield_int4(&row->tuple[4], pgtype_precision(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[5], pgtype_length(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); - set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, PG_TYPE_OID, PG_STATIC)); + set_tuplefield_int4(&row->tuple[4], pgtype_column_size(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[5], pgtype_buffer_length(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); + set_tuplefield_int2(&row->tuple[6], pgtype_decimal_digits(stmt, PG_TYPE_OID, PG_STATIC)); set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO); QR_add_tuple(res, row); @@ -2137,11 +2152,11 @@ PGAPI_SpecialColumns( set_tuplefield_null(&row->tuple[0]); set_tuplefield_string(&row->tuple[1], "xmin"); - set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, the_type)); + set_tuplefield_int2(&row->tuple[2], pgtype_to_concise_type(stmt, the_type)); set_tuplefield_string(&row->tuple[3], pgtype_to_name(stmt, the_type)); - set_tuplefield_int4(&row->tuple[4], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&row->tuple[5], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC)); - set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, the_type, PG_STATIC)); + set_tuplefield_int4(&row->tuple[4], pgtype_column_size(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[5], pgtype_buffer_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int2(&row->tuple[6], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO); QR_add_tuple(res, row); @@ -2227,7 +2242,7 @@ PGAPI_Statistics( * a statement is actually executed, so we'll have to do this * ourselves. */ - extend_bindings(stmt, 13); + extend_column_bindings(SC_get_ARD(stmt), 13); /* set the field names */ QR_set_num_fields(res, 13); @@ -2671,7 +2686,7 @@ PGAPI_PrimaryKeys( * ourselves. */ result_cols = 6; - extend_bindings(stmt, result_cols); + extend_column_bindings(SC_get_ARD(stmt), result_cols); /* set the field names */ QR_set_num_fields(res, result_cols); @@ -3067,7 +3082,7 @@ PGAPI_ForeignKeys( * ourselves. */ result_cols = 14; - extend_bindings(stmt, result_cols); + extend_column_bindings(SC_get_ARD(stmt), result_cols); /* set the field names */ QR_set_num_fields(res, result_cols); @@ -3803,7 +3818,7 @@ PGAPI_Procedures( * results can be retrieved. */ stmt->status = STMT_FINISHED; - extend_bindings(stmt, 8); + extend_column_bindings(SC_get_ARD(stmt), 8); /* set up the current tuple pointer for SQLFetch */ stmt->currTuple = -1; stmt->rowset_start = -1; @@ -3891,7 +3906,7 @@ PGAPI_TablePrivileges( * ourselves. */ result_cols = 7; - extend_bindings(stmt, result_cols); + extend_column_bindings(SC_get_ARD(stmt), result_cols); /* set the field names */ stmt->manual_result = TRUE; @@ -3983,7 +3998,7 @@ PGAPI_TablePrivileges( char *grolist, *uid, *delm; snprintf(proc_query, sizeof(proc_query) - 1, "select grolist from pg_group where groname = '%s'", user); - if ((gres = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT))) + if (gres = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), gres != NULL) { grolist = QR_get_value_backend_row(gres, 0, 0); if (grolist && grolist[0] == '{') diff --git a/src/interfaces/odbc/info30.c b/src/interfaces/odbc/info30.c index 414110b893..c1c2c63d06 100644 --- a/src/interfaces/odbc/info30.c +++ b/src/interfaces/odbc/info30.c @@ -53,14 +53,13 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, | SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION | SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE - | SQL_CA1_POS_REFRESH); - if (ci->drivers.lie) - value |= (SQL_CA1_BULK_ADD + | SQL_CA1_POS_REFRESH | SQL_CA1_BULK_ADD | SQL_CA1_BULK_UPDATE_BY_BOOKMARK | SQL_CA1_BULK_DELETE_BY_BOOKMARK | SQL_CA1_BULK_FETCH_BY_BOOKMARK - - | SQL_CA1_LOCK_EXCLUSIVE + ); + if (ci->drivers.lie) + value |= (SQL_CA1_LOCK_EXCLUSIVE | SQL_CA1_LOCK_UNLOCK | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_POSITIONED_DELETE @@ -72,9 +71,10 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, value = 0; if (ci->updatable_cursors || ci->drivers.lie) value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY - | SQL_CA2_SENSITIVITY_ADDITIONS | SQL_CA2_SENSITIVITY_DELETIONS - | SQL_CA2_SENSITIVITY_UPDATES); + | SQL_CA2_SENSITIVITY_UPDATES + /* | SQL_CA2_SENSITIVITY_ADDITIONS */ + ); if (ci->drivers.lie) value |= (SQL_CA2_READ_ONLY_CONCURRENCY | SQL_CA2_LOCK_CONCURRENCY @@ -97,19 +97,21 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, len = 4; value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK - | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION; + | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION + | SQL_CA1_POS_REFRESH; if (ci->updatable_cursors) value |= (SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE - | SQL_CA1_POS_REFRESH); + ); break; case SQL_STATIC_CURSOR_ATTRIBUTES2: len = 4; - value = 0; + value = SQL_CA2_READ_ONLY_CONCURRENCY; if (ci->updatable_cursors) value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY - | SQL_CA2_SENSITIVITY_ADDITIONS + /* | SQL_CA2_SENSITIVITY_ADDITIONS | SQL_CA2_SENSITIVITY_DELETIONS - | SQL_CA2_SENSITIVITY_UPDATES); + | SQL_CA2_SENSITIVITY_UPDATES */ + ); break; case SQL_ODBC_INTERFACE_CONFORMANCE: diff --git a/src/interfaces/odbc/misc.c b/src/interfaces/odbc/misc.c index 443d1f47b3..a7bcaca8d9 100644 --- a/src/interfaces/odbc/misc.c +++ b/src/interfaces/odbc/misc.c @@ -115,6 +115,11 @@ mylog(char *fmt,...) va_end(args); } } +#else +void +MyLog(char *fmt,...) +{ +} #endif diff --git a/src/interfaces/odbc/misc.h b/src/interfaces/odbc/misc.h index b658ed7c91..cba7f2bfed 100644 --- a/src/interfaces/odbc/misc.h +++ b/src/interfaces/odbc/misc.h @@ -36,17 +36,18 @@ #define MYLOGDIR "/tmp" #else #define MYLOGDIR "c:" -#endif +#endif /* WIN32 */ extern void mylog(char *fmt,...); -#define inolog mylog /* for really temporary debug */ #else #ifndef WIN32 #define mylog(args...) /* GNU convention for variable arguments */ #else -#define mylog /* mylog */ -#endif -#endif +extern void MyLog(char *fmt,...); +#define mylog if (0) MyLog /* mylog */ +#endif /* WIN32 */ +#endif /* MY_LOG */ +#define inolog mylog /* for really temporary debug */ #ifdef Q_LOG #define QLOGFILE "psqlodbc_" @@ -64,6 +65,7 @@ extern void qlog(char *fmt,...); #define qlog /* qlog */ #endif #endif +#define inoqlog qlog #ifndef WIN32 #define DIRSEPARATOR "/" diff --git a/src/interfaces/odbc/multibyte.c b/src/interfaces/odbc/multibyte.c index 362916da2c..9c8c7dae6a 100644 --- a/src/interfaces/odbc/multibyte.c +++ b/src/interfaces/odbc/multibyte.c @@ -59,7 +59,7 @@ pg_CS CS_Table[] = { "OTHER", OTHER } }; -int +static int pg_ismb(int characterset_code) { int i=0,MB_CHARACTERSET[]={EUC_JP,EUC_CN,EUC_KR,EUC_TW,UTF8,MULE_INTERNAL,SJIS,BIG5,GBK,UHC,JOHAB}; @@ -228,8 +228,8 @@ pg_CS_stat(int stat,unsigned int character,int characterset_code) character > 0xa0) stat = 3; else if (stat == 3 || - (stat < 2 && - character > 0xa0)) + stat < 2 && + character > 0xa0) stat = 2; else if (stat == 2) stat = 1; diff --git a/src/interfaces/odbc/multibyte.h b/src/interfaces/odbc/multibyte.h index 8c9b4cce02..47a57b0ade 100644 --- a/src/interfaces/odbc/multibyte.h +++ b/src/interfaces/odbc/multibyte.h @@ -86,7 +86,4 @@ void encoded_str_constr(encoded_str *encstr, int ccsc, const char *str); #define make_encoded_str(encstr, conn, str) encoded_str_constr(encstr, conn->ccsc, str) extern int encoded_nextchar(encoded_str *encstr); extern int encoded_byte_check(encoded_str *encstr, int abspos); - -/* This doesn't seem to be called by anyone, bjm 2002-03-24 */ -extern int pg_ismb(int characterset_code); #define check_client_encoding(X) pg_CS_name(pg_CS_code(X)) diff --git a/src/interfaces/odbc/odbcapi.c b/src/interfaces/odbc/odbcapi.c index cbb31dad0a..d116d00006 100644 --- a/src/interfaces/odbc/odbcapi.c +++ b/src/interfaces/odbc/odbcapi.c @@ -204,8 +204,9 @@ SQLFetch(HSTMT StatementHandle) if (conn->driver_version >= 0x0300) { - SQLUSMALLINT *rowStatusArray = stmt->options.rowStatusArray; - SQLINTEGER *pcRow = stmt->options.rowsFetched; + IRDFields *irdopts = SC_get_IRD(stmt); + SQLUSMALLINT *rowStatusArray = irdopts->rowStatusArray; + SQLINTEGER *pcRow = irdopts->rowsFetched; mylog("[[%s]]", func); return PGAPI_ExtendedFetch(StatementHandle, SQL_FETCH_NEXT, 0, diff --git a/src/interfaces/odbc/odbcapi30.c b/src/interfaces/odbc/odbcapi30.c index c4efdd56e5..0e9b63a304 100644 --- a/src/interfaces/odbc/odbcapi30.c +++ b/src/interfaces/odbc/odbcapi30.c @@ -155,8 +155,9 @@ SQLFetchScroll(HSTMT StatementHandle, static char *func = "SQLFetchScroll"; StatementClass *stmt = (StatementClass *) StatementHandle; RETCODE ret; - SQLUSMALLINT *rowStatusArray = stmt->options.rowStatusArray; - SQLINTEGER *pcRow = stmt->options.rowsFetched; + IRDFields *irdopts = SC_get_IRD(stmt); + SQLUSMALLINT *rowStatusArray = irdopts->rowStatusArray; + SQLINTEGER *pcRow = irdopts->rowsFetched; mylog("[[%s]] %d,%d\n", func, FetchOrientation, FetchOffset); if (FetchOrientation == SQL_FETCH_BOOKMARK) @@ -208,8 +209,8 @@ SQLGetDescField(SQLHDESC DescriptorHandle, SQLINTEGER *StringLength) { mylog("[[SQLGetDescField]]\n"); - mylog("Error not implemented\n"); - return SQL_ERROR; + return PGAPI_GetDescField(DescriptorHandle, RecNumber, FieldIdentifier, + Value, BufferLength, StringLength); } /* new function */ @@ -233,7 +234,7 @@ SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, PTR DiagInfo, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength) { - mylog("[[SQLGetDiagField]]\n"); + mylog("[[SQLGetDiagField]] Handle=(%u,%x) Rec=%d Id=%d\n", HandleType, Handle, RecNumber, DiagIdentifier); return SQL_ERROR; } @@ -285,21 +286,9 @@ SQLGetConnectAttr(HDBC ConnectionHandle, SQLINTEGER Attribute, PTR Value, SQLINTEGER BufferLength, SQLINTEGER *StringLength) { - ConnectionClass *conn = (ConnectionClass *) ConnectionHandle; - mylog("[[SQLGetConnectAttr]] %d\n", Attribute); - switch (Attribute) - { - case SQL_ATTR_ASYNC_ENABLE: - case SQL_ATTR_AUTO_IPD: - case SQL_ATTR_CONNECTION_DEAD: - case SQL_ATTR_CONNECTION_TIMEOUT: - case SQL_ATTR_METADATA_ID: - conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - conn->errormsg = "Unsupported connection option (Set)"; - return SQL_ERROR; - } - return PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value); + return PGAPI_GetConnectAttr(ConnectionHandle, Attribute,Value, + BufferLength, StringLength); } /* SQLGetStmtOption -> SQLGetStmtAttr */ @@ -309,91 +298,10 @@ SQLGetStmtAttr(HSTMT StatementHandle, SQLINTEGER BufferLength, SQLINTEGER *StringLength) { static char *func = "SQLGetStmtAttr"; - StatementClass *stmt = (StatementClass *) StatementHandle; - RETCODE ret = SQL_SUCCESS; - int len = 0; mylog("[[%s]] Handle=%u %d\n", func, StatementHandle, Attribute); - switch (Attribute) - { - case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */ - Value = stmt->options.bookmark_ptr; - len = 4; - break; - case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ - Value = stmt->options.param_offset_ptr; - len = 4; - break; - case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ - *((SQLUINTEGER *) Value) = stmt->options.param_bind_type; - len = 4; - break; - case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */ - Value = stmt->options.param_operation_ptr; - len = 4; - break; - case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ - Value = stmt->options.param_status_ptr; - len = 4; - break; - case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ - Value = stmt->options.param_processed_ptr; - len = 4; - break; - case SQL_ATTR_PARAMSET_SIZE: /* 22 */ - *((SQLUINTEGER *) Value) = stmt->options.paramset_size; - len = 4; - break; - case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ - Value = stmt->options.row_offset_ptr; - len = 4; - break; - case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */ - Value = stmt->options.row_operation_ptr; - len = 4; - break; - case SQL_ATTR_ROW_STATUS_PTR: /* 25 */ - Value = stmt->options.rowStatusArray; - len = 4; - break; - case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */ - Value = stmt->options.rowsFetched; - len = 4; - break; - case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */ - *((SQLUINTEGER *) Value) = stmt->options.rowset_size; - len = 4; - break; - case SQL_ATTR_APP_ROW_DESC: /* 10010 */ - case SQL_ATTR_APP_PARAM_DESC: /* 10011 */ - case SQL_ATTR_IMP_ROW_DESC: /* 10012 */ - case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */ - len = 4; - *((HSTMT *) Value) = descHandleFromStatementHandle(StatementHandle, Attribute); - break; - case SQL_ATTR_AUTO_IPD: /* 10001 */ - /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */ - - case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */ - case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */ - case SQL_ATTR_ENABLE_AUTO_IPD: /* 15 */ - case SQL_ATTR_METADATA_ID: /* 10014 */ - - /* - * case SQL_ATTR_PREDICATE_PTR: case - * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR: - */ - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - stmt->errormsg = "Unsupported statement option (Get)"; - SC_log_error(func, "", stmt); - return SQL_ERROR; - default: - len = 4; - ret = PGAPI_GetStmtOption(StatementHandle, (UWORD) Attribute, Value); - } - if (ret == SQL_SUCCESS && StringLength) - *StringLength = len; - return ret; + return PGAPI_GetStmtAttr(StatementHandle, Attribute, Value, + BufferLength, StringLength); } /* SQLSetConnectOption -> SQLSetConnectAttr */ @@ -405,18 +313,8 @@ SQLSetConnectAttr(HDBC ConnectionHandle, ConnectionClass *conn = (ConnectionClass *) ConnectionHandle; mylog("[[SQLSetConnectAttr]] %d\n", Attribute); - switch (Attribute) - { - case SQL_ATTR_ASYNC_ENABLE: - case SQL_ATTR_AUTO_IPD: - case SQL_ATTR_CONNECTION_DEAD: - case SQL_ATTR_CONNECTION_TIMEOUT: - case SQL_ATTR_METADATA_ID: - conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - conn->errormsg = "Unsupported connection option (Set)"; - return SQL_ERROR; - } - return PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value); + return PGAPI_SetConnectAttr(ConnectionHandle, Attribute, Value, + StringLength); } /* new function */ @@ -497,70 +395,7 @@ SQLSetStmtAttr(HSTMT StatementHandle, StatementClass *stmt = (StatementClass *) StatementHandle; mylog("[[%s]] Handle=%u %d,%u\n", func, StatementHandle, Attribute, Value); - switch (Attribute) - { - case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */ - case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */ - - case SQL_ATTR_ENABLE_AUTO_IPD: /* 15 */ - - case SQL_ATTR_APP_ROW_DESC: /* 10010 */ - case SQL_ATTR_APP_PARAM_DESC: /* 10011 */ - case SQL_ATTR_AUTO_IPD: /* 10001 */ - /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */ - case SQL_ATTR_IMP_ROW_DESC: /* 10012 (read-only) */ - case SQL_ATTR_IMP_PARAM_DESC: /* 10013 (read-only) */ - case SQL_ATTR_METADATA_ID: /* 10014 */ - - /* - * case SQL_ATTR_PREDICATE_PTR: case - * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR: - */ - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - stmt->errormsg = "Unsupported statement option (Set)"; - SC_log_error(func, "", stmt); - return SQL_ERROR; - - case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */ - stmt->options.bookmark_ptr = Value; - break; - case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ - stmt->options.param_offset_ptr = (SQLUINTEGER *) Value; - break; - case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ - stmt->options.param_bind_type = (SQLUINTEGER) Value; - break; - case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */ - stmt->options.param_operation_ptr = Value; - break; - case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ - stmt->options.param_status_ptr = (SQLUSMALLINT *) Value; - break; - case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ - stmt->options.param_processed_ptr = (SQLUINTEGER *) Value; - break; - case SQL_ATTR_PARAMSET_SIZE: /* 22 */ - stmt->options.paramset_size = (SQLUINTEGER) Value; - break; - case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ - stmt->options.row_offset_ptr = (SQLUINTEGER *) Value; - break; - case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */ - stmt->options.row_operation_ptr = Value; - break; - case SQL_ATTR_ROW_STATUS_PTR: /* 25 */ - stmt->options.rowStatusArray = (SQLUSMALLINT *) Value; - break; - case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */ - stmt->options.rowsFetched = (SQLUINTEGER *) Value; - break; - case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */ - stmt->options.rowset_size = (SQLUINTEGER) Value; - break; - default: - return PGAPI_SetStmtOption(StatementHandle, (UWORD) Attribute, (UDWORD) Value); - } - return SQL_SUCCESS; + return PGAPI_SetStmtAttr(StatementHandle, Attribute, Value, StringLength); } #define SQL_FUNC_ESET(pfExists, uwAPI) \ @@ -630,8 +465,9 @@ PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists) if (ci->drivers.lie) SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNPRIVILEGES); /* 56 not implemented yet */ SQL_FUNC_ESET(pfExists, SQL_API_SQLDATASOURCES); /* 57 */ - SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBEPARAM); /* 58 */ - /* SQL_FUNC_ESET(pfExists, SQL_API_SQLEXTENDEDFETCH); 59 deprecated */ + if (ci->drivers.lie) + SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBEPARAM); /* 58 not properly implemented */ + SQL_FUNC_ESET(pfExists, SQL_API_SQLEXTENDEDFETCH); /* 59 deprecated ? */ /* * for (++i; i < SQL_API_SQLBINDPARAMETER; i++) @@ -678,8 +514,75 @@ PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists) SQL_FUNC_ESET(pfExists, SQL_API_SQLSETENVATTR); /* 1019 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSTMTATTR); /* 1020 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLFETCHSCROLL); /* 1021 */ - if (ci->drivers.lie) - SQL_FUNC_ESET(pfExists, SQL_API_SQLBULKOPERATIONS); /* 24 not implemented yet */ + if (ci->updatable_cursors) + SQL_FUNC_ESET(pfExists, SQL_API_SQLBULKOPERATIONS); /* 24 */ return SQL_SUCCESS; } + +RETCODE SQL_API +SQLBulkOperations(HSTMT hstmt, SQLSMALLINT operation) +{ + static char *func = "SQLBulkOperations"; + StatementClass *stmt = (StatementClass *) hstmt; + ARDFields *opts = SC_get_ARD(stmt); + RETCODE ret; + UInt4 offset, bind_size = opts->bind_size, *bmark; + int i, processed; + ConnectionClass *conn; + BOOL auto_commit_needed = FALSE; + + mylog("[[%s]] operation = %d\n", func, operation); + offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; + switch (operation) + { + case SQL_ADD: + ret = PGAPI_SetPos(hstmt, 0, operation, SQL_LOCK_NO_CHANGE); + break; + default: + if (SQL_FETCH_BY_BOOKMARK != operation) + { + conn = SC_get_conn(stmt); + if (auto_commit_needed = CC_is_in_autocommit(conn), auto_commit_needed) + PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, +SQL_AUTOCOMMIT_OFF); + } + if (bmark = (UInt4 *) opts->bookmark->buffer, !bmark) + { + stmt->errormsg = "bookmark isn't specified"; + return SQL_ERROR; + } + bmark += (offset >> 4); + for (i = 0, processed = 0; i < opts->rowset_size; i++) + { + if (!opts->row_operation_ptr || SQL_ROW_PROCEED == opts->row_operation_ptr[i]) + { + switch (operation) + { + case SQL_UPDATE_BY_BOOKMARK: + ret = SC_pos_update(stmt, (UWORD) i, *bmark); + break; + case SQL_DELETE_BY_BOOKMARK: + ret = SC_pos_delete(stmt, (UWORD) i, *bmark); + break; + case SQL_FETCH_BY_BOOKMARK: + ret = SC_pos_refresh(stmt, (UWORD) i, *bmark); + break; + } + processed++; + if (SQL_ERROR == ret) + break; + if (bind_size > 0) + bmark += (bind_size >> 2); + else + bmark++; + } + } + if (auto_commit_needed) + PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); + if (SC_get_IRD(stmt)->rowsFetched) + *SC_get_IRD(stmt)->rowsFetched = processed; + break; + } + return ret; +} diff --git a/src/interfaces/odbc/odbcapi30w.c b/src/interfaces/odbc/odbcapi30w.c index 35237202fa..0273f97b6b 100644 --- a/src/interfaces/odbc/odbcapi30w.c +++ b/src/interfaces/odbc/odbcapi30w.c @@ -5,7 +5,7 @@ * * Classes: n/a * - * API functions: SQLColAttributeW, SQLGetStmtW, SQLSetStmtW, + * API functions: SQLColAttributeW, SQLGetStmtAttrW, SQLSetStmtAttrW, SQLSetConnectAttrW, SQLGetConnectAttrW, SQLGetDescFieldW, SQLGetDescRecW, SQLGetDiagFieldW, SQLGetDiagRecW, @@ -75,6 +75,48 @@ RETCODE SQL_API SQLSetConnectAttrW(HDBC hdbc, return ret; } +/* new function */ +RETCODE SQL_API +SQLSetDescFieldW(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, + SQLSMALLINT FieldIdentifier, PTR Value, + SQLINTEGER BufferLength) +{ + RETCODE ret; + UInt4 vallen; + char *uval = NULL; + BOOL val_alloced = FALSE; + + mylog("[SQLSetDescFieldW]"); + if (BufferLength > 0) + { + uval = ucs2_to_utf8(Value, BufferLength / 2, &vallen); + val_alloced = TRUE; + } + else + { + uval = Value; + vallen = BufferLength; + } + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, FieldIdentifier, + uval, vallen); + if (val_alloced) + free(uval); + return ret; +} +RETCODE SQL_API +SQLGetDescFieldW(SQLHDESC hdesc, SQLSMALLINT iRecord, SQLSMALLINT iField, + PTR rgbValue, SQLINTEGER cbValueMax, + SQLINTEGER *pcbValue) +{ + RETCODE ret; + char *qstr = NULL, *mtxt = NULL; + + mylog("[SQLGetDescFieldW]"); + ret = PGAPI_GetDescField(hdesc, iRecord, iField, rgbValue, + cbValueMax, pcbValue); + return ret; +} + RETCODE SQL_API SQLGetDiagRecW(SWORD fHandleType, SQLHANDLE handle, SQLSMALLINT iRecord, @@ -131,7 +173,25 @@ RETCODE SQL_API SQLColAttributeW( RETCODE ret; mylog("[SQLColAttributeW]"); + switch (fDescType) + { + case SQL_DESC_BASE_COLUMN_NAME: + case SQL_DESC_BASE_TABLE_NAME: + case SQL_DESC_CATALOG_NAME: + case SQL_DESC_LABEL: + case SQL_DESC_LITERAL_PREFIX: + case SQL_DESC_LITERAL_SUFFIX: + case SQL_DESC_LOCAL_TYPE_NAME: + case SQL_DESC_NAME: + case SQL_DESC_SCHEMA_NAME: + case SQL_DESC_TABLE_NAME: + case SQL_DESC_TYPE_NAME: + case SQL_COLUMN_NAME: + break; + } + ret = PGAPI_ColAttributes(hstmt, icol, fDescType, rgbDesc, cbDescMax, pcbDesc, pfDesc); + return ret; } diff --git a/src/interfaces/odbc/options.c b/src/interfaces/odbc/options.c index ea11fae76b..d560ff4670 100644 --- a/src/interfaces/odbc/options.c +++ b/src/interfaces/odbc/options.c @@ -53,9 +53,9 @@ set_statement_option(ConnectionClass *conn, case SQL_BIND_TYPE: /* now support multi-column and multi-row binding */ if (conn) - conn->stmtOptions.bind_size = vParam; + conn->ardOptions.bind_size = vParam; if (stmt) - stmt->options.bind_size = vParam; + SC_get_ARD(stmt)->bind_size = vParam; break; case SQL_CONCURRENCY: @@ -173,7 +173,7 @@ set_statement_option(ConnectionClass *conn, */ if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0) - stmt->save_rowset_size = stmt->options.rowset_size; + stmt->save_rowset_size = SC_get_ARD(stmt)->rowset_size; if (vParam < 1) { @@ -182,9 +182,9 @@ set_statement_option(ConnectionClass *conn, } if (conn) - conn->stmtOptions.rowset_size = vParam; + conn->ardOptions.rowset_size = vParam; if (stmt) - stmt->options.rowset_size = vParam; + SC_get_ARD(stmt)->rowset_size = vParam; break; case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */ @@ -590,16 +590,16 @@ PGAPI_GetStmtOption( break; case SQL_BIND_TYPE: - *((SDWORD *) pvParam) = stmt->options.bind_size; + *((SDWORD *) pvParam) = SC_get_ARD(stmt)->bind_size; break; case SQL_CONCURRENCY: /* NOT REALLY SUPPORTED */ - mylog("GetStmtOption(): SQL_CONCURRENCY\n"); + mylog("GetStmtOption(): SQL_CONCURRENCY %d\n", stmt->options.scroll_concurrency); *((SDWORD *) pvParam) = stmt->options.scroll_concurrency; break; case SQL_CURSOR_TYPE: /* PARTIAL SUPPORT */ - mylog("GetStmtOption(): SQL_CURSOR_TYPE\n"); + mylog("GetStmtOption(): SQL_CURSOR_TYPE %d\n", stmt->options.cursor_type); *((SDWORD *) pvParam) = stmt->options.cursor_type; break; @@ -630,7 +630,7 @@ PGAPI_GetStmtOption( break; case SQL_ROWSET_SIZE: - *((SDWORD *) pvParam) = stmt->options.rowset_size; + *((SDWORD *) pvParam) = SC_get_ARD(stmt)->rowset_size; break; case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */ diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c index 8a7e6d7415..3549ff2ef7 100644 --- a/src/interfaces/odbc/parse.c +++ b/src/interfaces/odbc/parse.c @@ -236,19 +236,25 @@ void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k) { char *str; + Int2 reserved_cols; +#if (ODBCVER >= 0x0300) + reserved_cols = 18; +#else + reserved_cols = 12; +#endif /* ODBCVER */ if (fi->name[0] == '\0') strcpy(fi->name, QR_get_value_manual(col_info->result, k, 3)); - fi->type = atoi(QR_get_value_manual(col_info->result, k, 13)); - fi->precision = atoi(QR_get_value_manual(col_info->result, k, 6)); + fi->type = atoi(QR_get_value_manual(col_info->result, k, (Int2)(reserved_cols + 1))); + fi->column_size = atoi(QR_get_value_manual(col_info->result, k, 6)); fi->length = atoi(QR_get_value_manual(col_info->result, k, 7)); if (str = QR_get_value_manual(col_info->result, k, 8), str) - fi->scale = atoi(str); + fi->decimal_digits = atoi(str); else - fi->scale = -1; + fi->decimal_digits = -1; fi->nullable = atoi(QR_get_value_manual(col_info->result, k, 10)); - fi->display_size = atoi(QR_get_value_manual(col_info->result, k, 12)); + fi->display_size = atoi(QR_get_value_manual(col_info->result, k, reserved_cols)); } @@ -315,16 +321,17 @@ parse_statement(StatementClass *stmt) ConnectionClass *conn = stmt->hdbc; HSTMT hcol_stmt; StatementClass *col_stmt; + IRDFields *irdflds = SC_get_IRD(stmt); RETCODE result; BOOL updatable = TRUE; mylog("%s: entering...\n", func); ptr = stmt->statement; - fi = stmt->fi; + fi = irdflds->fi; ti = stmt->ti; - stmt->nfld = 0; + irdflds->nfields = 0; stmt->ntab = 0; stmt->from_pos = -1; stmt->where_pos = -1; @@ -371,7 +378,7 @@ parse_statement(StatementClass *stmt) mylog("FROM\n"); continue; } - } + } /* in_select && unquoted && blevel == 0 */ if (unquoted && blevel == 0) { if ((!stricmp(token, "where") || @@ -386,18 +393,18 @@ parse_statement(StatementClass *stmt) in_from = FALSE; in_where = TRUE; - if (!stricmp(token, "where")) + if (stmt->where_pos < 0) + stmt->where_pos = pptr - stmt->statement; + mylog("%s...\n", token); + if (stricmp(token, "where") && + stricmp(token, "order")) { - if (stmt->where_pos < 0) - stmt->where_pos = pptr - stmt->statement; - } - else if (stricmp(token, "order")) updatable = FALSE; - - mylog("WHERE...\n"); - break; + break; + } + continue; } - } + } /* unquoted && blevel == 0 */ if (in_select && (in_expr || in_func)) { /* just eat the expression */ @@ -435,7 +442,7 @@ parse_statement(StatementClass *stmt) } } continue; - } + } /* in_select && (in_expr || in_func) */ if (unquoted && !stricmp(token, "select")) { @@ -464,81 +471,81 @@ parse_statement(StatementClass *stmt) } mylog("done distinct\n"); in_distinct = FALSE; - } + } /* in_distinct */ if (!in_field) { if (!token[0]) continue; - if (!(stmt->nfld % FLD_INCR)) + if (!(irdflds->nfields % FLD_INCR)) { - mylog("reallocing at nfld=%d\n", stmt->nfld); - fi = (FIELD_INFO **) realloc(fi, (stmt->nfld + FLD_INCR) * sizeof(FIELD_INFO *)); + mylog("reallocing at nfld=%d\n", irdflds->nfields); + fi = (FIELD_INFO **) realloc(fi, (irdflds->nfields + FLD_INCR) * sizeof(FIELD_INFO *)); if (!fi) { stmt->parse_status = STMT_PARSE_FATAL; return FALSE; } - stmt->fi = fi; + irdflds->fi = fi; } - fi[stmt->nfld] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO)); - if (fi[stmt->nfld] == NULL) + fi[irdflds->nfields] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO)); + if (fi[irdflds->nfields] == NULL) { stmt->parse_status = STMT_PARSE_FATAL; return FALSE; } /* Initialize the field info */ - memset(fi[stmt->nfld], 0, sizeof(FIELD_INFO)); + memset(fi[irdflds->nfields], 0, sizeof(FIELD_INFO)); /* double quotes are for qualifiers */ if (dquote) - fi[stmt->nfld]->dquote = TRUE; + fi[irdflds->nfields]->dquote = TRUE; if (quote) { - fi[stmt->nfld]->quote = TRUE; - fi[stmt->nfld]->precision = strlen(token); + fi[irdflds->nfields]->quote = TRUE; + fi[irdflds->nfields]->column_size = strlen(token); } else if (numeric) { - mylog("**** got numeric: nfld = %d\n", stmt->nfld); - fi[stmt->nfld]->numeric = TRUE; + mylog("**** got numeric: nfld = %d\n", irdflds->nfields); + fi[irdflds->nfields]->numeric = TRUE; } else if (token[0] == '(') { /* expression */ mylog("got EXPRESSION\n"); - fi[stmt->nfld++]->expr = TRUE; + fi[irdflds->nfields++]->expr = TRUE; in_expr = TRUE; blevel = 1; continue; } else { - strcpy(fi[stmt->nfld]->name, token); - fi[stmt->nfld]->dot[0] = '\0'; + strcpy(fi[irdflds->nfields]->name, token); + fi[irdflds->nfields]->dot[0] = '\0'; } - mylog("got field='%s', dot='%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->dot); + mylog("got field='%s', dot='%s'\n", fi[irdflds->nfields]->name, fi[irdflds->nfields]->dot); if (delim == ',') mylog("comma (1)\n"); else in_field = TRUE; - stmt->nfld++; + irdflds->nfields++; continue; - } + } /* !in_field */ /* * We are in a field now */ if (in_dot) { - stmt->nfld--; - strcpy(fi[stmt->nfld]->dot, fi[stmt->nfld]->name); - strcpy(fi[stmt->nfld]->name, token); - stmt->nfld++; + irdflds->nfields--; + strcpy(fi[irdflds->nfields]->dot, fi[irdflds->nfields]->name); + strcpy(fi[irdflds->nfields]->name, token); + irdflds->nfields++; in_dot = FALSE; if (delim == ',') @@ -551,13 +558,13 @@ parse_statement(StatementClass *stmt) if (in_as) { - stmt->nfld--; - strcpy(fi[stmt->nfld]->alias, token); - mylog("alias for field '%s' is '%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->alias); + irdflds->nfields--; + strcpy(fi[irdflds->nfields]->alias, token); + mylog("alias for field '%s' is '%s'\n", fi[irdflds->nfields]->name, fi[irdflds->nfields]->alias); in_as = FALSE; in_field = FALSE; - stmt->nfld++; + irdflds->nfields++; if (delim == ',') mylog("comma(2)\n"); @@ -569,13 +576,13 @@ parse_statement(StatementClass *stmt) { in_func = TRUE; blevel = 1; - fi[stmt->nfld - 1]->func = TRUE; + fi[irdflds->nfields - 1]->func = TRUE; /* * name will have the function name -- maybe useful some * day */ - mylog("**** got function = '%s'\n", fi[stmt->nfld - 1]->name); + mylog("**** got function = '%s'\n", fi[irdflds->nfields - 1]->name); continue; } @@ -595,11 +602,11 @@ parse_statement(StatementClass *stmt) /* otherwise, it's probably an expression */ in_expr = TRUE; - fi[stmt->nfld - 1]->expr = TRUE; - fi[stmt->nfld - 1]->name[0] = '\0'; - fi[stmt->nfld - 1]->precision = 0; + fi[irdflds->nfields - 1]->expr = TRUE; + fi[irdflds->nfields - 1]->name[0] = '\0'; + fi[irdflds->nfields - 1]->column_size = 0; mylog("*** setting expression\n"); - } + } /* in_select end */ if (in_from) { @@ -607,6 +614,8 @@ parse_statement(StatementClass *stmt) { if (!token[0]) continue; + if (token[0] == ';') + break; if (!(stmt->ntab % TAB_INCR)) { @@ -658,12 +667,17 @@ parse_statement(StatementClass *stmt) continue; } - strcpy(ti[stmt->ntab - 1]->alias, token); - mylog("alias for table '%s' is '%s'\n", ti[stmt->ntab - 1]->name, ti[stmt->ntab - 1]->alias); - in_table = FALSE; - if (delim == ',') - mylog("more than 1 tables\n"); - } + if (token[0] == ';') + break; + if (stricmp(token, "as")) + { + strcpy(ti[stmt->ntab - 1]->alias, token); + mylog("alias for table '%s' is '%s'\n", ti[stmt->ntab - 1]->name, ti[stmt->ntab - 1]->alias); + in_table = FALSE; + if (delim == ',') + mylog("more than 1 tables\n"); + } + } /* in_from */ } /* @@ -673,7 +687,7 @@ parse_statement(StatementClass *stmt) parse = TRUE; /* Resolve field names with tables */ - for (i = 0; i < stmt->nfld; i++) + for (i = 0; i < (int) irdflds->nfields; i++) { if (fi[i]->func || fi[i]->expr || fi[i]->numeric) { @@ -687,16 +701,16 @@ parse_statement(StatementClass *stmt) fi[i]->ti = NULL; /* - * fi[i]->type = PG_TYPE_TEXT; fi[i]->precision = 0; the + * fi[i]->type = PG_TYPE_TEXT; fi[i]->column_size = 0; the * following may be better */ fi[i]->type = PG_TYPE_UNKNOWN; - if (fi[i]->precision == 0) + if (fi[i]->column_size == 0) { fi[i]->type = PG_TYPE_VARCHAR; - fi[i]->precision = 254; + fi[i]->column_size = 254; } - fi[i]->length = fi[i]->precision; + fi[i]->length = fi[i]->column_size; continue; } /* it's a dot, resolve to table or alias */ @@ -721,9 +735,9 @@ parse_statement(StatementClass *stmt) } mylog("--------------------------------------------\n"); - mylog("nfld=%d, ntab=%d\n", stmt->nfld, stmt->ntab); + mylog("nfld=%d, ntab=%d\n", irdflds->nfields, stmt->ntab); - for (i = 0; i < stmt->nfld; i++) + for (i = 0; i < (int) irdflds->nfields; i++) { mylog("Field %d: expr=%d, func=%d, quote=%d, dquote=%d, numeric=%d, name='%s', alias='%s', dot='%s'\n", i, fi[i]->expr, fi[i]->func, fi[i]->quote, fi[i]->dquote, fi[i]->numeric, fi[i]->name, fi[i]->alias, fi[i]->dot); if (fi[i]->ti) @@ -836,7 +850,7 @@ parse_statement(StatementClass *stmt) /* * Now resolve the fields to point to column info */ - for (i = 0; i < stmt->nfld;) + for (i = 0; i < (int) irdflds->nfields;) { fi[i]->updatable = updatable; /* Dont worry about functions or quotes */ @@ -875,8 +889,8 @@ parse_statement(StatementClass *stmt) increased_cols = total_cols - 1; /* Allocate some more field pointers if necessary */ - old_alloc = ((stmt->nfld - 1) / FLD_INCR + 1) * FLD_INCR; - new_size = stmt->nfld + increased_cols; + old_alloc = ((irdflds->nfields - 1) / FLD_INCR + 1) * FLD_INCR; + new_size = irdflds->nfields + increased_cols; mylog("k=%d, increased_cols=%d, old_alloc=%d, new_size=%d\n", k, increased_cols, old_alloc, new_size); @@ -891,14 +905,14 @@ parse_statement(StatementClass *stmt) stmt->parse_status = STMT_PARSE_FATAL; return FALSE; } - stmt->fi = fi; + irdflds->fi = fi; } /* * copy any other fields (if there are any) up past the * expansion */ - for (j = stmt->nfld - 1; j > i; j--) + for (j = irdflds->nfields - 1; j > i; j--) { mylog("copying field %d to %d\n", j, increased_cols + j); fi[increased_cols + j] = fi[j]; @@ -906,8 +920,8 @@ parse_statement(StatementClass *stmt) mylog("done copying fields\n"); /* Set the new number of fields */ - stmt->nfld += increased_cols; - mylog("stmt->nfld now at %d\n", stmt->nfld); + irdflds->nfields += increased_cols; + mylog("irdflds->nfields now at %d\n", irdflds->nfields); /* copy the new field info */ @@ -990,6 +1004,7 @@ parse_statement(StatementClass *stmt) else stmt->parse_status = STMT_PARSE_COMPLETE; + stmt->updatable = updatable; mylog("done parse_statement: parse=%d, parse_status=%d\n", parse, stmt->parse_status); return parse; } diff --git a/src/interfaces/odbc/pgapi30.c b/src/interfaces/odbc/pgapi30.c index 6e4c575e31..a67cc434fa 100644 --- a/src/interfaces/odbc/pgapi30.c +++ b/src/interfaces/odbc/pgapi30.c @@ -24,8 +24,10 @@ #include "environ.h" #include "connection.h" #include "statement.h" +#include "descriptor.h" #include "pgapifunc.h" +static HSTMT statementHandleFromDescHandle(HSTMT, SQLINTEGER *descType); /* SQLError -> SQLDiagRec */ RETCODE SQL_API PGAPI_GetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, @@ -54,6 +56,12 @@ PGAPI_GetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, NativeError, MessageText, BufferLength, TextLength, 0); break; + case SQL_HANDLE_DESC: + ret = PGAPI_StmtError(statementHandleFromDescHandle(Handle, NULL), + RecNumber, Sqlstate, NativeError, + MessageText, BufferLength, + TextLength, 0); + break; default: ret = SQL_ERROR; } @@ -131,21 +139,37 @@ PGAPI_GetConnectAttr(HDBC ConnectionHandle, SQLINTEGER Attribute, PTR Value, SQLINTEGER BufferLength, SQLINTEGER *StringLength) { + static const char *func = "PGAPI_GetConnectAttr"; ConnectionClass *conn = (ConnectionClass *) ConnectionHandle; + RETCODE ret = SQL_SUCCESS; + SQLINTEGER len = 4; mylog("PGAPI_GetConnectAttr %d\n", Attribute); switch (Attribute) { case SQL_ATTR_ASYNC_ENABLE: + *((SQLUINTEGER *) Value) = SQL_ASYNC_ENABLE_OFF; + break; case SQL_ATTR_AUTO_IPD: + *((SQLUINTEGER *) Value) = SQL_FALSE; + break; case SQL_ATTR_CONNECTION_DEAD: + *((SQLUINTEGER *) Value) = SQL_CD_FALSE; + break; case SQL_ATTR_CONNECTION_TIMEOUT: + *((SQLUINTEGER *) Value) = 0; + break; case SQL_ATTR_METADATA_ID: conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER; conn->errormsg = "Unsupported connect attribute (Get)"; + CC_log_error(func, "", conn); return SQL_ERROR; + default: + ret = PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value); } - return PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value); + if (StringLength) + *StringLength = len; + return ret; } static HSTMT @@ -168,113 +192,263 @@ static HSTMT statementHandleFromDescHandle(HSTMT DescHandle, SQLINTEGER *descType) { SQLUINTEGER res = (SQLUINTEGER) DescHandle % 4; - switch (res) + if (descType) { - case 0: *descType = SQL_ATTR_APP_ROW_DESC; /* 10010 */ - break; - case 1: *descType = SQL_ATTR_APP_PARAM_DESC; /* 10011 */ - break; - case 2: *descType = SQL_ATTR_IMP_ROW_DESC; /* 10012 */ - break; - case 3: *descType = SQL_ATTR_IMP_PARAM_DESC; /* 10013 */ - break; + switch (res) + { + case 0: *descType = SQL_ATTR_APP_ROW_DESC; /* 10010 */ + break; + case 1: *descType = SQL_ATTR_APP_PARAM_DESC; /* 10011 */ + break; + case 2: *descType = SQL_ATTR_IMP_ROW_DESC; /* 10012 */ + break; + case 3: *descType = SQL_ATTR_IMP_PARAM_DESC; /* 10013 */ + break; + } } return (HSTMT) ((SQLUINTEGER) DescHandle - res); } +static void column_bindings_set(ARDFields *opts, int cols, BOOL maxset) +{ + int i; + + if (cols == opts->allocated) + return; + if (cols > opts->allocated) + { + extend_column_bindings(opts, cols); + return; + } + if (maxset) return; + + for (i = opts->allocated; i > cols; i--) + reset_a_column_binding(opts, i); + opts->allocated = cols; + if (0 == cols) + { + free(opts->bindings); + opts->bindings = NULL; + } +} + static RETCODE SQL_API ARDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) { RETCODE ret = SQL_SUCCESS; PTR tptr; + ARDFields *opts = SC_get_ARD(stmt); switch (FieldIdentifier) { case SQL_DESC_ARRAY_SIZE: - stmt->options.rowset_size = (SQLUINTEGER) Value; + opts->rowset_size = (SQLUINTEGER) Value; break; case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.row_operation_ptr = Value; + opts->row_operation_ptr = Value; break; case SQL_DESC_BIND_OFFSET_PTR: - stmt->options.row_offset_ptr = Value; + opts->row_offset_ptr = Value; break; case SQL_DESC_BIND_TYPE: - stmt->options.bind_size = (SQLUINTEGER) Value; + opts->bind_size = (SQLUINTEGER) Value; break; + case SQL_DESC_TYPE: + column_bindings_set(opts, RecNumber, TRUE); + reset_a_column_binding(opts, RecNumber); + opts->bindings[RecNumber - 1].returntype = (Int4) Value; + break; + case SQL_DESC_DATETIME_INTERVAL_CODE: + column_bindings_set(opts, RecNumber, TRUE); + switch (opts->bindings[RecNumber - 1].returntype) + { + case SQL_DATETIME: + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_TYPE_TIMESTAMP: + switch ((Int4) Value) + { + case SQL_CODE_DATE: + opts->bindings[RecNumber - 1].returntype = SQL_C_TYPE_DATE; + break; + case SQL_CODE_TIME: + opts->bindings[RecNumber - 1].returntype = SQL_C_TYPE_TIME; + break; + case SQL_CODE_TIMESTAMP: + opts->bindings[RecNumber - 1].returntype = SQL_C_TYPE_TIMESTAMP; + break; + } + break; + } + break; + case SQL_DESC_CONCISE_TYPE: + column_bindings_set(opts, RecNumber, TRUE); + opts->bindings[RecNumber - 1].returntype = (Int4) Value; + break; case SQL_DESC_DATA_PTR: if (!RecNumber) - stmt->bookmark.buffer = Value; + opts->bookmark->buffer = Value; else - stmt->bindings[RecNumber - 1].buffer = Value; + { + column_bindings_set(opts, RecNumber, TRUE); + opts->bindings[RecNumber - 1].buffer = Value; + } break; case SQL_DESC_INDICATOR_PTR: if (!RecNumber) - tptr = stmt->bookmark.used; + tptr = opts->bookmark->used; else - tptr = stmt->bindings[RecNumber - 1].used; + { + column_bindings_set(opts, RecNumber, TRUE); + tptr = opts->bindings[RecNumber - 1].used; + } if (Value != tptr) { ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR"; } break; case SQL_DESC_OCTET_LENGTH_PTR: if (!RecNumber) - stmt->bookmark.used = Value; + opts->bookmark->used = Value; else - stmt->bindings[RecNumber - 1].used = Value; + { + column_bindings_set(opts, RecNumber, TRUE); + opts->bindings[RecNumber - 1].used = Value; + } break; + case SQL_DESC_COUNT: + column_bindings_set(opts, (SQLUINTEGER) Value, FALSE); + break; + case SQL_DESC_OCTET_LENGTH: + if (RecNumber) + { + column_bindings_set(opts, RecNumber, TRUE); + opts->bindings[RecNumber - 1].buflen = (Int4) Value; + } + break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_PRECISION: + case SQL_DESC_LENGTH: + case SQL_DESC_NUM_PREC_RADIX: + case SQL_DESC_PRECISION: + case SQL_DESC_SCALE: default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - stmt->errormsg = "not implemedted yet"; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } return ret; } +static void parameter_bindings_set(APDFields *opts, int params, BOOL maxset) +{ + int i; + + if (params == opts->allocated) + return; + if (params > opts->allocated) + { + extend_parameter_bindings(opts, params); + return; + } + if (maxset) return; + + for (i = opts->allocated; i > params; i--) + reset_a_parameter_binding(opts, i); + opts->allocated = params; + if (0 == params) + { + free(opts->parameters); + opts->parameters = NULL; + } +} + static RETCODE SQL_API APDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) { RETCODE ret = SQL_SUCCESS; + APDFields *opts = SC_get_APD(stmt); switch (FieldIdentifier) { case SQL_DESC_ARRAY_SIZE: - stmt->options.paramset_size = (SQLUINTEGER) Value; + opts->paramset_size = (SQLUINTEGER) Value; break; case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.param_operation_ptr = Value; + opts->param_operation_ptr = Value; break; case SQL_DESC_BIND_OFFSET_PTR: - stmt->options.param_offset_ptr = Value; + opts->param_offset_ptr = Value; break; case SQL_DESC_BIND_TYPE: - stmt->options.param_bind_type = (SQLUINTEGER) Value; + opts->param_bind_type = (SQLUINTEGER) Value; break; + case SQL_DESC_TYPE: + parameter_bindings_set(opts, RecNumber, TRUE); + reset_a_parameter_binding(opts, RecNumber); + opts->parameters[RecNumber - 1].CType = (Int4) Value; + break; + case SQL_DESC_DATETIME_INTERVAL_CODE: + parameter_bindings_set(opts, RecNumber, TRUE); + switch (opts->parameters[RecNumber - 1].CType) + { + case SQL_DATETIME: + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_TYPE_TIMESTAMP: + switch ((Int4) Value) + { + case SQL_CODE_DATE: + opts->parameters[RecNumber - 1].CType = SQL_C_TYPE_DATE; + break; + case SQL_CODE_TIME: + opts->parameters[RecNumber - 1].CType = SQL_C_TYPE_TIME; + break; + case SQL_CODE_TIMESTAMP: + opts->parameters[RecNumber - 1].CType = SQL_C_TYPE_TIMESTAMP; + break; + } + break; + } + break; + case SQL_DESC_CONCISE_TYPE: + parameter_bindings_set(opts, RecNumber, TRUE); + opts->parameters[RecNumber - 1].CType = (Int4) Value; + break; case SQL_DESC_DATA_PTR: - if (stmt->parameters_allocated < RecNumber) - PGAPI_BindParameter(stmt, RecNumber, 0, 0, 0, 0, 0, 0, 0, 0); - stmt->parameters[RecNumber - 1].buffer = Value; + parameter_bindings_set(opts, RecNumber, TRUE); + opts->parameters[RecNumber - 1].buffer = Value; break; case SQL_DESC_INDICATOR_PTR: - if (stmt->parameters_allocated < RecNumber || - Value != stmt->parameters[RecNumber - 1].used) + if (opts->allocated < RecNumber || + Value != opts->parameters[RecNumber - 1].used) { ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR"; } break; - case SQL_DESC_OCTET_LENGTH_PTR: - if (stmt->parameters_allocated < RecNumber) - PGAPI_BindParameter(stmt, RecNumber, 0, 0, 0, 0, 0, 0, 0, 0); - stmt->parameters[RecNumber - 1].used = Value; + case SQL_DESC_OCTET_LENGTH: + parameter_bindings_set(opts, RecNumber, TRUE); + opts->parameters[RecNumber - 1].buflen = (Int4) Value; break; + case SQL_DESC_OCTET_LENGTH_PTR: + parameter_bindings_set(opts, RecNumber, TRUE); + opts->parameters[RecNumber - 1].used = Value; + break; + case SQL_DESC_COUNT: + parameter_bindings_set(opts, (SQLUINTEGER) Value, FALSE); + break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_PRECISION: + case SQL_DESC_LENGTH: + case SQL_DESC_NUM_PREC_RADIX: + case SQL_DESC_PRECISION: + case SQL_DESC_SCALE: default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } return ret; } @@ -284,16 +458,51 @@ IRDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) { RETCODE ret = SQL_SUCCESS; + IRDFields *opts = SC_get_IRD(stmt); switch (FieldIdentifier) { case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.rowStatusArray = (SQLUSMALLINT *) Value; + opts->rowStatusArray = (SQLUSMALLINT *) Value; break; case SQL_DESC_ROWS_PROCESSED_PTR: - stmt->options.rowsFetched = (SQLUINTEGER *) Value; + opts->rowsFetched = (SQLUINTEGER *) Value; break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + case SQL_DESC_COUNT: /* read-only */ + case SQL_DESC_AUTO_UNIQUE_VALUE: /* read-only */ + case SQL_DESC_BASE_COLUMN_NAME: /* read-only */ + case SQL_DESC_BASE_TABLE_NAME: /* read-only */ + case SQL_DESC_CASE_SENSITIVE: /* read-only */ + case SQL_DESC_CATALOG_NAME: /* read-only */ + case SQL_DESC_CONCISE_TYPE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_CODE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_PRECISION: /* read-only */ + case SQL_DESC_DISPLAY_SIZE: /* read-only */ + case SQL_DESC_FIXED_PREC_SCALE: /* read-only */ + case SQL_DESC_LABEL: /* read-only */ + case SQL_DESC_LENGTH: /* read-only */ + case SQL_DESC_LITERAL_PREFIX: /* read-only */ + case SQL_DESC_LITERAL_SUFFIX: /* read-only */ + case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */ + case SQL_DESC_NAME: /* read-only */ + case SQL_DESC_NULLABLE: /* read-only */ + case SQL_DESC_NUM_PREC_RADIX: /* read-only */ + case SQL_DESC_OCTET_LENGTH: /* read-only */ + case SQL_DESC_PRECISION: /* read-only */ +#if (ODBCVER >= 0x0350) + case SQL_DESC_ROWVER: /* read-only */ +#endif /* ODBCVER */ + case SQL_DESC_SCALE: /* read-only */ + case SQL_DESC_SCHEMA_NAME: /* read-only */ + case SQL_DESC_SEARCHABLE: /* read-only */ + case SQL_DESC_TABLE_NAME: /* read-only */ + case SQL_DESC_TYPE: /* read-only */ + case SQL_DESC_TYPE_NAME: /* read-only */ + case SQL_DESC_UNNAMED: /* read-only */ + case SQL_DESC_UNSIGNED: /* read-only */ + case SQL_DESC_UPDATABLE: /* read-only */ default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } return ret; } @@ -303,20 +512,529 @@ IPDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) { RETCODE ret = SQL_SUCCESS; + IPDFields *ipdopts = SC_get_IPD(stmt); + APDFields *apdopts = SC_get_APD(stmt); + switch (FieldIdentifier) { case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.param_status_ptr = (SQLUSMALLINT *) Value; + ipdopts->param_status_ptr = (SQLUSMALLINT *) Value; break; case SQL_DESC_ROWS_PROCESSED_PTR: - stmt->options.param_processed_ptr = (SQLUINTEGER *) Value; + ipdopts->param_processed_ptr = (SQLUINTEGER *) Value; break; + case SQL_DESC_UNNAMED: /* only SQL_UNNAMED is allowed */ + if (SQL_UNNAMED != (SQLUINTEGER) Value) + { + ret = SQL_ERROR; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; + } + break; + case SQL_DESC_TYPE: + parameter_bindings_set(apdopts, RecNumber, TRUE); + apdopts->parameters[RecNumber - 1].SQLType = (Int4) Value; + break; + case SQL_DESC_DATETIME_INTERVAL_CODE: + parameter_bindings_set(apdopts, RecNumber, TRUE); + switch (apdopts->parameters[RecNumber - 1].SQLType) + { + case SQL_DATETIME: + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + switch ((Int4) Value) + { + case SQL_CODE_DATE: + apdopts->parameters[RecNumber - 1].SQLType = SQL_TYPE_DATE; + break; + case SQL_CODE_TIME: + apdopts->parameters[RecNumber - 1].SQLType = SQL_TYPE_TIME; + break; + case SQL_CODE_TIMESTAMP: + apdopts->parameters[RecNumber - 1].SQLType = SQL_TYPE_TIMESTAMP; + break; + } + break; + } + break; + case SQL_DESC_CONCISE_TYPE: + parameter_bindings_set(apdopts, RecNumber, TRUE); + apdopts->parameters[RecNumber - 1].SQLType = (Int4) Value; + break; + case SQL_DESC_COUNT: + parameter_bindings_set(apdopts, (SQLUINTEGER) Value, FALSE); + break; + case SQL_DESC_PARAMETER_TYPE: + apdopts->parameters[RecNumber - 1].paramType = (Int2) Value; + break; + case SQL_DESC_SCALE: + apdopts->parameters[RecNumber - 1].scale = (Int2) Value; + break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + case SQL_DESC_CASE_SENSITIVE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_PRECISION: + case SQL_DESC_FIXED_PREC_SCALE: /* read-only */ + case SQL_DESC_LENGTH: + case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */ + case SQL_DESC_NAME: + case SQL_DESC_NULLABLE: /* read-only */ + case SQL_DESC_NUM_PREC_RADIX: + case SQL_DESC_OCTET_LENGTH: + case SQL_DESC_PRECISION: +#if (ODBCVER >= 0x0350) + case SQL_DESC_ROWVER: /* read-only */ +#endif /* ODBCVER */ + case SQL_DESC_TYPE_NAME: /* read-only */ + case SQL_DESC_UNSIGNED: /* read-only */ default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } return ret; } + +static RETCODE SQL_API +ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber, + SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength, + SQLINTEGER *StringLength) +{ + RETCODE ret = SQL_SUCCESS; + SQLINTEGER len, ival; + PTR ptr = NULL; + const ARDFields *opts = SC_get_ARD(stmt); + + len = 4; + switch (FieldIdentifier) + { + case SQL_DESC_ARRAY_SIZE: + ival = opts->rowset_size; + break; + case SQL_DESC_ARRAY_STATUS_PTR: + ptr = opts->row_operation_ptr; + break; + case SQL_DESC_BIND_OFFSET_PTR: + ptr = opts->row_offset_ptr; + break; + case SQL_DESC_BIND_TYPE: + ival = opts->bind_size; + break; + case SQL_DESC_TYPE: + switch (opts->bindings[RecNumber - 1].returntype) + { + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_TYPE_TIMESTAMP: + ival = SQL_DATETIME; + break; + default: + ival = opts->bindings[RecNumber - 1].returntype; + } + break; + case SQL_DESC_DATETIME_INTERVAL_CODE: + switch (opts->bindings[RecNumber - 1].returntype) + { + case SQL_C_TYPE_DATE: + ival = SQL_CODE_DATE; + break; + case SQL_C_TYPE_TIME: + ival = SQL_CODE_TIME; + break; + case SQL_C_TYPE_TIMESTAMP: + ival = SQL_CODE_TIMESTAMP; + break; + default: + ival = 0; + break; + } + break; + case SQL_DESC_CONCISE_TYPE: + ival = opts->bindings[RecNumber - 1].returntype; + break; + case SQL_DESC_DATA_PTR: + if (!RecNumber) + ptr = opts->bookmark->buffer; + else + { + ptr = opts->bindings[RecNumber - 1].buffer; + } + break; + case SQL_DESC_INDICATOR_PTR: + if (!RecNumber) + ptr = opts->bookmark->used; + else + { + ptr = opts->bindings[RecNumber - 1].used; + } + break; + case SQL_DESC_OCTET_LENGTH_PTR: + if (!RecNumber) + ptr = opts->bookmark->used; + else + { + ptr = opts->bindings[RecNumber - 1].used; + } + break; + case SQL_DESC_COUNT: + ival = opts->allocated; + break; + case SQL_DESC_OCTET_LENGTH: + if (RecNumber) + { + ival = opts->bindings[RecNumber - 1].buflen; + } + break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + ival = SQL_DESC_ALLOC_AUTO; + break; + case SQL_DESC_DATETIME_INTERVAL_PRECISION: + case SQL_DESC_LENGTH: + case SQL_DESC_NUM_PREC_RADIX: + case SQL_DESC_PRECISION: + case SQL_DESC_SCALE: + default:ret = SQL_ERROR; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; + } + switch (BufferLength) + { + case 0: + case SQL_IS_INTEGER: + len = 4; + *((SQLINTEGER *) Value) = ival; + break; + case SQL_IS_UINTEGER: + len = 4; + *((UInt4 *) Value) = ival; + break; + case SQL_IS_SMALLINT: + len = 2; + *((SQLSMALLINT *) Value) = (SQLSMALLINT) ival; + break; + case SQL_IS_USMALLINT: + len = 2; + *((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival; + break; + case SQL_IS_POINTER: + len = 4; + *((void **) Value) = ptr; + break; + } + + if (StringLength) + *StringLength = len; + return ret; +} + +static RETCODE SQL_API +APDGetField(StatementClass *stmt, SQLSMALLINT RecNumber, + SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength, + SQLINTEGER *StringLength) +{ + RETCODE ret = SQL_SUCCESS; + SQLINTEGER ival = 0, len; + PTR ptr = NULL; + const APDFields *opts = SC_get_APD(stmt); + + len = 4; + switch (FieldIdentifier) + { + case SQL_DESC_ARRAY_SIZE: + ival = opts->paramset_size; + break; + case SQL_DESC_ARRAY_STATUS_PTR: + ptr = opts->param_operation_ptr; + break; + case SQL_DESC_BIND_OFFSET_PTR: + ptr = opts->param_offset_ptr; + break; + case SQL_DESC_BIND_TYPE: + ival = opts->param_bind_type; + break; + + case SQL_DESC_TYPE: + switch (opts->parameters[RecNumber - 1].CType) + { + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_TYPE_TIMESTAMP: + ival = SQL_DATETIME; + break; + default: + ival = opts->parameters[RecNumber - 1].CType; + } + break; + case SQL_DESC_DATETIME_INTERVAL_CODE: + switch (opts->parameters[RecNumber - 1].CType) + { + case SQL_C_TYPE_DATE: + ival = SQL_CODE_DATE; + break; + case SQL_C_TYPE_TIME: + ival = SQL_CODE_TIME; + break; + case SQL_C_TYPE_TIMESTAMP: + ival = SQL_CODE_TIMESTAMP; + break; + default: + ival = 0; + break; + } + break; + case SQL_DESC_CONCISE_TYPE: + ival = opts->parameters[RecNumber - 1].CType; + break; + case SQL_DESC_DATA_PTR: + ptr = opts->parameters[RecNumber - 1].buffer; + break; + case SQL_DESC_INDICATOR_PTR: + ptr = opts->parameters[RecNumber - 1].used; + break; + case SQL_DESC_OCTET_LENGTH: + ival = opts->parameters[RecNumber - 1].buflen; + break; + case SQL_DESC_OCTET_LENGTH_PTR: + ptr = opts->parameters[RecNumber - 1].used; + break; + case SQL_DESC_COUNT: + ival = opts->allocated; + break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + ival = SQL_DESC_ALLOC_AUTO; + break; + case SQL_DESC_DATETIME_INTERVAL_PRECISION: + case SQL_DESC_LENGTH: + case SQL_DESC_NUM_PREC_RADIX: + case SQL_DESC_PRECISION: + case SQL_DESC_SCALE: + default:ret = SQL_ERROR; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; + } + switch (BufferLength) + { + case 0: + case SQL_IS_INTEGER: + len = 4; + *((Int4 *) Value) = ival; + break; + case SQL_IS_UINTEGER: + len = 4; + *((UInt4 *) Value) = ival; + break; + case SQL_IS_SMALLINT: + len = 2; + *((SQLSMALLINT *) Value) = (SQLSMALLINT) ival; + break; + case SQL_IS_USMALLINT: + len = 2; + *((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival; + break; + case SQL_IS_POINTER: + len = 4; + *((void **) Value) = ptr; + break; + } + + if (StringLength) + *StringLength = len; + return ret; +} + +static RETCODE SQL_API +IRDGetField(StatementClass *stmt, SQLSMALLINT RecNumber, + SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength, + SQLINTEGER *StringLength) +{ + RETCODE ret = SQL_SUCCESS; + SQLINTEGER ival = 0, len; + PTR ptr = NULL; + const IRDFields *opts = SC_get_IRD(stmt); + + switch (FieldIdentifier) + { + case SQL_DESC_ARRAY_STATUS_PTR: + ptr = opts->rowStatusArray; + break; + case SQL_DESC_ROWS_PROCESSED_PTR: + ptr = opts->rowsFetched; + break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + case SQL_DESC_COUNT: /* read-only */ + case SQL_DESC_AUTO_UNIQUE_VALUE: /* read-only */ + case SQL_DESC_BASE_COLUMN_NAME: /* read-only */ + case SQL_DESC_BASE_TABLE_NAME: /* read-only */ + case SQL_DESC_CASE_SENSITIVE: /* read-only */ + case SQL_DESC_CATALOG_NAME: /* read-only */ + case SQL_DESC_CONCISE_TYPE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_CODE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_PRECISION: /* read-only */ + case SQL_DESC_DISPLAY_SIZE: /* read-only */ + case SQL_DESC_FIXED_PREC_SCALE: /* read-only */ + case SQL_DESC_LABEL: /* read-only */ + case SQL_DESC_LENGTH: /* read-only */ + case SQL_DESC_LITERAL_PREFIX: /* read-only */ + case SQL_DESC_LITERAL_SUFFIX: /* read-only */ + case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */ + case SQL_DESC_NAME: /* read-only */ + case SQL_DESC_NULLABLE: /* read-only */ + case SQL_DESC_NUM_PREC_RADIX: /* read-only */ + case SQL_DESC_OCTET_LENGTH: /* read-only */ + case SQL_DESC_PRECISION: /* read-only */ +#if (ODBCVER >= 0x0350) + case SQL_DESC_ROWVER: /* read-only */ +#endif /* ODBCVER */ + case SQL_DESC_SCALE: /* read-only */ + case SQL_DESC_SCHEMA_NAME: /* read-only */ + case SQL_DESC_SEARCHABLE: /* read-only */ + case SQL_DESC_TABLE_NAME: /* read-only */ + case SQL_DESC_TYPE: /* read-only */ + case SQL_DESC_TYPE_NAME: /* read-only */ + case SQL_DESC_UNNAMED: /* read-only */ + case SQL_DESC_UNSIGNED: /* read-only */ + case SQL_DESC_UPDATABLE: /* read-only */ + default:ret = SQL_ERROR; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; + } + switch (BufferLength) + { + case 0: + case SQL_IS_INTEGER: + len = 4; + *((Int4 *) Value) = ival; + break; + case SQL_IS_UINTEGER: + len = 4; + *((UInt4 *) Value) = ival; + break; + case SQL_IS_SMALLINT: + len = 2; + *((SQLSMALLINT *) Value) = (SQLSMALLINT) ival; + break; + case SQL_IS_USMALLINT: + len = 2; + *((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival; + break; + case SQL_IS_POINTER: + len = 4; + *((void **) Value) = ptr; + break; + } + + if (StringLength) + *StringLength = len; + return ret; +} + +static RETCODE SQL_API +IPDGetField(StatementClass *stmt, SQLSMALLINT RecNumber, + SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength, + SQLINTEGER *StringLength) +{ + RETCODE ret = SQL_SUCCESS; + SQLINTEGER ival = 0, len; + PTR ptr = NULL; + const IPDFields *ipdopts = SC_get_IPD(stmt); + const APDFields *apdopts = SC_get_APD(stmt); + + switch (FieldIdentifier) + { + case SQL_DESC_ARRAY_STATUS_PTR: + ptr = ipdopts->param_status_ptr; + break; + case SQL_DESC_ROWS_PROCESSED_PTR: + ptr = ipdopts->param_processed_ptr; + break; + case SQL_DESC_UNNAMED: /* only SQL_UNNAMED is allowed */ + ival = SQL_UNNAMED; + break; + case SQL_DESC_TYPE: + switch (apdopts->parameters[RecNumber - 1].SQLType) + { + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + ival = SQL_DATETIME; + break; + default: + ival = apdopts->parameters[RecNumber - 1].SQLType; + } + break; + case SQL_DESC_DATETIME_INTERVAL_CODE: + switch (apdopts->parameters[RecNumber - 1].SQLType) + { + case SQL_TYPE_DATE: + ival = SQL_CODE_DATE; + case SQL_TYPE_TIME: + ival = SQL_CODE_TIME; + break; + case SQL_TYPE_TIMESTAMP: + ival = SQL_CODE_TIMESTAMP; + break; + default: + ival = 0; + } + break; + case SQL_DESC_CONCISE_TYPE: + ival = apdopts->parameters[RecNumber - 1].SQLType; + break; + case SQL_DESC_COUNT: + ival = apdopts->allocated; + break; + case SQL_DESC_PARAMETER_TYPE: + ival = apdopts->parameters[RecNumber - 1].paramType; + break; + case SQL_DESC_SCALE: + ival = apdopts->parameters[RecNumber - 1].scale ; + break; + case SQL_DESC_ALLOC_TYPE: /* read-only */ + ival = SQL_DESC_ALLOC_AUTO; + break; + case SQL_DESC_CASE_SENSITIVE: /* read-only */ + case SQL_DESC_DATETIME_INTERVAL_PRECISION: + case SQL_DESC_FIXED_PREC_SCALE: /* read-only */ + case SQL_DESC_LENGTH: + case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */ + case SQL_DESC_NAME: + case SQL_DESC_NULLABLE: /* read-only */ + case SQL_DESC_NUM_PREC_RADIX: + case SQL_DESC_OCTET_LENGTH: + case SQL_DESC_PRECISION: +#if (ODBCVER >= 0x0350) + case SQL_DESC_ROWVER: /* read-only */ +#endif /* ODBCVER */ + case SQL_DESC_TYPE_NAME: /* read-only */ + case SQL_DESC_UNSIGNED: /* read-only */ + default:ret = SQL_ERROR; + stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; + } + switch (BufferLength) + { + case 0: + case SQL_IS_INTEGER: + len = 4; + *((Int4 *) Value) = ival; + break; + case SQL_IS_UINTEGER: + len = 4; + *((UInt4 *) Value) = ival; + break; + case SQL_IS_SMALLINT: + len = 2; + *((SQLSMALLINT *) Value) = (SQLSMALLINT) ival; + break; + case SQL_IS_USMALLINT: + len = 2; + *((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival; + break; + case SQL_IS_POINTER: + len = 4; + *((void **)Value) = ptr; + break; + } + + if (StringLength) + *StringLength = len; + return ret; +} + /* SQLGetStmtOption -> SQLGetStmtAttr */ RETCODE SQL_API PGAPI_GetStmtAttr(HSTMT StatementHandle, @@ -332,51 +1050,51 @@ PGAPI_GetStmtAttr(HSTMT StatementHandle, switch (Attribute) { case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */ - Value = stmt->options.bookmark_ptr; + *((void **) Value) = stmt->options.bookmark_ptr; len = 4; break; case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ - Value = stmt->options.param_offset_ptr; + *((SQLUINTEGER **) Value) = SC_get_APD(stmt)->param_offset_ptr; len = 4; break; case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ - *((SQLUINTEGER *) Value) = stmt->options.param_bind_type; + *((SQLUINTEGER *) Value) = SC_get_APD(stmt)->param_bind_type; len = 4; break; case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */ - Value = stmt->options.param_operation_ptr; + *((SQLUSMALLINT **) Value) = SC_get_APD(stmt)->param_operation_ptr; len = 4; break; case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ - Value = stmt->options.param_status_ptr; + *((SQLUSMALLINT **) Value) = SC_get_IPD(stmt)->param_status_ptr; len = 4; break; case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ - Value = stmt->options.param_processed_ptr; + *((SQLUINTEGER **) Value) = SC_get_IPD(stmt)->param_processed_ptr; len = 4; break; case SQL_ATTR_PARAMSET_SIZE: /* 22 */ - *((SQLUINTEGER *) Value) = stmt->options.paramset_size; + *((SQLUINTEGER *) Value) = SC_get_APD(stmt)->paramset_size; len = 4; break; case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ - Value = stmt->options.row_offset_ptr; + *((SQLUINTEGER **) Value) = SC_get_ARD(stmt)->row_offset_ptr; len = 4; break; case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */ - Value = stmt->options.row_operation_ptr; + *((SQLUSMALLINT **) Value) = SC_get_ARD(stmt)->row_operation_ptr; len = 4; break; case SQL_ATTR_ROW_STATUS_PTR: /* 25 */ - Value = stmt->options.rowStatusArray; + *((SQLUSMALLINT **) Value) = SC_get_IRD(stmt)->rowStatusArray; len = 4; break; case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */ - Value = stmt->options.rowsFetched; + *((SQLUINTEGER **) Value) = SC_get_IRD(stmt)->rowsFetched; len = 4; break; case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */ - *((SQLUINTEGER *) Value) = stmt->options.rowset_size; + *((SQLUINTEGER *) Value) = SC_get_ARD(stmt)->rowset_size; len = 4; break; case SQL_ATTR_APP_ROW_DESC: /* 10010 */ @@ -418,6 +1136,7 @@ PGAPI_SetConnectAttr(HDBC ConnectionHandle, SQLINTEGER StringLength) { ConnectionClass *conn = (ConnectionClass *) ConnectionHandle; + RETCODE ret = SQL_SUCCESS; mylog("PGAPI_SetConnectAttr %d\n", Attribute); switch (Attribute) @@ -430,8 +1149,54 @@ PGAPI_SetConnectAttr(HDBC ConnectionHandle, conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER; conn->errormsg = "Unsupported connect attribute (Set)"; return SQL_ERROR; + default: + ret = PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value); } - return PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value); + return ret; +} + +/* new function */ +RETCODE SQL_API +PGAPI_GetDescField(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, + PTR Value, SQLINTEGER BufferLength, + SQLINTEGER *StringLength) +{ + RETCODE ret = SQL_SUCCESS; + HSTMT hstmt; + SQLUINTEGER descType; + StatementClass *stmt; + static const char *func = "PGAPI_GetDescField"; + + mylog("%s h=%u rec=%d field=%d blen=%d\n", func, DescriptorHandle, RecNumber, FieldIdentifier, BufferLength); + hstmt = statementHandleFromDescHandle(DescriptorHandle, &descType); + mylog("stmt=%x type=%d\n", hstmt, descType); + stmt = (StatementClass *) hstmt; + switch (descType) + { + case SQL_ATTR_APP_ROW_DESC: + ret = ARDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength); + break; + case SQL_ATTR_APP_PARAM_DESC: + ret = APDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength); + break; + case SQL_ATTR_IMP_ROW_DESC: + ret = IRDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength); + break; + case SQL_ATTR_IMP_PARAM_DESC: + ret = IPDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength); + break; + default:ret = SQL_ERROR; + stmt->errornumber = STMT_INTERNAL_ERROR; + stmt->errormsg = "Error not implemented"; + } + if (ret == SQL_ERROR) + { + if (!stmt->errormsg && stmt->errornumber == STMT_INVALID_DESCRIPTOR_IDENTIFIER) + stmt->errormsg = "can't SQLGetDescField for this descriptor identifier"; + SC_log_error(func, "", stmt); + } + return ret; } /* new function */ @@ -446,7 +1211,7 @@ PGAPI_SetDescField(SQLHDESC DescriptorHandle, StatementClass *stmt; static const char *func = "PGAPI_SetDescField"; - mylog("%s h=%u rec=%d field=%d val=%x\n", func, DescriptorHandle, RecNumber, FieldIdentifier, Value); + mylog("%s h=%u rec=%d field=%d val=%x,%d\n", func, DescriptorHandle, RecNumber, FieldIdentifier, Value, BufferLength); hstmt = statementHandleFromDescHandle(DescriptorHandle, &descType); mylog("stmt=%x type=%d\n", hstmt, descType); stmt = (StatementClass *) hstmt; @@ -469,7 +1234,11 @@ PGAPI_SetDescField(SQLHDESC DescriptorHandle, stmt->errormsg = "Error not implemented"; } if (ret == SQL_ERROR) + { + if (!stmt->errormsg && stmt->errornumber == STMT_INVALID_DESCRIPTOR_IDENTIFIER) + stmt->errormsg = "can't SQLSetDescField for this descriptor identifier"; SC_log_error(func, "", stmt); + } return ret; } @@ -511,37 +1280,37 @@ PGAPI_SetStmtAttr(HSTMT StatementHandle, stmt->options.bookmark_ptr = Value; break; case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ - stmt->options.param_offset_ptr = (SQLUINTEGER *) Value; + SC_get_APD(stmt)->param_offset_ptr = (SQLUINTEGER *) Value; break; case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ - stmt->options.param_bind_type = (SQLUINTEGER) Value; + SC_get_APD(stmt)->param_bind_type = (SQLUINTEGER) Value; break; case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */ - stmt->options.param_operation_ptr = Value; + SC_get_APD(stmt)->param_operation_ptr = Value; break; case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ - stmt->options.param_status_ptr = (SQLUSMALLINT *) Value; + SC_get_IPD(stmt)->param_status_ptr = (SQLUSMALLINT *) Value; break; case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ - stmt->options.param_processed_ptr = (SQLUINTEGER *) Value; + SC_get_IPD(stmt)->param_processed_ptr = (SQLUINTEGER *) Value; break; case SQL_ATTR_PARAMSET_SIZE: /* 22 */ - stmt->options.paramset_size = (SQLUINTEGER) Value; + SC_get_APD(stmt)->paramset_size = (SQLUINTEGER) Value; break; case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ - stmt->options.row_offset_ptr = (SQLUINTEGER *) Value; + SC_get_ARD(stmt)->row_offset_ptr = (SQLUINTEGER *) Value; break; case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */ - stmt->options.row_operation_ptr = Value; + SC_get_ARD(stmt)->row_operation_ptr = Value; break; case SQL_ATTR_ROW_STATUS_PTR: /* 25 */ - stmt->options.rowStatusArray = (SQLUSMALLINT *) Value; + SC_get_IRD(stmt)->rowStatusArray = (SQLUSMALLINT *) Value; break; case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */ - stmt->options.rowsFetched = (SQLUINTEGER *) Value; + SC_get_IRD(stmt)->rowsFetched = (SQLUINTEGER *) Value; break; case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */ - stmt->options.rowset_size = (SQLUINTEGER) Value; + SC_get_ARD(stmt)->rowset_size = (SQLUINTEGER) Value; break; default: return PGAPI_SetStmtOption(StatementHandle, (UWORD) Attribute, (UDWORD) Value); diff --git a/src/interfaces/odbc/pgapifunc.h b/src/interfaces/odbc/pgapifunc.h index f7a72a28e4..a906a311de 100644 --- a/src/interfaces/odbc/pgapifunc.h +++ b/src/interfaces/odbc/pgapifunc.h @@ -280,5 +280,8 @@ RETCODE SQL_API PGAPI_SetStmtAttr(HSTMT StatementHandle, RETCODE SQL_API PGAPI_SetDescField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength); +RETCODE SQL_API PGAPI_GetDescField(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, + PTR Value, SQLINTEGER BufferLength, SQLINTEGER *StringLength); #endif /* ODBCVER */ #endif /* define_PG_API_FUNC_H__ */ diff --git a/src/interfaces/odbc/pgtypes.c b/src/interfaces/odbc/pgtypes.c index b40985866e..691fd92c53 100644 --- a/src/interfaces/odbc/pgtypes.c +++ b/src/interfaces/odbc/pgtypes.c @@ -26,7 +26,7 @@ -Int4 getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); +Int4 getCharColumnSize(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); /* * these are the types we support. all of the pgtype_ functions should @@ -230,13 +230,13 @@ sqltype_to_pgtype(StatementClass *stmt, SWORD fSqlType) * types that are unknown. All other pg routines in here return a suitable default. */ Int2 -pgtype_to_sqltype(StatementClass *stmt, Int4 type) +pgtype_to_concise_type(StatementClass *stmt, Int4 type) { ConnectionClass *conn = SC_get_conn(stmt); ConnInfo *ci = &(conn->connInfo); #if (ODBCVER >= 0x0300) EnvironmentClass *env = (EnvironmentClass *) (conn->henv); -#endif +#endif /* ODBCVER */ switch (type) { @@ -289,7 +289,7 @@ pgtype_to_sqltype(StatementClass *stmt, Int4 type) if (!conn->ms_jet) return SQL_BIGINT; #endif /* ODBCVER */ - return SQL_CHAR; + return SQL_VARCHAR; case PG_TYPE_NUMERIC: return SQL_NUMERIC; @@ -338,6 +338,40 @@ pgtype_to_sqltype(StatementClass *stmt, Int4 type) } } +Int2 +pgtype_to_sqldesctype(StatementClass *stmt, Int4 type) +{ + Int2 rettype; + + switch (rettype = pgtype_to_concise_type(stmt, type)) + { +#if (ODBCVER >= 0x0300) + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + return SQL_DATETIME; +#endif /* ODBCVER */ + } + return rettype; +} + +Int2 +pgtype_to_datetime_sub(StatementClass *stmt, Int4 type) +{ + switch (pgtype_to_concise_type(stmt, type)) + { +#if (ODBCVER >= 0x0300) + case SQL_TYPE_DATE: + return SQL_CODE_DATE; + case SQL_TYPE_TIME: + return SQL_CODE_TIME; + case SQL_TYPE_TIMESTAMP: + return SQL_CODE_TIMESTAMP; +#endif /* ODBCVER */ + } + return -1; +} + Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type) @@ -346,7 +380,7 @@ pgtype_to_ctype(StatementClass *stmt, Int4 type) ConnInfo *ci = &(conn->connInfo); #if (ODBCVER >= 0x0300) EnvironmentClass *env = (EnvironmentClass *) (conn->henv); -#endif +#endif /* ODBCVER */ switch (type) { @@ -484,13 +518,13 @@ pgtype_to_name(StatementClass *stmt, Int4 type) static Int2 -getNumericScale(StatementClass *stmt, Int4 type, int col) +getNumericDecimalDigits(StatementClass *stmt, Int4 type, int col) { Int4 atttypmod = -1; QResultClass *result; ColumnInfoClass *flds; - mylog("getNumericScale: type=%d, col=%d\n", type, col); + mylog("getNumericDecimalDigits: type=%d, col=%d\n", type, col); if (col < 0) return PG_NUMERIC_MAX_SCALE; @@ -525,13 +559,13 @@ getNumericScale(StatementClass *stmt, Int4 type, int col) static Int4 -getNumericPrecision(StatementClass *stmt, Int4 type, int col) +getNumericColumnSize(StatementClass *stmt, Int4 type, int col) { Int4 atttypmod = -1; QResultClass *result; ColumnInfoClass *flds; - mylog("getNumericPrecision: type=%d, col=%d\n", type, col); + mylog("getNumericColumnSize: type=%d, col=%d\n", type, col); if (col < 0) return PG_NUMERIC_MAX_PRECISION; @@ -566,15 +600,15 @@ getNumericPrecision(StatementClass *stmt, Int4 type, int col) Int4 -getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) +getCharColumnSize(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { - int p = -1, + int p = -1, attlen = -1, maxsize; QResultClass *result; ColumnInfoClass *flds; ConnInfo *ci = &(SC_get_conn(stmt)->connInfo); - mylog("getCharPrecision: type=%d, col=%d, unknown = %d\n", type, col, handle_unknown_size_as); + mylog("getCharColumnSize: type=%d, col=%d, unknown = %d\n", type, col, handle_unknown_size_as); /* Assign Maximum size based on parameters */ switch (type) @@ -607,7 +641,7 @@ getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si maxsize = TEXT_FIELD_SIZE; } /* - * Static Precision (i.e., the Maximum Precision of the datatype) This + * Static ColumnSize (i.e., the Maximum ColumnSize of the datatype) This * has nothing to do with a result set. */ if (col < 0) @@ -628,35 +662,37 @@ getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si return maxsize; } + p = QR_get_display_size(result, col); /* longest */ + attlen = QR_get_atttypmod(result, col); /* Size is unknown -- handle according to parameter */ - if (QR_get_atttypmod(result, col) > -1) - return QR_get_atttypmod(result, col); + if (attlen > p) /* maybe the length is known */ + return attlen; /* The type is really unknown */ if (type == PG_TYPE_BPCHAR || handle_unknown_size_as == UNKNOWNS_AS_LONGEST) { - p = QR_get_display_size(result, col); - mylog("getCharPrecision: LONGEST: p = %d\n", p); + mylog("getCharColumnSize: LONGEST: p = %d\n", p); if (p >= 0) return p; } + if (p > maxsize) + maxsize = p; if (handle_unknown_size_as == UNKNOWNS_AS_MAX) return maxsize; else /* handle_unknown_size_as == DONT_KNOW */ return -1; - } static Int2 -getTimestampScale(StatementClass *stmt, Int4 type, int col) +getTimestampDecimalDigits(StatementClass *stmt, Int4 type, int col) { ConnectionClass *conn = SC_get_conn(stmt); Int4 atttypmod; QResultClass *result; ColumnInfoClass *flds; - mylog("getTimestampScale: type=%d, col=%d\n", type, col); + mylog("getTimestampDecimalDigits: type=%d, col=%d\n", type, col); if (col < 0) return 0; @@ -685,12 +721,12 @@ getTimestampScale(StatementClass *stmt, Int4 type, int col) static Int4 -getTimestampPrecision(StatementClass *stmt, Int4 type, int col) +getTimestampColumnSize(StatementClass *stmt, Int4 type, int col) { Int4 fixed, scale; - mylog("getTimestampPrecision: type=%d, col=%d\n", type, col); + mylog("getTimestampColumnSize: type=%d, col=%d\n", type, col); switch (type) { @@ -710,11 +746,13 @@ getTimestampPrecision(StatementClass *stmt, Int4 type, int col) fixed = 19; break; } - scale = getTimestampScale(stmt, type, col); + scale = getTimestampDecimalDigits(stmt, type, col); return (scale > 0) ? fixed + 1 + scale : fixed; } /* + * This corresponds to "precision" in ODBC 2.x. + * * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_NUMERIC, SQLColumns will * override this length with the atttypmod length from pg_attribute . * @@ -722,7 +760,7 @@ getTimestampPrecision(StatementClass *stmt, Int4 type, int col) * This is used for functions SQLDescribeCol and SQLColAttributes. */ Int4 -pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) +pgtype_column_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { switch (type) { @@ -750,7 +788,7 @@ pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si return 19; /* signed */ case PG_TYPE_NUMERIC: - return getNumericPrecision(stmt, type, col); + return getNumericColumnSize(stmt, type, col); case PG_TYPE_FLOAT4: case PG_TYPE_MONEY: @@ -769,7 +807,7 @@ pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si return 22; case PG_TYPE_DATETIME: /* return 22; */ - return getTimestampPrecision(stmt, type, col); + return getTimestampColumnSize(stmt, type, col); case PG_TYPE_BOOL: { @@ -787,10 +825,26 @@ pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si return SQL_NO_TOTAL; /* Handle Character types and unknown types */ - return getCharPrecision(stmt, type, col, handle_unknown_size_as); + return getCharColumnSize(stmt, type, col, handle_unknown_size_as); } } +/* + * "precision in ODBC 3.x. + */ +Int4 +pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) +{ + switch (type) + { + case PG_TYPE_NUMERIC: + return getNumericColumnSize(stmt, type, col); + case PG_TYPE_DATETIME: + return getTimestampDecimalDigits(stmt, type, col); + } + return -1; +} + Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) @@ -813,7 +867,7 @@ pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown return 20; /* signed: 19 digits + sign */ case PG_TYPE_NUMERIC: - dsize = getNumericPrecision(stmt, type, col); + dsize = getNumericColumnSize(stmt, type, col); return dsize < 0 ? dsize : dsize + 2; case PG_TYPE_MONEY: @@ -827,20 +881,88 @@ pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown /* Character types use regular precision */ default: - return pgtype_precision(stmt, type, col, handle_unknown_size_as); + return pgtype_column_size(stmt, type, col, handle_unknown_size_as); } } /* - * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will - * override this length with the atttypmod length from pg_attribute + * The length in bytes of data transferred on an SQLGetData, SQLFetch, + * or SQLFetchScroll operation if SQL_C_DEFAULT is specified. */ Int4 -pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) +pgtype_buffer_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { ConnectionClass *conn = SC_get_conn(stmt); + switch (type) + { + case PG_TYPE_INT2: + return 2; /* sizeof(SQLSMALLINT) */ + + case PG_TYPE_OID: + case PG_TYPE_XID: + case PG_TYPE_INT4: + return 4; /* sizeof(SQLINTEGER) */ + + case PG_TYPE_INT8: + if (SQL_C_CHAR == pgtype_to_ctype(stmt, type)) + return 20; /* signed: 19 digits + sign */ + return 8; /* sizeof(SQLSBININT) */ + + case PG_TYPE_NUMERIC: + return getNumericColumnSize(stmt, type, col) + 2; + + case PG_TYPE_FLOAT4: + case PG_TYPE_MONEY: + return 4; /* sizeof(SQLREAL) */ + + case PG_TYPE_FLOAT8: + return 8; /* sizeof(SQLFLOAT) */ + + case PG_TYPE_DATE: + case PG_TYPE_TIME: + return 6; /* sizeof(DATE(TIME)_STRUCT) */ + + case PG_TYPE_ABSTIME: + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: + return 16; /* sizeof(TIMESTAMP_STRUCT) */ + + /* Character types use the default precision */ + case PG_TYPE_VARCHAR: + case PG_TYPE_BPCHAR: + { + int coef = 1; + Int4 prec = pgtype_column_size(stmt, type, col, handle_unknown_size_as), maxvarc; + if (conn->unicode) + return prec * 2; +#ifdef MULTIBYTE + /* after 7.2 */ + if (PG_VERSION_GE(conn, 7.2)) + coef = 3; + else +#endif /* MULTIBYTE */ + if ((conn->connInfo).lf_conversion) + /* CR -> CR/LF */ + coef = 2; + if (coef == 1) + return prec; + maxvarc = conn->connInfo.drivers.max_varchar_size; + if (prec <= maxvarc && prec * coef > maxvarc) + return maxvarc; + return coef * prec; + } + default: + return pgtype_column_size(stmt, type, col, handle_unknown_size_as); + } +} + +/* + */ +Int4 +pgtype_desclength(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) +{ switch (type) { case PG_TYPE_INT2: @@ -855,7 +977,7 @@ pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_ return 20; /* signed: 19 digits + sign */ case PG_TYPE_NUMERIC: - return getNumericPrecision(stmt, type, col) + 2; + return getNumericColumnSize(stmt, type, col) + 2; case PG_TYPE_FLOAT4: case PG_TYPE_MONEY: @@ -866,21 +988,33 @@ pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_ case PG_TYPE_DATE: case PG_TYPE_TIME: - return 6; /* sizeof(DATE(TIME)_STRUCT) */ - case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: - return 16; /* sizeof(TIMESTAMP_STRUCT) */ - - /* Character types (and NUMERIC) use the default precision */ case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: - { - int coef = 1; - Int4 prec = pgtype_precision(stmt, type, col, handle_unknown_size_as), maxvarc; + return pgtype_column_size(stmt, type, col, handle_unknown_size_as); + default: + return pgtype_column_size(stmt, type, col, handle_unknown_size_as); + } +} + +/* + * Transfer octet length. + */ +Int4 +pgtype_transfer_octet_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) +{ + ConnectionClass *conn = SC_get_conn(stmt); + + int coef = 1; + Int4 prec = pgtype_column_size(stmt, type, col, handle_unknown_size_as), maxvarc; + switch (type) + { + case PG_TYPE_VARCHAR: + case PG_TYPE_BPCHAR: if (conn->unicode) - return (prec + 1) * 2; + return prec * 2; #ifdef MULTIBYTE /* after 7.2 */ if (PG_VERSION_GE(conn, 7.2)) @@ -891,20 +1025,22 @@ pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_ /* CR -> CR/LF */ coef = 2; if (coef == 1) - return prec + 1; + return prec; maxvarc = conn->connInfo.drivers.max_varchar_size; if (prec <= maxvarc && prec * coef > maxvarc) return maxvarc; return coef * prec; - } - default: - return pgtype_precision(stmt, type, col, handle_unknown_size_as); + case PG_TYPE_BYTEA: + return prec; } + return -1; } - +/* + * corrsponds to "scale" in ODBC 2.x. + */ Int2 -pgtype_scale(StatementClass *stmt, Int4 type, int col) +pgtype_decimal_digits(StatementClass *stmt, Int4 type, int col) { switch (type) { @@ -927,16 +1063,30 @@ pgtype_scale(StatementClass *stmt, Int4 type, int col) return 0; case PG_TYPE_DATETIME: /* return 0; */ - return getTimestampScale(stmt, type, col); + return getTimestampDecimalDigits(stmt, type, col); case PG_TYPE_NUMERIC: - return getNumericScale(stmt, type, col); + return getNumericDecimalDigits(stmt, type, col); default: return -1; } } +/* + * "scale" in ODBC 3.x. + */ +Int2 +pgtype_scale(StatementClass *stmt, Int4 type, int col) +{ + switch (type) + { + case PG_TYPE_NUMERIC: + return getNumericDecimalDigits(stmt, type, col); + } + return -1; +} + Int2 pgtype_radix(StatementClass *stmt, Int4 type) diff --git a/src/interfaces/odbc/pgtypes.h b/src/interfaces/odbc/pgtypes.h index 651817921f..cef2362ddf 100644 --- a/src/interfaces/odbc/pgtypes.h +++ b/src/interfaces/odbc/pgtypes.h @@ -72,16 +72,22 @@ extern Int2 sqlTypes[]; Int4 sqltype_to_pgtype(StatementClass *stmt, Int2 fSqlType); -Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type); +Int2 pgtype_to_concise_type(StatementClass *stmt, Int4 type); +Int2 pgtype_to_sqldesctype(StatementClass *stmt, Int4 type); +Int2 pgtype_to_datetime_sub(StatementClass *stmt, Int4 type); Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type); char *pgtype_to_name(StatementClass *stmt, Int4 type); /* These functions can use static numbers or result sets(col parameter) */ -Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); +Int4 pgtype_column_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); /* corresponds to "precision" in ODBC 2.x */ +Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); /* "precsion in ODBC 3.x */ Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); -Int4 pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); +Int4 pgtype_buffer_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); +Int4 pgtype_desclength(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); +Int4 pgtype_transfer_octet_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as); -Int2 pgtype_scale(StatementClass *stmt, Int4 type, int col); +Int2 pgtype_decimal_digits(StatementClass *stmt, Int4 type, int col); /* corresponds to "scale" in ODBC 2.x */ +Int2 pgtype_scale(StatementClass *stmt, Int4 type, int col); /* ODBC 3.x " */ Int2 pgtype_radix(StatementClass *stmt, Int4 type); Int2 pgtype_nullable(StatementClass *stmt, Int4 type); Int2 pgtype_auto_increment(StatementClass *stmt, Int4 type); diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index 958a3b6b10..a0d4be134b 100644 --- a/src/interfaces/odbc/psqlodbc.h +++ b/src/interfaces/odbc/psqlodbc.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: psqlodbc.h,v 1.60 2002/03/14 05:42:03 inoue Exp $ + * $Id: psqlodbc.h,v 1.61 2002/03/28 08:08:06 inoue Exp $ * */ @@ -91,7 +91,11 @@ typedef UInt4 Oid; #ifdef WIN32 #if (ODBCVER >= 0x0300) +#ifdef UNICODE_SUPPORT +#define DRIVER_FILE_NAME "PSQLODBC30W.DLL" +#else #define DRIVER_FILE_NAME "PSQLODBC30.DLL" +#endif /* UNICODE_SUPPORT */ #else #define DRIVER_FILE_NAME "PSQLODBC.DLL" #endif /* ODBCVER */ @@ -167,6 +171,11 @@ typedef struct EnvironmentClass_ EnvironmentClass; typedef struct TupleNode_ TupleNode; typedef struct TupleField_ TupleField; typedef struct KeySet_ KeySet; +typedef struct Rollback_ Rollback; +typedef struct ARDFields_ ARDFields; +typedef struct APDFields_ APDFields; +typedef struct IRDFields_ IRDFields; +typedef struct IPDFields_ IPDFields; typedef struct col_info COL_INFO; typedef struct lo_arg LO_ARG; @@ -201,25 +210,12 @@ 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 */ int use_bookmarks; - UInt4 *rowsFetched; - UInt2 *rowStatusArray; - void *bookmark_ptr; - UInt2 *row_operation_ptr; - UInt4 *row_offset_ptr; - UInt4 paramset_size; - UInt4 param_bind_type; - UInt4 *param_processed_ptr; - UInt2 *param_status_ptr; - UInt2 *param_operation_ptr; - UInt4 *param_offset_ptr; + void *bookmark_ptr; } StatementOptions; /* Used to pass extra query info to send_query */ @@ -260,6 +256,7 @@ UInt4 ucs2strlen(const SQLWCHAR *ucs2str); char *ucs2_to_utf8(const SQLWCHAR *ucs2str, Int4 ilen, UInt4 *olen); UInt4 utf8_to_ucs2(const char * utf8str, Int4 ilen, SQLWCHAR *ucs2str, UInt4 buflen); #endif /* UNICODE_SUPPORT */ +/*#define _MEMORY_DEBUG_ */ #ifdef _MEMORY_DEBUG_ void *debug_alloc(size_t); void *debug_realloc(void *, size_t); diff --git a/src/interfaces/odbc/psqlodbc.rc b/src/interfaces/odbc/psqlodbc.rc index 698bd6c74f..7c99c038e2 100644 --- a/src/interfaces/odbc/psqlodbc.rc +++ b/src/interfaces/odbc/psqlodbc.rc @@ -138,7 +138,7 @@ BEGIN BS_NOTIFY | WS_TABSTOP,247,205,40,10 END -DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 176 +DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 196 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Advanced Options (DataSource)" FONT 10, "Terminal" @@ -155,23 +155,25 @@ BEGIN BS_AUTOCHECKBOX | WS_TABSTOP,45,43,92,10 CONTROL "True is -1",DS_TRUEISMINUS1,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,149,43,86,10 - GROUPBOX "Protocol",IDC_STATIC,43,59,180,25 + CONTROL "(Trial) Updatable cursors",DS_UPDATABLECURSORS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,45,58,112,10 + GROUPBOX "Protocol",IDC_STATIC,43,74,180,25 CONTROL "7.X,6.4+",DS_PG64,"Button",BS_AUTORADIOBUTTON | - WS_GROUP,53,69,47,10 + WS_GROUP,53,84,47,10 CONTROL "6.3",DS_PG63,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP, - 131,69,26,10 + 131,84,26,10 CONTROL "6.2",DS_PG62,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP, - 187,69,26,10 - GROUPBOX "OID Options",IDC_STATIC,43,89,180,25 + 187,84,26,10 + GROUPBOX "OID Options",IDC_STATIC,43,104,180,25 CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX | - WS_GROUP | WS_TABSTOP,53,100,59,10 + WS_GROUP | WS_TABSTOP,53,115,59,10 CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX | - WS_GROUP | WS_TABSTOP,161,100,55,10 - LTEXT "Connect &Settings:",IDC_STATIC,10,120,35,25 - EDITTEXT DS_CONNSETTINGS,50,120,200,20,ES_MULTILINE | + WS_GROUP | WS_TABSTOP,161,115,55,10 + LTEXT "Connect &Settings:",IDC_STATIC,10,135,35,25 + EDITTEXT DS_CONNSETTINGS,50,135,200,20,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - DEFPUSHBUTTON "OK",IDOK,71,150,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,146,150,50,14 + DEFPUSHBUTTON "OK",IDOK,71,165,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,146,165,50,14 END #else DLG_CONFIG DIALOG DISCARDABLE 65, 43, 292, 116 @@ -259,7 +261,7 @@ BEGIN BS_NOTIFY | WS_TABSTOP,233,224,40,10 END -DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 176 +DLG_OPTIONS_DS DIALOG DISCARDABLE 0, 0, 267, 186 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Advanced Options (DataSource)" FONT 8, "MS Sans Serif" @@ -273,26 +275,28 @@ BEGIN CONTROL "Disallow &Premature",DS_DISALLOWPREMATURE,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,130,25,85,10 CONTROL "LF <-> CR/LF convert",DS_LFCONVERSION,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,45,40,92,10 + BS_AUTOCHECKBOX | WS_TABSTOP,25,40,92,10 CONTROL "True is -1",DS_TRUEISMINUS1,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,149,40,86,10 - GROUPBOX "Protocol",IDC_STATIC,15,55,180,25 + BS_AUTOCHECKBOX | WS_TABSTOP,130,40,86,10 + CONTROL "(Trial) Updatable Cursors",DS_UPDATABLECURSORS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,25,55,102,10 + GROUPBOX "Protocol",IDC_STATIC,15,70,180,25 CONTROL "7.X,6.4+",DS_PG64,"Button",BS_AUTORADIOBUTTON | WS_GROUP,25, - 65,35,10 + 80,35,10 CONTROL "6.3",DS_PG63,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP, - 75,65,26,10 + 75,80,26,10 CONTROL "6.2",DS_PG62,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP, - 130,65,26,10 - GROUPBOX "OID Options",IDC_STATIC,15,86,180,25 + 130,80,26,10 + GROUPBOX "OID Options",IDC_STATIC,15,101,180,25 CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX | - WS_GROUP | WS_TABSTOP,25,96,59,10 + WS_GROUP | WS_TABSTOP,25,111,59,10 CONTROL "Fake &Index",DS_FAKEOIDINDEX,"Button",BS_AUTOCHECKBOX | - WS_GROUP | WS_TABSTOP,115,96,51,10 - RTEXT "Connect &Settings:",IDC_STATIC,10,120,35,25 - EDITTEXT DS_CONNSETTINGS,50,120,200,20,ES_MULTILINE | + WS_GROUP | WS_TABSTOP,115,111,51,10 + RTEXT "Connect &Settings:",IDC_STATIC,10,135,35,25 + EDITTEXT DS_CONNSETTINGS,50,135,200,20,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - DEFPUSHBUTTON "OK",IDOK,71,150,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,146,150,50,14 + DEFPUSHBUTTON "OK",IDOK,71,165,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,146,165,50,14 END #endif diff --git a/src/interfaces/odbc/psqlodbc30w.reg b/src/interfaces/odbc/psqlodbc30w.reg new file mode 100644 index 0000000000..81fd93e15c --- /dev/null +++ b/src/interfaces/odbc/psqlodbc30w.reg @@ -0,0 +1,16 @@ +REGEDIT4 + +[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI] + +[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers] +"PostgreSQL30W"="Installed" + +[HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\PostgreSQL30W] +"APILevel"="1" +"ConnectFunctions"="YYN" +"Driver"="PSQLODBC30W.DLL" +"DriverODBCVer"="03.00" +"FileUsage"="0" +"Setup"="PSQLODBC30W.DLL" +"SQLLevel"="1" +"UsageCount"=dword:00000001 diff --git a/src/interfaces/odbc/psqlodbc_api30.def b/src/interfaces/odbc/psqlodbc_api30.def index a2031c7aba..ddc78062e6 100755 --- a/src/interfaces/odbc/psqlodbc_api30.def +++ b/src/interfaces/odbc/psqlodbc_api30.def @@ -74,9 +74,9 @@ SQLSetDescField @96 SQLSetDescRec @97 SQLSetEnvAttr @98 SQLSetStmtAttr @99 +SQLBulkOperations @100 SQLDummyOrdinal @199 dconn_FDriverConnectProc @200 DllMain @201 ConfigDSN @202 - diff --git a/src/interfaces/odbc/psqlodbc_api30w.def b/src/interfaces/odbc/psqlodbc_api30w.def index ef7cdfdf33..86dedae64c 100644 --- a/src/interfaces/odbc/psqlodbc_api30w.def +++ b/src/interfaces/odbc/psqlodbc_api30w.def @@ -1,4 +1,4 @@ -LIBRARY psqlodbc30 +LIBRARY psqlodbc30w EXPORTS SQLAllocConnect @1 SQLAllocEnv @2 @@ -71,6 +71,7 @@ SQLSetDescField @96 SQLSetDescRec @97 SQLSetEnvAttr @98 SQLSetStmtAttr @99 +SQLBulkOperations @100 SQLDummyOrdinal @199 dconn_FDriverConnectProc @200 diff --git a/src/interfaces/odbc/qresult.c b/src/interfaces/odbc/qresult.c index 89ce0396ec..923448abeb 100644 --- a/src/interfaces/odbc/qresult.c +++ b/src/interfaces/odbc/qresult.c @@ -123,6 +123,8 @@ QR_Constructor() rv->rowset_size = 1; rv->haskeyset = 0; rv->keyset = NULL; + rv->rb_count = 0; + rv->rollback = NULL; } mylog("exit QR_Constructor\n"); @@ -228,6 +230,12 @@ QR_free_memory(QResultClass *self) free(self->keyset); self->keyset = NULL; } + if (self->rollback) + { + free(self->rollback); + self->rb_count = 0; + self->rollback = NULL; + } self->fcount = 0; @@ -280,6 +288,8 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) { self->status = PGRES_FIELDS_OK; self->num_fields = CI_get_num_fields(self->fields); + if (self->haskeyset) + self->num_fields -= 2; } else { @@ -302,15 +312,18 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) /* allocate memory for the tuple cache */ mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size); self->count_allocated = 0; - self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size); + if (self->num_fields > 0) + { + 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."); + return FALSE; + } + } if (self->haskeyset) self->keyset = (KeySet *) calloc(sizeof(KeySet), tuple_size); - if (!self->backend_tuples) - { - self->status = PGRES_FATAL_ERROR; - QR_set_message(self, "Could not get memory for tuple cache."); - return FALSE; - } self->count_allocated = tuple_size; self->inTuples = TRUE; @@ -415,6 +428,7 @@ QR_next_tuple(QResultClass *self) char fetch[128]; QueryInfo qi; ConnInfo *ci = NULL; + BOOL set_no_trans; if (fetch_count < fcount) { @@ -484,12 +498,16 @@ QR_next_tuple(QResultClass *self) if (!self->backend_tuples || self->cache_size > self->count_allocated) { self->count_allocated = 0; - self->backend_tuples = (TupleField *) realloc(self->backend_tuples, self->num_fields * sizeof(TupleField) * self->cache_size); - if (!self->backend_tuples) + if (self->num_fields > 0) { - self->status = PGRES_FATAL_ERROR; - QR_set_message(self, "Out of memory while reading tuples."); - return FALSE; + 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; + } } if (self->haskeyset) self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * self->cache_size); @@ -555,13 +573,16 @@ QR_next_tuple(QResultClass *self) mylog("REALLOC: old_count = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size); tuple_size *= 2; - self->backend_tuples = (TupleField *) realloc(self->backend_tuples, - tuple_size * self->num_fields * sizeof(TupleField)); - if (!self->backend_tuples) + if (self->num_fields > 0) { - self->status = PGRES_FATAL_ERROR; - QR_set_message(self, "Out of memory while reading tuples."); - return FALSE; + self->backend_tuples = (TupleField *) realloc(self->backend_tuples, + tuple_size * self->num_fields * sizeof(TupleField)); + if (!self->backend_tuples) + { + self->status = PGRES_FATAL_ERROR; + QR_set_message(self, "Out of memory while reading tuples."); + return FALSE; + } } if (self->haskeyset) self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * tuple_size); @@ -606,8 +627,10 @@ QR_next_tuple(QResultClass *self) QR_set_message(self, msgbuffer); self->status = PGRES_FATAL_ERROR; + set_no_trans = FALSE; if (!strncmp(msgbuffer, "FATAL", 5)) - CC_set_no_trans(self->conn); + set_no_trans = TRUE; + CC_on_abort(self->conn, set_no_trans); qlog("ERROR from backend in next_tuple: '%s'\n", msgbuffer); @@ -626,7 +649,7 @@ QR_next_tuple(QResultClass *self) qlog("QR_next_tuple: Unexpected result from backend: id = '%c' (%d)\n", id, id); QR_set_message(self, "Unexpected result from backend. It probably crashed"); self->status = PGRES_FATAL_ERROR; - CC_set_no_trans(self->conn); + CC_on_abort(self->conn, TRUE); return FALSE; } } @@ -647,16 +670,21 @@ QR_read_tuple(QResultClass *self, char binary) Int2 bitcnt; Int4 len; char *buffer; - int num_fields = self->num_fields; /* speed up access */ + int ci_num_fields = QR_NumResultCols(self); /* speed up access */ + int num_fields = self->num_fields; /* speed up access */ SocketClass *sock = CC_get_socket(self->conn); ColumnInfoClass *flds; + int effective_cols; + char tidoidbuf[32]; /* set the current row to read the fields into */ + effective_cols = ci_num_fields; this_tuplefield = self->backend_tuples + (self->fcount * num_fields); if (self->haskeyset) { this_keyset = self->keyset + self->fcount; this_keyset->status = 0; + effective_cols -= 2; } bitmaplen = (Int2) num_fields / BYTELEN; @@ -672,8 +700,9 @@ QR_read_tuple(QResultClass *self, char binary) bitmap_pos = 0; bitcnt = 0; bmp = bitmap[bitmap_pos]; + flds = self->fields; - for (field_lf = 0; field_lf < num_fields; field_lf++) + for (field_lf = 0; field_lf < ci_num_fields; field_lf++) { /* Check if the current field is NULL */ if (!(bmp & 0200)) @@ -692,14 +721,27 @@ QR_read_tuple(QResultClass *self, char binary) if (!binary) len -= VARHDRSZ; - buffer = (char *) malloc(len + 1); + if (field_lf >= effective_cols) + buffer = tidoidbuf; + else + buffer = (char *) malloc(len + 1); SOCK_get_n_char(sock, buffer, len); buffer[len] = '\0'; mylog("qresult: len=%d, buffer='%s'\n", len, buffer); - this_tuplefield[field_lf].len = len; - this_tuplefield[field_lf].value = buffer; + if (field_lf >= effective_cols) + { + if (field_lf == effective_cols) + sscanf(buffer, "(%lu,%hu)", + &this_keyset->blocknum, &this_keyset->offset); + else + this_keyset->oid = strtoul(buffer, NULL, 10); + } + else + { + this_tuplefield[field_lf].len = len; + this_tuplefield[field_lf].value = buffer; /* * This can be used to set the longest length of the column @@ -710,9 +752,9 @@ QR_read_tuple(QResultClass *self, char binary) * row! */ - flds = self->fields; - if (flds && flds->display_size && flds->display_size[field_lf] < len) - flds->display_size[field_lf] = len; + if (flds && flds->display_size && flds->display_size[field_lf] < len) + flds->display_size[field_lf] = len; + } } /* @@ -728,15 +770,6 @@ QR_read_tuple(QResultClass *self, char binary) else bmp <<= 1; } - if (this_keyset) - { - if (this_tuplefield[num_fields - 2].value) - sscanf(this_tuplefield[num_fields - 2].value, "(%lu,%hu)", - &this_keyset->blocknum, &this_keyset->offset); - if (this_tuplefield[num_fields - 1].value) - sscanf(this_tuplefield[num_fields - 1].value, "%lu", - &this_keyset->oid); - } self->currTuple++; return TRUE; } diff --git a/src/interfaces/odbc/qresult.h b/src/interfaces/odbc/qresult.h index dbb6f46901..a7291c43a6 100644 --- a/src/interfaces/odbc/qresult.h +++ b/src/interfaces/odbc/qresult.h @@ -73,8 +73,9 @@ struct QResultClass_ * progress? */ char aborted; /* was aborted? */ char haskeyset; /* this result contains keyset ? */ - KeySet *keyset; - + KeySet *keyset; + UInt4 rb_count; /* count of rollback info */ + Rollback *rollback; }; #define QR_get_fields(self) (self->fields) diff --git a/src/interfaces/odbc/resource.h b/src/interfaces/odbc/resource.h index 4cd1639887..ac67ad5a5e 100644 --- a/src/interfaces/odbc/resource.h +++ b/src/interfaces/odbc/resource.h @@ -54,6 +54,7 @@ #define DS_DISALLOWPREMATURE 1061 #define DS_LFCONVERSION 1062 #define DS_TRUEISMINUS1 1063 +#define DS_UPDATABLECURSORS 1064 /* Next default values for new objects */ /* */ @@ -61,7 +62,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1062 +#define _APS_NEXT_CONTROL_VALUE 1065 #define _APS_NEXT_SYMED_VALUE 101 #endif /* */ diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c index af7c8b30f8..6d20bbaad5 100644 --- a/src/interfaces/odbc/results.c +++ b/src/interfaces/odbc/results.c @@ -138,7 +138,7 @@ PGAPI_NumResultCols( if (stmt->parse_status != STMT_PARSE_FATAL) { parse_ok = TRUE; - *pccol = stmt->nfld; + *pccol = SC_get_IRD(stmt)->nfields; mylog("PARSE: PGAPI_NumResultCols: *pccol = %d\n", *pccol); } } @@ -189,11 +189,12 @@ PGAPI_DescribeCol( /* gets all the information about a specific column */ StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; + IRDFields *irdflds; QResultClass *res; char *col_name = NULL; Int4 fieldtype = 0; - int precision = 0, - scale = 0; + int column_size = 0, + decimal_digits = 0; ConnInfo *ci; char parse_ok; char buf[255]; @@ -213,6 +214,7 @@ PGAPI_DescribeCol( SC_clear_error(stmt); + irdflds = SC_get_IRD(stmt); #if (ODBCVER >= 0x0300) if (0 == icol) /* bookmark column */ { @@ -249,11 +251,11 @@ PGAPI_DescribeCol( parse_statement(stmt); } - mylog("PARSE: DescribeCol: icol=%d, stmt=%u, stmt->nfld=%d, stmt->fi=%u\n", icol, stmt, stmt->nfld, stmt->fi); + mylog("PARSE: DescribeCol: icol=%d, stmt=%u, stmt->nfld=%d, stmt->fi=%u\n", icol, stmt, irdflds->nfields, irdflds->fi); - if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol]) + if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi && irdflds->fi[icol]) { - if (icol >= stmt->nfld) + if (icol >= irdflds->nfields) { stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR; stmt->errormsg = "Invalid column number in DescribeCol."; @@ -262,15 +264,15 @@ PGAPI_DescribeCol( } mylog("DescribeCol: getting info for icol=%d\n", icol); - fieldtype = stmt->fi[icol]->type; - if (stmt->fi[icol]->alias[0]) - col_name = stmt->fi[icol]->alias; + fieldtype = irdflds->fi[icol]->type; + if (irdflds->fi[icol]->alias[0]) + col_name = irdflds->fi[icol]->alias; else - col_name = stmt->fi[icol]->name; - precision = stmt->fi[icol]->precision; - scale = stmt->fi[icol]->scale; + col_name = irdflds->fi[icol]->name; + column_size = irdflds->fi[icol]->column_size; + decimal_digits = irdflds->fi[icol]->decimal_digits; - mylog("PARSE: fieldtype=%d, col_name='%s', precision=%d\n", fieldtype, col_name, precision); + mylog("PARSE: fieldtype=%d, col_name='%s', column_size=%d\n", fieldtype, col_name, column_size); if (fieldtype > 0) parse_ok = TRUE; } @@ -310,13 +312,13 @@ PGAPI_DescribeCol( fieldtype = QR_get_field_type(res, icol); /* atoi(ci->unknown_sizes) */ - precision = pgtype_precision(stmt, fieldtype, icol, ci->drivers.unknown_sizes); - scale = pgtype_scale(stmt, fieldtype, icol); + column_size = pgtype_column_size(stmt, fieldtype, icol, ci->drivers.unknown_sizes); + decimal_digits = pgtype_decimal_digits(stmt, fieldtype, icol); } mylog("describeCol: col %d fieldname = '%s'\n", icol, col_name); mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype); - mylog("describeCol: col %d precision = %d\n", icol, precision); + mylog("describeCol: col %d column_size = %d\n", icol, column_size); result = SQL_SUCCESS; @@ -341,37 +343,37 @@ PGAPI_DescribeCol( } /* - * SQL TYPE + * CONCISE(SQL) TYPE */ if (pfSqlType) { - *pfSqlType = pgtype_to_sqltype(stmt, fieldtype); + *pfSqlType = pgtype_to_concise_type(stmt, fieldtype); mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType); } /* - * PRECISION + * COLUMN SIZE(PRECISION in 2.x) */ if (pcbColDef) { - if (precision < 0) - precision = 0; /* "I dont know" */ + if (column_size < 0) + column_size = 0; /* "I dont know" */ - *pcbColDef = precision; + *pcbColDef = column_size; mylog("describeCol: col %d *pcbColDef = %d\n", icol, *pcbColDef); } /* - * SCALE + * DECIMAL DIGITS(SCALE in 2.x) */ if (pibScale) { - if (scale < 0) - scale = 0; + if (decimal_digits < 0) + decimal_digits = 0; - *pibScale = scale; + *pibScale = decimal_digits; mylog("describeCol: col %d *pibScale = %d\n", icol, *pibScale); } @@ -380,7 +382,7 @@ PGAPI_DescribeCol( */ if (pfNullable) { - *pfNullable = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype); + *pfNullable = (parse_ok) ? irdflds->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype); mylog("describeCol: col %d *pfNullable = %d\n", icol, *pfNullable); } @@ -402,6 +404,7 @@ PGAPI_ColAttributes( { static char *func = "PGAPI_ColAttributes"; StatementClass *stmt = (StatementClass *) hstmt; + IRDFields *irdflds; Int4 col_idx, field_type = 0; ConnectionClass *conn; ConnInfo *ci; @@ -414,7 +417,8 @@ PGAPI_ColAttributes( value = 0; const FIELD_INFO *fi = NULL; - mylog("%s: entering..col=%d %d.\n", func, icol, fDescType); + mylog("%s: entering..col=%d %d len=%d.\n", func, icol, fDescType, + cbDescMax); if (!stmt) { @@ -422,6 +426,9 @@ PGAPI_ColAttributes( return SQL_INVALID_HANDLE; } + if (pcbDesc) + *pcbDesc = 0; + irdflds = SC_get_IRD(stmt); conn = SC_get_conn(stmt); ci = &(conn->connInfo); @@ -466,7 +473,7 @@ PGAPI_ColAttributes( parse_statement(stmt); } - cols = stmt->nfld; + cols = irdflds->nfields; /* * Column Count is a special case. The Column number is ignored @@ -484,7 +491,7 @@ PGAPI_ColAttributes( return SQL_SUCCESS; } - if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[col_idx]) + if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi && irdflds->fi[col_idx]) { if (col_idx >= cols) { @@ -493,14 +500,14 @@ PGAPI_ColAttributes( SC_log_error(func, "", stmt); return SQL_ERROR; } - field_type = stmt->fi[col_idx]->type; + field_type = irdflds->fi[col_idx]->type; if (field_type > 0) parse_ok = TRUE; } } if (parse_ok) - fi = stmt->fi[col_idx]; + fi = irdflds->fi[col_idx]; else { SC_pre_execute(stmt); @@ -542,8 +549,8 @@ PGAPI_ColAttributes( } field_type = QR_get_field_type(SC_get_Curres(stmt), col_idx); - if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[col_idx]) - fi = stmt->fi[col_idx]; + if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi && irdflds->fi[col_idx]) + fi = irdflds->fi[col_idx]; } mylog("colAttr: col %d field_type = %d\n", col_idx, field_type); @@ -587,15 +594,18 @@ inolog("AUTO_INCREMENT=%d\n", value); #if (ODBCVER >= 0x0300) case SQL_DESC_NAME: -#endif /* ODBCVER */ +#else case SQL_COLUMN_NAME: +#endif /* ODBCVER */ p = fi ? (fi->alias[0] ? fi->alias : fi->name) : QR_get_fieldname(SC_get_Curres(stmt), col_idx); mylog("PGAPI_ColAttr: COLUMN_NAME = '%s'\n", p); break; case SQL_COLUMN_LENGTH: - value = fi ? fi->length : pgtype_length(stmt, field_type, col_idx, unknown_sizes); + value = (fi && fi->length > 0) ? fi->length : pgtype_buffer_length(stmt, field_type, col_idx, unknown_sizes); + if (value < 0) + value = 0; mylog("PGAPI_ColAttributes: col %d, length = %d\n", col_idx, value); break; @@ -607,8 +617,9 @@ inolog("COLUMN_MONEY=%d\n", value); #if (ODBCVER >= 0x0300) case SQL_DESC_NULLABLE: -#endif /* ODBCVER */ +#else case SQL_COLUMN_NULLABLE: +#endif /* ODBCVER */ value = fi ? fi->nullable : pgtype_nullable(stmt, field_type); inolog("COLUMN_NULLABLE=%d\n", value); break; @@ -617,19 +628,23 @@ inolog("COLUMN_NULLABLE=%d\n", value); p = ""; break; - case SQL_COLUMN_PRECISION: - value = fi ? fi->precision : pgtype_precision(stmt, field_type, col_idx, unknown_sizes); + case SQL_COLUMN_PRECISION: /* in 2.x */ + value = (fi && fi->column_size > 0) ? fi->column_size : pgtype_column_size(stmt, field_type, col_idx, unknown_sizes); + if (value < 0) + value = 0; - mylog("PGAPI_ColAttributes: col %d, precision = %d\n", col_idx, value); + mylog("PGAPI_ColAttributes: col %d, column_size = %d\n", col_idx, value); break; case SQL_COLUMN_QUALIFIER_NAME: /* == SQL_DESC_CATALOG_NAME */ p = ""; break; - case SQL_COLUMN_SCALE: - value = pgtype_scale(stmt, field_type, col_idx); + case SQL_COLUMN_SCALE: /* in 2.x */ + value = pgtype_decimal_digits(stmt, field_type, col_idx); inolog("COLUMN_SCALE=%d\n", value); + if (value < 0) + value = 0; break; case SQL_COLUMN_SEARCHABLE: /* SQL_DESC_SEARCHABLE */ @@ -643,7 +658,7 @@ inolog("COLUMN_SCALE=%d\n", value); break; case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */ - value = pgtype_to_sqltype(stmt, field_type); + value = pgtype_to_concise_type(stmt, field_type); inolog("COLUMN_TYPE=%d\n", value); break; @@ -678,46 +693,53 @@ inolog("COLUMN_TYPE=%d\n", value); mylog("PGAPI_ColAttr: BASE_COLUMN_NAME = '%s'\n", p); break; case SQL_DESC_BASE_TABLE_NAME: /* the same as TABLE_NAME ok ? */ - p = fi && (fi->ti) ? fi->ti->name : ""; + p = (fi && (fi->ti)) ? fi->ti->name : ""; mylog("PGAPI_ColAttr: BASE_TABLE_NAME = '%s'\n", p); break; case SQL_DESC_LENGTH: /* different from SQL_COLUMN_LENGTH */ - value = fi ? fi->length : pgtype_length(stmt, field_type, col_idx, unknown_sizes); + value = (fi && fi->length > 0) ? fi->length : pgtype_desclength(stmt, field_type, col_idx, unknown_sizes); + if (value < 0) + value = 0; mylog("PGAPI_ColAttributes: col %d, length = %d\n", col_idx, value); break; case SQL_DESC_OCTET_LENGTH: - value = fi ? fi->length : pgtype_length(stmt, field_type, col_idx, unknown_sizes); - + value = (fi && fi->length > 0) ? fi->length : pgtype_transfer_octet_length(stmt, field_type, col_idx, unknown_sizes); + if (value < 0) + value = 0; mylog("PGAPI_ColAttributes: col %d, octet_length = %d\n", col_idx, value); break; case SQL_DESC_PRECISION: /* different from SQL_COLUMN_PRECISION */ - value = fi ? fi->precision : pgtype_precision(stmt, field_type, col_idx, unknown_sizes); + value = (fi && fi->column_size > 0) ? fi->column_size : pgtype_precision(stmt, field_type, col_idx, unknown_sizes); + if (value < 0) + value = 0; mylog("PGAPI_ColAttributes: col %d, desc_precision = %d\n", col_idx, value); break; case SQL_DESC_SCALE: /* different from SQL_COLUMN_SCALE */ value = pgtype_scale(stmt, field_type, col_idx); + if (value < 0) + value = 0; break; case SQL_DESC_LOCAL_TYPE_NAME: p = pgtype_to_name(stmt, field_type); break; case SQL_DESC_TYPE: - value = pgtype_to_sqltype(stmt, field_type); - switch (value) - { - case SQL_TYPE_DATE: - case SQL_TYPE_TIME: - case SQL_TYPE_TIMESTAMP: - value = SQL_DATETIME; - break; - } + value = pgtype_to_sqldesctype(stmt, field_type); + break; + case SQL_DESC_NUM_PREC_RADIX: + value = pgtype_radix(stmt, field_type); break; case SQL_DESC_LITERAL_PREFIX: + p = pgtype_literal_prefix(stmt, field_type); + break; case SQL_DESC_LITERAL_SUFFIX: - case SQL_DESC_NUM_PREC_RADIX: + p = pgtype_literal_suffix(stmt, field_type); + break; case SQL_DESC_UNNAMED: + value = (fi && !fi->name[0] && !fi->alias[0]) ? SQL_UNNAMED : SQL_NAMED; + break; #endif /* ODBCVER */ default: stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; @@ -967,6 +989,7 @@ PGAPI_Fetch( { static char *func = "PGAPI_Fetch"; StatementClass *stmt = (StatementClass *) hstmt; + ARDFields *opts; QResultClass *res; mylog("PGAPI_Fetch: stmt = %u, stmt->result= %u\n", stmt, SC_get_Curres(stmt)); @@ -988,7 +1011,8 @@ PGAPI_Fetch( } /* Not allowed to bind a bookmark column when using SQLFetch. */ - if (stmt->bookmark.buffer) + opts = SC_get_ARD(stmt); + if (opts->bookmark->buffer) { stmt->errornumber = STMT_COLNUM_ERROR; stmt->errormsg = "Not allowed to bind a bookmark column when using PGAPI_Fetch"; @@ -1012,7 +1036,7 @@ PGAPI_Fetch( return SQL_ERROR; } - if (stmt->bindings == NULL) + if (opts->bindings == NULL) { /* just to avoid a crash if the user insists on calling this */ /* function even if SQL_ExecDirect has reported an Error */ @@ -1040,6 +1064,7 @@ PGAPI_ExtendedFetch( { static char *func = "PGAPI_ExtendedFetch"; StatementClass *stmt = (StatementClass *) hstmt; + ARDFields *opts; QResultClass *res; int num_tuples, i, @@ -1078,11 +1103,12 @@ PGAPI_ExtendedFetch( return SQL_ERROR; } + opts = SC_get_ARD(stmt); /* * If a bookmark colunmn is bound but bookmark usage is off, then * error */ - if (stmt->bookmark.buffer && stmt->options.use_bookmarks == SQL_UB_OFF) + if (opts->bookmark->buffer && stmt->options.use_bookmarks == SQL_UB_OFF) { stmt->errornumber = STMT_COLNUM_ERROR; stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled"; @@ -1106,7 +1132,7 @@ PGAPI_ExtendedFetch( return SQL_ERROR; } - if (stmt->bindings == NULL) + if (opts->bindings == NULL) { /* just to avoid a crash if the user insists on calling this */ /* function even if SQL_ExecDirect has reported an Error */ @@ -1118,7 +1144,7 @@ PGAPI_ExtendedFetch( /* Initialize to no rows fetched */ if (rgfRowStatus) - for (i = 0; i < stmt->options.rowset_size; i++) + for (i = 0; i < opts->rowset_size; i++) *(rgfRowStatus + i) = SQL_ROW_NOROW; if (pcrow) @@ -1144,7 +1170,7 @@ PGAPI_ExtendedFetch( stmt->rowset_start = 0; else - stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : stmt->options.rowset_size); + stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : opts->rowset_size); mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple); break; @@ -1159,22 +1185,22 @@ PGAPI_ExtendedFetch( */ if (stmt->rowset_start >= num_tuples) { - if (stmt->options.rowset_size > num_tuples) + if (opts->rowset_size > num_tuples) { stmt->errornumber = STMT_POS_BEFORE_RECORDSET; stmt->errormsg = "fetch prior from eof and before the beggining"; } - stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size); + stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - opts->rowset_size); } else { - if (stmt->rowset_start < stmt->options.rowset_size) + if (stmt->rowset_start < opts->rowset_size) { stmt->errormsg = "fetch prior and before the beggining"; stmt->errornumber = STMT_POS_BEFORE_RECORDSET; } - stmt->rowset_start -= stmt->options.rowset_size; + stmt->rowset_start -= opts->rowset_size; } break; @@ -1187,7 +1213,7 @@ PGAPI_ExtendedFetch( case SQL_FETCH_LAST: mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple); - stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size); + stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - opts->rowset_size); break; case SQL_FETCH_ABSOLUTE: @@ -1255,7 +1281,7 @@ PGAPI_ExtendedFetch( /* 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) + if (stmt->rowset_start + opts->rowset_size <= 0) { stmt->rowset_start = -1; return SQL_NO_DATA_FOUND; @@ -1271,7 +1297,7 @@ PGAPI_ExtendedFetch( stmt->currTuple = stmt->rowset_start - 1; /* increment the base row in the tuple cache */ - QR_set_rowset_size(res, stmt->options.rowset_size); + QR_set_rowset_size(res, opts->rowset_size); /* QR_inc_base(res, stmt->last_fetch_count); */ /* Is inc_base right ? */ res->base = stmt->rowset_start; @@ -1281,7 +1307,7 @@ PGAPI_ExtendedFetch( mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple); truncated = error = FALSE; - for (i = 0; i < stmt->options.rowset_size; i++) + for (i = 0; i < opts->rowset_size; i++) { stmt->bind_row = i; /* set the binding location */ result = SC_fetch(stmt); @@ -1302,11 +1328,14 @@ PGAPI_ExtendedFetch( #ifdef DRIVER_CURSOR_IMPLEMENT else if (res->keyset) { - UWORD pstatus = res->keyset[stmt->currTuple].status & KEYSET_INFO_PUBLIC; + DWORD currp = stmt->rowset_start + i; + UWORD pstatus = res->keyset[currp].status & KEYSET_INFO_PUBLIC; if (pstatus != 0) { rgfRowStatus[i] = pstatus; - res->keyset[stmt->currTuple].status &= (~KEYSET_INFO_PUBLIC); + /* refresh the status */ + if (SQL_ROW_DELETED != pstatus) + res->keyset[currp].status &= (~KEYSET_INFO_PUBLIC); } else rgfRowStatus[i] = SQL_ROW_SUCCESS; @@ -1314,6 +1343,8 @@ PGAPI_ExtendedFetch( #endif /* DRIVER_CURSOR_IMPLEMENT */ else *(rgfRowStatus + i) = SQL_ROW_SUCCESS; +if (rgfRowStatus[i] != SQL_ROW_SUCCESS) +inolog("rgfRowStatus[%d]=%d\n", i, rgfRowStatus[i]); } } @@ -1374,39 +1405,35 @@ PGAPI_MoreResults( /* * Stuff for updatable cursors. */ -static const char *getOidValue(const QResultClass *res, int index) +static Int2 getNumResultCols(const QResultClass *res) { - return QR_get_value_backend_row(res, index, QR_NumResultCols(res) - 1); + Int2 res_cols = QR_NumResultCols(res); + return res->keyset ? res_cols - 2 : res_cols; } static UInt4 getOid(const QResultClass *res, int index) { return res->keyset[index].oid; } -static const char *getTidValue(const QResultClass *res, int index) -{ - return QR_get_value_backend_row(res, index, QR_NumResultCols(res) - 2); -} static void getTid(const QResultClass *res, int index, UInt4 *blocknum, UInt2 *offset) { *blocknum = res->keyset[index].blocknum; *offset = res->keyset[index].offset; } -static void KeySetSet(const QResultClass *res, int index) +static void KeySetSet(const TupleField *tuple, int num_fields, KeySet *keyset) { - int num_fields = res->num_fields; - TupleField *tuple = res->backend_tuples + num_fields * index; - KeySet *keyset = res->keyset + index; - sscanf(tuple[num_fields - 2].value, "(%u,%hu)", &keyset->blocknum, &keyset->offset); sscanf(tuple[num_fields - 1].value, "%u", &keyset->oid); } +#define LATEST_TUPLE_LOAD 1L +#define USE_INSERTED_TID (1L << 1) static QResultClass * -positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval) +positioned_load(StatementClass *stmt, UInt4 flag, UInt4 oid, const char *tidval) { QResultClass *qres; char *selstr; + BOOL latest = ((flag & LATEST_TUPLE_LOAD) != 0); UInt4 len; len = strlen(stmt->load_statement); @@ -1419,6 +1446,12 @@ positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, cons else sprintf(selstr, "%s where ctid = '%s' and oid = %u", stmt->load_statement, tidval, oid); } + else if ((flag & USE_INSERTED_TID) != 0) + { + len += 50; + selstr = malloc(len); + sprintf(selstr, "%s where ctid = currtid(0, '(,)') and oid = %u", stmt->load_statement, oid); + } else { len += 20; @@ -1428,23 +1461,24 @@ positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, cons mylog("selstr=%s\n", selstr); qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, CLEAR_RESULT_ON_ABORT); -free(selstr); + free(selstr); return qres; } RETCODE SQL_API -SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) +SC_pos_reload(StatementClass *stmt, UWORD irow, UDWORD global_ridx, UWORD *count) { int i, res_cols; - UWORD rcnt, global_ridx, offset; + UWORD rcnt, offset; UInt4 oid, blocknum; QResultClass *res, *qres; + IRDFields *irdflds = SC_get_IRD(stmt); RETCODE ret = SQL_ERROR; char tidval[32]; - mylog("positioned load fi=%x ti=%x\n", stmt->fi, stmt->ti); + mylog("positioned load fi=%x ti=%x\n", irdflds->fi, stmt->ti); rcnt = 0; if (count) *count = 0; @@ -1452,23 +1486,18 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) return SQL_ERROR; if (!stmt->ti) parse_statement(stmt); /* not preferable */ - if (!stmt->ti || stmt->ntab != 1) + if (!stmt->updatable) { stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; return SQL_ERROR; } global_ridx = irow + stmt->rowset_start; - res_cols = QR_NumResultCols(res); if (!(oid = getOid(res, global_ridx))) return SQL_SUCCESS_WITH_INFO; getTid(res, global_ridx, &blocknum, &offset); sprintf(tidval, "(%u, %u)", blocknum, offset); - /*if (!(oidval = getOidValue(res, global_ridx))) - return SQL_SUCCESS_WITH_INFO; - sscanf(oidval, "%u", &oid); - tidval = getTidValue(res, global_ridx);*/ - res_cols -= 2; - if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres) + res_cols = getNumResultCols(res); + if (qres = positioned_load(stmt, LATEST_TUPLE_LOAD, oid, tidval), qres) { TupleField *tupleo, *tuplen; @@ -1476,9 +1505,18 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) tupleo = res->backend_tuples + res->num_fields * global_ridx; if (rcnt == 1) { + int effective_fields = res_cols; + QR_set_position(qres, 0); tuplen = qres->tupleField; - for (i = 0; i < res->num_fields; i++) + if (res->keyset) + { + if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type && + strcmp(tuplen[qres->num_fields - 2].value, tidval)) + res->keyset[global_ridx].status |= SQL_ROW_UPDATED; + KeySetSet(tuplen, qres->num_fields, res->keyset + global_ridx); + } + for (i = 0; i < effective_fields; i++) { if (tupleo[i].value) free(tupleo[i].value); @@ -1487,13 +1525,6 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) tupleo[i].value = tuplen[i].value; tuplen[i].value = NULL; } - if (res->keyset) - { - if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type && - strcmp(tupleo[res->num_fields - 2].value, tidval)) - res->keyset[global_ridx].status |= SQL_ROW_UPDATED; - KeySetSet(res, global_ridx); - } ret = SQL_SUCCESS; } else @@ -1503,10 +1534,7 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) ret = SQL_SUCCESS_WITH_INFO; if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) { - if (tupleo[res_cols + 1].value) - free(tupleo[res_cols + 1].value); - tupleo[res_cols + 1].value = NULL; - tupleo[res_cols + 1].len = 0; + res->keyset[global_ridx].oid = 0; res->keyset[global_ridx].status |= SQL_ROW_DELETED; } } @@ -1520,24 +1548,23 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) } RETCODE SQL_API -SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) +SC_pos_newload(StatementClass *stmt, UInt4 oid, BOOL tidRef) { int i; - QResultClass *res, - *qres; + QResultClass *res, *qres; RETCODE ret = SQL_ERROR; - mylog("positioned new fi=%x ti=%x\n", stmt->fi, stmt->ti); + mylog("positioned new ti=%x\n", stmt->ti); if (!(res = SC_get_Curres(stmt))) return SQL_ERROR; if (!stmt->ti) parse_statement(stmt); /* not preferable */ - if (!stmt->ti || stmt->ntab != 1) + if (!stmt->updatable) { stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; return SQL_ERROR; } - if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres) + if (qres = positioned_load(stmt, tidRef ? USE_INSERTED_TID : 0, oid, NULL), qres) { TupleField *tupleo, *tuplen; int count = QR_get_num_tuples(qres); @@ -1545,6 +1572,8 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) QR_set_position(qres, 0); if (count == 1) { + int effective_fields = res->num_fields; + tuplen = qres->tupleField; if (res->fcount >= res->count_allocated) { @@ -1569,22 +1598,29 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) res->count_allocated = tuple_size; } tupleo = res->backend_tuples + res->num_fields * res->fcount; - for (i = 0; i < res->num_fields; i++) + KeySetSet(tuplen, qres->num_fields, res->keyset + res->fcount); + for (i = 0; i < effective_fields; i++) { tupleo[i].len = tuplen[i].len; tuplen[i].len = 0; tupleo[i].value = tuplen[i].value; tuplen[i].value = NULL; } - KeySetSet(res, res->fcount); + for (; i < res->num_fields; i++) + { + tupleo[i].len = 0; + tupleo[i].value = NULL; + } res->fcount++; ret = SQL_SUCCESS; } + else if (0 == count) + ret = SQL_NO_DATA_FOUND; else { stmt->errornumber = STMT_ROW_VERSION_CHANGED; - stmt->errormsg = "the content was changed before updation"; - ret = SQL_SUCCESS_WITH_INFO; + stmt->errormsg = "the driver cound't identify inserted rows"; + ret = SQL_ERROR; } QR_Destructor(qres); /* stmt->currTuple = stmt->rowset_start + irow; */ @@ -1593,7 +1629,7 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) } static RETCODE SQL_API -irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow) +irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow, UDWORD global_ridx) { if (ret != SQL_ERROR) { @@ -1604,18 +1640,17 @@ irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow sscanf(cmdstr, "UPDATE %d", &updcnt) == 1) { if (updcnt == 1) - SC_pos_reload(stmt, irow, (UWORD *) 0); + SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); else if (updcnt == 0) { stmt->errornumber = STMT_ROW_VERSION_CHANGED; stmt->errormsg = "the content was changed before updation"; ret = SQL_ERROR; if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - SC_pos_reload(stmt, irow, (UWORD *) 0); + SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); } else ret = SQL_ERROR; - stmt->currTuple = stmt->rowset_start + irow; } else ret = SQL_ERROR; @@ -1627,62 +1662,60 @@ irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow } return ret; } -RETCODE SQL_API +RETCODE SC_pos_update(StatementClass *stmt, - UWORD irow) + UWORD irow, UDWORD global_ridx) { int i, - res_cols, num_cols, upd_cols; - UWORD global_ridx; QResultClass *res; - BindInfoClass *bindings = stmt->bindings; + ARDFields *opts = SC_get_ARD(stmt); + IRDFields *irdflds = SC_get_IRD(stmt); + BindInfoClass *bindings = opts->bindings; + FIELD_INFO **fi = SC_get_IRD(stmt)->fi; char updstr[4096]; RETCODE ret; UInt4 oid, offset, blocknum; UInt2 pgoffset; - Int4 *used; + Int4 *used, bind_size = opts->bind_size; - mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, SC_get_Curres(stmt)->base, stmt->fi, stmt->ti); + mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, SC_get_Curres(stmt)->base,fi, stmt->ti); if (!(res = SC_get_Curres(stmt))) return SQL_ERROR; if (!stmt->ti) parse_statement(stmt); /* not preferable */ - if (!stmt->ti || stmt->ntab != 1) + if (!stmt->updatable) { stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; return SQL_ERROR; } - global_ridx = irow + stmt->rowset_start; - res_cols = QR_NumResultCols(res); - /*if (!(oidval = getOidValue(res, global_ridx)))*/ if (!(oid = getOid(res, global_ridx))) { stmt->errormsg = "The row is already deleted"; return SQL_ERROR; } - /*tidval = getTidValue(res, global_ridx);*/ getTid(res, global_ridx, &blocknum, &pgoffset); sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name); - num_cols = stmt->nfld; - if (stmt->options.row_offset_ptr) - offset = *stmt->options.row_offset_ptr; - else - offset = 0; + num_cols = irdflds->nfields; + offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; for (i = upd_cols = 0; i < num_cols; i++) { if (used = bindings[i].used, used != NULL) { used += (offset >> 2); - mylog("%d used=%d\n", i, *used); - if (*used != SQL_IGNORE) + if (bind_size > 0) + used += (bind_size * irow / 4); + else + used += irow; + mylog("%d used=%d,%x\n", i, *used, used); + if (*used != SQL_IGNORE && fi[i]->updatable) { if (upd_cols) - sprintf(updstr, "%s, \"%s\" = ?", updstr, stmt->fi[i]->name); + sprintf(updstr, "%s, \"%s\" = ?", updstr, fi[i]->name); else - sprintf(updstr, "%s \"%s\" = ?", updstr, stmt->fi[i]->name); + sprintf(updstr, "%s \"%s\" = ?", updstr, fi[i]->name); upd_cols++; } } @@ -1695,6 +1728,7 @@ SC_pos_update(StatementClass *stmt, int j; int res_cols = QR_NumResultCols(res); StatementClass *qstmt; + APDFields *apdopts; /*sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr, tidval, oidval);*/ @@ -1704,21 +1738,26 @@ SC_pos_update(StatementClass *stmt, if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS) return SQL_ERROR; qstmt = (StatementClass *) hstmt; - qstmt->options.param_bind_type = stmt->options.bind_size; - qstmt->options.param_offset_ptr = stmt->options.row_offset_ptr; + apdopts = SC_get_APD(qstmt); + apdopts->param_bind_type = opts->bind_size; + apdopts->param_offset_ptr = opts->row_offset_ptr; for (i = j = 0; i < num_cols; i++) { if (used = bindings[i].used, used != NULL) { used += (offset >> 2); + if (bind_size > 0) + used += (bind_size * irow / 4); + else + used += irow; mylog("%d used=%d\n", i, *used); - if (*used != SQL_IGNORE) + if (*used != SQL_IGNORE && fi[i]->updatable) { PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++j, SQL_PARAM_INPUT, bindings[i].returntype, - pgtype_to_sqltype(stmt, QR_get_field_type(res, i)), + pgtype_to_concise_type(stmt, QR_get_field_type(res, i)), QR_get_fieldsize(res, i), - (SQLSMALLINT) stmt->fi[i]->precision, + (SQLSMALLINT) fi[i]->decimal_digits, bindings[i].buffer, bindings[i].buflen, bindings[i].used); @@ -1739,55 +1778,61 @@ SC_pos_update(StatementClass *stmt, stmt->errormsg = "SetPos with data_at_exec not yet supported"; ret = SQL_ERROR; } - ret = irow_update(ret, stmt, qstmt, irow); + ret = irow_update(ret, stmt, qstmt, irow, global_ridx); PGAPI_FreeStmt(hstmt, SQL_DROP); } else + { ret = SQL_SUCCESS_WITH_INFO; + stmt->errormsg = "update list null"; + } if (SQL_SUCCESS == ret && res->keyset) - res->keyset[global_ridx].status |= (SQL_ROW_UPDATED | DRV_SELF_UPDATED); + { + if (CC_is_in_trans(SC_get_conn(stmt))) + res->keyset[global_ridx].status |= (SQL_ROW_UPDATED | CURS_SELF_UPDATING); + else + res->keyset[global_ridx].status |= (SQL_ROW_UPDATED | CURS_SELF_UPDATED); + } #if (ODBCVER >= 0x0300) - if (stmt->options.rowStatusArray) + if (irdflds->rowStatusArray) { switch (ret) { case SQL_SUCCESS: - stmt->options.rowStatusArray[irow] = SQL_ROW_UPDATED; + irdflds->rowStatusArray[irow] = SQL_ROW_UPDATED; break; default: - stmt->options.rowStatusArray[irow] = ret; + irdflds->rowStatusArray[irow] = ret; } } #endif /* ODBCVER */ return ret; } -RETCODE SQL_API +RETCODE SC_pos_delete(StatementClass *stmt, - UWORD irow) + UWORD irow, UDWORD global_ridx) { - int res_cols; - UWORD global_ridx, offset; + UWORD offset; QResultClass *res, *qres; - BindInfoClass *bindings = stmt->bindings; + ARDFields *opts = SC_get_ARD(stmt); + IRDFields *irdflds = SC_get_IRD(stmt); + BindInfoClass *bindings = opts->bindings; char dltstr[4096]; RETCODE ret; /*const char *oidval;*/ UInt4 oid, blocknum; - mylog("POS DELETE fi=%x ti=%x\n", stmt->fi, stmt->ti); + mylog("POS DELETE ti=%x\n", stmt->ti); if (!(res = SC_get_Curres(stmt))) return SQL_ERROR; if (!stmt->ti) parse_statement(stmt); /* not preferable */ - if (!stmt->ti || stmt->ntab != 1) + if (!stmt->updatable) { stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; return SQL_ERROR; } - res_cols = QR_NumResultCols(res); - global_ridx = irow + stmt->rowset_start; - /* if (!(oidval = getOidValue(res, global_ridx)))*/ if (!(oid = getOid(res, global_ridx))) { stmt->errormsg = "The row is already deleted"; @@ -1810,18 +1855,17 @@ SC_pos_delete(StatementClass *stmt, sscanf(cmdstr, "DELETE %d", &dltcnt) == 1) { if (dltcnt == 1) - SC_pos_reload(stmt, irow, (UWORD *) 0); + SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); else if (dltcnt == 0) { stmt->errornumber = STMT_ROW_VERSION_CHANGED; stmt->errormsg = "the content was changed before deletion"; ret = SQL_ERROR; if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - SC_pos_reload(stmt, irow, (UWORD *) 0); + SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); } else ret = SQL_ERROR; - stmt->currTuple = stmt->rowset_start + irow; } else ret = SQL_ERROR; @@ -1836,17 +1880,22 @@ SC_pos_delete(StatementClass *stmt, if (qres) QR_Destructor(qres); if (SQL_SUCCESS == ret && res->keyset) - res->keyset[global_ridx].status |= (SQL_ROW_DELETED | DRV_SELF_DELETED); + { + if (CC_is_in_trans(SC_get_conn(stmt))) + res->keyset[global_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETING); + else + res->keyset[global_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETED); + } #if (ODBCVER >= 0x0300) - if (stmt->options.rowStatusArray) + if (irdflds->rowStatusArray) { switch (ret) { case SQL_SUCCESS: - stmt->options.rowStatusArray[irow] = SQL_ROW_DELETED; + irdflds->rowStatusArray[irow] = SQL_ROW_DELETED; break; default: - stmt->options.rowStatusArray[irow] = ret; + irdflds->rowStatusArray[irow] = ret; } } #endif /* ODBCVER */ @@ -1858,24 +1907,42 @@ irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt, int addpos { if (ret != SQL_ERROR) { - int addcnt; + int addcnt; UInt4 oid; - const char *cmdstr = QR_get_command(SC_get_Curres(istmt)); + ARDFields *opts = SC_get_ARD(stmt); + QResultClass *ires = SC_get_Curres(istmt); + const char *cmdstr; + cmdstr = QR_get_command((ires->next ? ires->next : ires)); if (cmdstr && sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 && addcnt == 1) { - SC_pos_newload(stmt, oid, NULL); - if (stmt->bookmark.buffer) + ConnectionClass *conn = SC_get_conn(stmt); + RETCODE qret; + + qret = SQL_NO_DATA_FOUND; + if (PG_VERSION_GE(conn, 7.2)) + { + qret = SC_pos_newload(stmt, oid, TRUE); + if (SQL_ERROR == qret) + return qret; + } + if (SQL_NO_DATA_FOUND == qret) + { + qret = SC_pos_newload(stmt, oid, FALSE); + if (SQL_ERROR == qret) + return qret; + } + if (opts->bookmark->buffer) { char buf[32]; - UInt4 offset = stmt->options.row_offset_ptr ? *stmt->options.row_offset_ptr : 0; + UInt4 offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; sprintf(buf, "%ld", addpos + 1); copy_and_convert_field(stmt, 0, buf, - SQL_C_ULONG, stmt->bookmark.buffer + offset, - 0, stmt->bookmark.used ? stmt->bookmark.used + SQL_C_ULONG, opts->bookmark->buffer + offset, + 0, opts->bookmark->used ? opts->bookmark->used + (offset >> 2) : NULL); } } @@ -1887,7 +1954,7 @@ irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt, int addpos } return ret; } -RETCODE SQL_API +RETCODE SC_pos_add(StatementClass *stmt, UWORD irow) { @@ -1896,51 +1963,62 @@ SC_pos_add(StatementClass *stmt, i; HSTMT hstmt; StatementClass *qstmt; + ConnectionClass *conn; QResultClass *res; - BindInfoClass *bindings = stmt->bindings; + ARDFields *opts = SC_get_ARD(stmt); + IRDFields *irdflds = SC_get_IRD(stmt); + APDFields *apdopts; + BindInfoClass *bindings = opts->bindings; + FIELD_INFO **fi = SC_get_IRD(stmt)->fi; char addstr[4096]; RETCODE ret; UInt4 offset; - Int4 *used; + Int4 *used, bind_size = opts->bind_size; - mylog("POS ADD fi=%x ti=%x\n", stmt->fi, stmt->ti); + mylog("POS ADD fi=%x ti=%x\n", fi, stmt->ti); if (!(res = SC_get_Curres(stmt))) return SQL_ERROR; if (!stmt->ti) parse_statement(stmt); /* not preferable */ - if (!stmt->ti || stmt->ntab != 1) + if (!stmt->updatable) { stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; return SQL_ERROR; } - num_cols = stmt->nfld; + num_cols = irdflds->nfields; + conn = SC_get_conn(stmt); sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name); if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS) return SQL_ERROR; - if (stmt->options.row_offset_ptr) - offset = *stmt->options.row_offset_ptr; + if (opts->row_offset_ptr) + offset = *opts->row_offset_ptr; else offset = 0; qstmt = (StatementClass *) hstmt; - qstmt->options.param_bind_type = stmt->options.bind_size; - qstmt->options.param_offset_ptr = stmt->options.row_offset_ptr; + apdopts = SC_get_APD(qstmt); + apdopts->param_bind_type = opts->bind_size; + apdopts->param_offset_ptr = opts->row_offset_ptr; for (i = add_cols = 0; i < num_cols; i++) { if (used = bindings[i].used, used != NULL) { used += (offset >> 2); + if (bind_size > 0) + used += (bind_size * irow / 4); + else + used += irow; mylog("%d used=%d\n", i, *used); - if (*used != SQL_IGNORE) + if (*used != SQL_IGNORE && fi[i]->updatable) { if (add_cols) - sprintf(addstr, "%s, \"%s\"", addstr, stmt->fi[i]->name); + sprintf(addstr, "%s, \"%s\"", addstr, fi[i]->name); else - sprintf(addstr, "%s\"%s\"", addstr, stmt->fi[i]->name); + sprintf(addstr, "%s\"%s\"", addstr, fi[i]->name); PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++add_cols, SQL_PARAM_INPUT, bindings[i].returntype, - pgtype_to_sqltype(stmt, QR_get_field_type(res, i)), + pgtype_to_concise_type(stmt, QR_get_field_type(res, i)), QR_get_fieldsize(res, i), - (SQLSMALLINT) stmt->fi[i]->precision, + (SQLSMALLINT) fi[i]->decimal_digits, bindings[i].buffer, bindings[i].buflen, bindings[i].used); @@ -1983,20 +2061,28 @@ SC_pos_add(StatementClass *stmt, stmt->bind_row = brow_save; } else + { ret = SQL_SUCCESS_WITH_INFO; + stmt->errormsg = "insert list null"; + } PGAPI_FreeStmt(hstmt, SQL_DROP); if (SQL_SUCCESS == ret && res->keyset) - res->keyset[res->fcount - 1].status |= DRV_SELF_ADDED; + { + if (CC_is_in_trans(conn)) + res->keyset[res->fcount - 1].status |= (SQL_ROW_ADDED | CURS_SELF_ADDING); + else + res->keyset[res->fcount - 1].status |= (SQL_ROW_ADDED | CURS_SELF_ADDED); + } #if (ODBCVER >= 0x0300) - if (stmt->options.rowStatusArray) + if (irdflds->rowStatusArray) { switch (ret) { case SQL_SUCCESS: - stmt->options.rowStatusArray[irow] = SQL_ROW_ADDED; + irdflds->rowStatusArray[irow] = SQL_ROW_ADDED; break; default: - stmt->options.rowStatusArray[irow] = ret; + irdflds->rowStatusArray[irow] = ret; } } #endif /* ODBCVER */ @@ -2009,6 +2095,47 @@ SC_pos_add(StatementClass *stmt, */ #endif /* DRIVER_CURSOR_IMPLEMENT */ +RETCODE +SC_pos_refresh(StatementClass *stmt, UWORD irow , UDWORD global_ridx) +{ + RETCODE ret; +#if (ODBCVER >= 0x0300) + IRDFields *irdflds = SC_get_IRD(stmt); +#endif /* ODBCVER */ + /* save the last_fetch_count */ + int last_fetch = stmt->last_fetch_count; + int bind_save = stmt->bind_row; + +#ifdef DRIVER_CURSOR_IMPLEMENT + if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) + SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); +#endif /* DRIVER_CURSOR_IMPLEMENT */ + stmt->bind_row = irow; + ret = SC_fetch(stmt); + /* restore the last_fetch_count */ + stmt->last_fetch_count = last_fetch; + stmt->bind_row = bind_save; +#if (ODBCVER >= 0x0300) + if (irdflds->rowStatusArray) + { + switch (ret) + { + case SQL_ERROR: + irdflds->rowStatusArray[irow] = SQL_ROW_ERROR; + break; + case SQL_SUCCESS: + irdflds->rowStatusArray[irow] = SQL_ROW_SUCCESS; + break; + default: + irdflds->rowStatusArray[irow] = ret; + break; + } + } +#endif /* ODBCVER */ + + return SQL_SUCCESS; +} + /* * This positions the cursor within a rowset, that was positioned using SQLExtendedFetch. * This will be useful (so far) only when using SQLGetData after SQLExtendedFetch. @@ -2023,10 +2150,13 @@ PGAPI_SetPos( static char *func = "PGAPI_SetPos"; RETCODE ret; StatementClass *stmt = (StatementClass *) hstmt; + ConnectionClass *conn = SC_get_conn(stmt); QResultClass *res; - int num_cols, - i; - BindInfoClass *bindings = stmt->bindings; + int num_cols, i, start_row, end_row, processed; + ARDFields *opts; + BindInfoClass *bindings; + UDWORD global_ridx, fcount; + BOOL auto_commit_needed = FALSE; if (!stmt) { @@ -2034,6 +2164,8 @@ PGAPI_SetPos( return SQL_INVALID_HANDLE; } + opts = SC_get_ARD(stmt); + bindings = opts->bindings; #ifdef DRIVER_CURSOR_IMPLEMENT mylog("%s fOption=%d irow=%d lock=%d currt=%d\n", func, fOption, irow, fLock, stmt->currTuple); if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY) @@ -2055,12 +2187,9 @@ PGAPI_SetPos( SC_log_error(func, "", stmt); return SQL_ERROR; } - num_cols = QR_NumResultCols(res); if (irow == 0) /* bulk operation */ { - int processed; - if (SQL_POSITION == fOption) { stmt->errornumber = STMT_ROW_OUT_OF_RANGE; @@ -2068,78 +2197,86 @@ PGAPI_SetPos( SC_log_error(func, "", stmt); return SQL_ERROR; } - ret = SQL_SUCCESS; - for (i = 0, processed = 0; i < stmt->options.rowset_size; i++) - { -#if (ODBCVER >= 0x0300) - if (!stmt->options.row_operation_ptr || stmt->options.row_operation_ptr[i] == SQL_ROW_PROCEED) - { -#endif /* ODBCVER */ - if (ret = PGAPI_SetPos(hstmt, (UWORD) (i + 1), fOption, fLock), SQL_ERROR == ret) - break; - processed++; -#if (ODBCVER >= 0x0300) - } -#endif /* ODBCVER */ - } - if (processed > 0 && SQL_ERROR == ret) - { - processed++; - ret = SQL_SUCCESS_WITH_INFO; - stmt->errornumber = STMT_ERROR_IN_ROW; - } - if (stmt->options.rowsFetched) - *stmt->options.rowsFetched = processed; - return ret; + start_row = 0; + end_row = opts->rowset_size - 1; } - - if (irow > stmt->last_fetch_count) + else { - stmt->errornumber = STMT_ROW_OUT_OF_RANGE; - stmt->errormsg = "Row value out of range"; - 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; + } + start_row = end_row = irow - 1; } - irow--; - + num_cols = QR_NumResultCols(res); + /* Reset for SQLGetData */ + if (bindings) + for (i = 0; i < num_cols; i++) + bindings[i].data_left = -1; + ret = SQL_SUCCESS; + fcount = res->fcount; #ifdef DRIVER_CURSOR_IMPLEMENT switch (fOption) { case SQL_UPDATE: - return SC_pos_update(stmt, irow); case SQL_DELETE: - return SC_pos_delete(stmt, irow); case SQL_ADD: - return SC_pos_add(stmt, irow); + if (auto_commit_needed = CC_is_in_autocommit(conn), auto_commit_needed) + PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF); + break; } #endif /* DRIVER_CURSOR_IMPLEMENT */ - /* Reset for SQLGetData */ - for (i = 0; i < num_cols; i++) - bindings[i].data_left = -1; - - if (fOption == SQL_REFRESH) + for (i = start_row, processed = 0; i <= end_row; i++) { - /* save the last_fetch_count */ - int last_fetch = stmt->last_fetch_count; - int bind_save = stmt->bind_row; - +#if (ODBCVER >= 0x0300) + if (0 != irow || !opts->row_operation_ptr || opts->row_operation_ptr[i] == SQL_ROW_PROCEED) + { +#endif /* ODBCVER */ + global_ridx = i + stmt->rowset_start; + switch (fOption) + { #ifdef DRIVER_CURSOR_IMPLEMENT - if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - SC_pos_reload(stmt, irow, (UWORD *) 0); + case SQL_UPDATE: + ret = SC_pos_update(stmt, (UWORD) i, global_ridx); + break; + case SQL_DELETE: + ret = SC_pos_delete(stmt, (UWORD) i, global_ridx); + break; + case SQL_ADD: + ret = SC_pos_add(stmt, (UWORD) i); + break; #endif /* DRIVER_CURSOR_IMPLEMENT */ - stmt->currTuple = stmt->rowset_start + irow - 1; - stmt->bind_row = irow; - SC_fetch(stmt); - /* restore the last_fetch_count */ - stmt->last_fetch_count = last_fetch; - stmt->bind_row = bind_save; + case SQL_REFRESH: + ret = SC_pos_refresh(stmt, (UWORD) i, global_ridx); + break; + } + processed++; + if (SQL_ERROR == ret) + break; +#if (ODBCVER >= 0x0300) + } +#endif /* ODBCVER */ } - else - stmt->currTuple = stmt->rowset_start + irow; - QR_set_position(res, irow); - - return SQL_SUCCESS; + if (SQL_ERROR == ret) + res->fcount = fcount; /* restore the count */ + if (auto_commit_needed) + PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); + if (irow > 0) + { + if (SQL_ADD != fOption) /* for SQLGetData */ + { + stmt->currTuple = stmt->rowset_start + irow - 1; + QR_set_position(res, irow - 1); + } + } + else if (SC_get_IRD(stmt)->rowsFetched) + *SC_get_IRD(stmt)->rowsFetched = processed; +inolog("rowset=%d processed=%d ret=%d\n", opts->rowset_size, processed, ret); + return ret; } diff --git a/src/interfaces/odbc/setup.c b/src/interfaces/odbc/setup.c index 0175d69f32..f243f0e242 100644 --- a/src/interfaces/odbc/setup.c +++ b/src/interfaces/odbc/setup.c @@ -56,7 +56,7 @@ typedef struct tagSETUPDLG /* Prototypes */ void INTFUNC CenterDialog(HWND hdlg); -int CALLBACK ConfigDlgProc(HWND hdlg, WORD wMsg, WPARAM wParam, LPARAM lParam); +int CALLBACK ConfigDlgProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam); void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg); BOOL INTFUNC SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg); @@ -211,7 +211,7 @@ CenterDialog(HWND hdlg) */ int CALLBACK ConfigDlgProc(HWND hdlg, - WORD wMsg, + UINT wMsg, WPARAM wParam, LPARAM lParam) { diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index b33ccc2cc9..f48a172421 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -117,6 +117,10 @@ PGAPI_AllocStmt(HDBC hdbc, /* Copy default statement options based from Connection options */ stmt->options = conn->stmtOptions; + stmt->ardopts = conn->ardOptions; + stmt->ardopts.bookmark = (BindInfoClass *) malloc(sizeof(BindInfoClass)); + stmt->ardopts.bookmark->buffer = NULL; + stmt->ardopts.bookmark->used = NULL; stmt->stmt_size_limit = CC_get_max_query_len(conn); /* Save the handle for later */ @@ -205,17 +209,35 @@ void InitializeStatementOptions(StatementOptions *opt) { memset(opt, 0, sizeof(StatementOptions)); - opt->maxRows = 0; /* driver returns all rows */ - opt->maxLength = 0; /* driver returns all data for char/binary */ - opt->rowset_size = 1; + opt->maxRows = 0; /* driver returns all rows */ + opt->maxLength = 0; /* driver returns all data for char/binary */ 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; opt->use_bookmarks = SQL_UB_OFF; +} + + +/* + * ARDFields initialize + */ +void +InitializeARDFields(ARDFields *opt) +{ + memset(opt, 0, sizeof(ARDFields)); + opt->rowset_size = 1; + opt->bind_size = 0; /* default is to bind by column */ +} +/* + * APDFields initialize + */ +void +InitializeAPDFields(APDFields *opt) +{ + memset(opt, 0, sizeof(APDFields)); opt->paramset_size = 1; - opt->param_bind_type = 0; /* default is column-wise binding */ + opt->param_bind_type = 0; /* default is to bind by column */ } @@ -246,15 +268,6 @@ SC_Constructor(void) rv->stmt_size_limit = -1; rv->statement_type = STMT_TYPE_UNKNOWN; - rv->bindings = NULL; - rv->bindings_allocated = 0; - - rv->bookmark.buffer = NULL; - rv->bookmark.used = NULL; - - rv->parameters_allocated = 0; - rv->parameters = 0; - rv->currTuple = -1; rv->rowset_start = -1; rv->current_col = -1; @@ -274,22 +287,65 @@ SC_Constructor(void) /* 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)); + memset(&rv->ardopts, 0, sizeof(ARDFields)); + memset(&rv->apdopts, 0, sizeof(APDFields)); + memset(&rv->irdopts, 0, sizeof(IRDFields)); + memset(&rv->ipdopts, 0, sizeof(IPDFields)); rv->pre_executing = FALSE; rv->inaccurate_result = FALSE; rv->miscinfo = 0; + rv->updatable = FALSE; } return rv; } +void ARDFields_free(ARDFields * self) +{ + if (self->bookmark) + { + free(self->bookmark); + self->bookmark = NULL; + } + /* + * the memory pointed to by the bindings is not deallocated by the + * driver but by the application that uses that driver, so we don't + * have to care + */ + ARD_unbind_cols(self, TRUE); +} + +void APDFields_free(APDFields * self) +{ + /* param bindings */ + APD_free_params(self, STMT_FREE_PARAMS_ALL); +} + +void IRDFields_free(IRDFields * self) +{ + /* Free the parsed field information */ + if (self->fi) + { + int i; + + for (i = 0; i < (int) self->nfields; i++) + if (self->fi[i]) + free(self->fi[i]); + free(self->fi); + self->fi = NULL; + } +} + +void IPDFields_free(IPDFields * self) +{ +} + char SC_Destructor(StatementClass *self) { @@ -322,47 +378,25 @@ SC_Destructor(StatementClass *self) if (self->load_statement) free(self->load_statement); - SC_free_params(self, STMT_FREE_PARAMS_ALL); - - /* - * the memory pointed to by the bindings is not deallocated by the - * driver but by the application that uses that driver, so we don't - * have to care - */ - /* about that here. */ - if (self->bindings) - { - int lf; - - for (lf = 0; lf < self->bindings_allocated; lf++) - { - if (self->bindings[lf].ttlbuf != NULL) - free(self->bindings[lf].ttlbuf); - } - free(self->bindings); - } - - /* Free the parsed table information */ + /* Free the parsed table information */ if (self->ti) { - int i; + int i; for (i = 0; i < self->ntab; i++) - free(self->ti[i]); + if (self->ti[i]); + free(self->ti[i]); free(self->ti); + self->ti = NULL; } /* Free the parsed field information */ - if (self->fi) - { - int i; - - for (i = 0; i < self->nfld; i++) - free(self->fi[i]); - free(self->fi); - } - + ARDFields_free(&(self->ardopts)); + APDFields_free(&(self->apdopts)); + IRDFields_free(&(self->irdopts)); + IPDFields_free(&(self->ipdopts)); + free(self); mylog("SC_Destructor: EXIT\n"); @@ -378,46 +412,16 @@ SC_Destructor(StatementClass *self) void SC_free_params(StatementClass *self, char option) { - int i; - - mylog("SC_free_params: ENTER, self=%d\n", self); - - if (!self->parameters) - return; - - for (i = 0; i < self->parameters_allocated; i++) - { - if (self->parameters[i].data_at_exec == TRUE) - { - if (self->parameters[i].EXEC_used) - { - free(self->parameters[i].EXEC_used); - self->parameters[i].EXEC_used = NULL; - } - - if (self->parameters[i].EXEC_buffer) - { - if (self->parameters[i].SQLType != SQL_LONGVARBINARY) - free(self->parameters[i].EXEC_buffer); - self->parameters[i].EXEC_buffer = NULL; - } - } - } + APD_free_params(SC_get_APD(self), option); self->data_at_exec = -1; self->current_exec_param = -1; self->put_data = FALSE; - if (option == STMT_FREE_PARAMS_ALL) { - free(self->parameters); - self->parameters = NULL; - self->parameters_allocated = 0; self->exec_start_row = -1; self->exec_end_row = -1; self->exec_current_row = -1; } - - mylog("SC_free_params: EXIT\n"); } @@ -493,31 +497,22 @@ SC_recycle_statement(StatementClass *self) return FALSE; } - /* Free the parsed table information */ + /* Free the parsed table information */ if (self->ti) { - int i; + int i; for (i = 0; i < self->ntab; i++) - free(self->ti[i]); - - free(self->ti); + if (self->ti) + free(self->ti); self->ti = NULL; self->ntab = 0; } - /* Free the parsed field information */ - if (self->fi) - { - int i; + IRDFields_free(SC_get_IRD(self)); - for (i = 0; i < self->nfld; i++) - free(self->fi[i]); - free(self->fi); - self->fi = NULL; - self->nfld = 0; - } self->parse_status = STMT_PARSE_NONE; + self->updatable = FALSE; /* Free any cursors */ if (res = SC_get_Result(self), res) @@ -605,19 +600,11 @@ SC_pre_execute(StatementClass *self) char SC_unbind_cols(StatementClass *self) { - Int2 lf; + ARDFields *opts = SC_get_ARD(self); - 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; - self->bindings[lf].returntype = SQL_C_CHAR; - } - - self->bookmark.buffer = NULL; - self->bookmark.used = NULL; + ARD_unbind_cols(opts, FALSE); + opts->bookmark->buffer = NULL; + opts->bookmark->used = NULL; return 1; } @@ -732,6 +719,7 @@ SC_fetch(StatementClass *self) { static char *func = "SC_fetch"; QResultClass *res = SC_get_Curres(self); + ARDFields *opts; int retval, result; @@ -791,25 +779,26 @@ SC_fetch(StatementClass *self) result = SQL_SUCCESS; self->last_fetch_count = 1; + opts = SC_get_ARD(self); /* * If the bookmark column was bound then return a bookmark. Since this * is used with SQLExtendedFetch, and the rowset size may be greater * than 1, and an application can use row or column wise binding, use * the code in copy_and_convert_field() to handle that. */ - if (self->bookmark.buffer) + if (opts->bookmark->buffer) { char buf[32]; - UInt4 offset = self->options.row_offset_ptr ? *self->options.row_offset_ptr : 0; + UInt4 offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; sprintf(buf, "%ld", SC_get_bookmark(self)); result = copy_and_convert_field(self, 0, buf, - SQL_C_ULONG, self->bookmark.buffer + offset, 0, - self->bookmark.used ? self->bookmark.used + (offset >> 2) : NULL); + SQL_C_ULONG, opts->bookmark->buffer + offset, 0, + opts->bookmark->used ? opts->bookmark->used + (offset >> 2) : NULL); } #ifdef DRIVER_CURSOR_IMPLEMENT - if (self->options.scroll_concurrency != SQL_CONCUR_READ_ONLY) + if (res->haskeyset) { num_cols -= 2; } @@ -818,12 +807,12 @@ SC_fetch(StatementClass *self) return SQL_SUCCESS; for (lf = 0; lf < num_cols; lf++) { - mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer); + mylog("fetch: cols=%d, lf=%d, opts = %u, opts->bindings = %u, buffer[] = %u\n", num_cols, lf, opts, opts->bindings, opts->bindings[lf].buffer); /* reset for SQLGetData */ - self->bindings[lf].data_left = -1; + opts->bindings[lf].data_left = -1; - if (self->bindings[lf].buffer != NULL) + if (opts->bindings[lf].buffer != NULL) { /* this column has a binding */ @@ -871,7 +860,7 @@ SC_fetch(StatementClass *self) self->errornumber = STMT_TRUNCATED; self->errormsg = "Fetched item was truncated."; qlog("The %dth item was truncated\n", lf + 1); - qlog("The buffer size = %d", self->bindings[lf].buflen); + qlog("The buffer size = %d", opts->bindings[lf].buflen); qlog(" and the value is '%s'\n", value); result = SQL_SUCCESS_WITH_INFO; break; @@ -905,12 +894,14 @@ SC_execute(StatementClass *self) { static char *func = "SC_execute"; ConnectionClass *conn; + APDFields *apdopts; char was_ok, was_nonfatal; QResultClass *res = NULL; Int2 oldstatus, numcols; QueryInfo qi; ConnInfo *ci; + UDWORD qflag = 0; conn = SC_get_conn(self); @@ -931,13 +922,15 @@ SC_execute(StatementClass *self) (!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER))) { mylog(" about to begin a transaction on statement = %u\n", self); - if (!CC_begin(conn)) - { - self->errormsg = "Could not begin a transaction"; - self->errornumber = STMT_EXEC_ERROR; - SC_log_error(func, "", self); - return SQL_ERROR; - } + if (PG_VERSION_GE(conn, 7.1)) + qflag |= GO_INTO_TRANSACTION; + else if (!CC_begin(conn)) + { + self->errormsg = "Could not begin a transaction"; + self->errornumber = STMT_EXEC_ERROR; + SC_log_error(func, "", self); + return SQL_ERROR; + } } oldstatus = conn->status; @@ -954,7 +947,7 @@ SC_execute(StatementClass *self) if (self->statement_type == STMT_TYPE_SELECT) { char fetch[128]; - UDWORD qflag = (SQL_CONCUR_ROWVER == self->options.scroll_concurrency ? CREATE_KEYSET : 0); + qflag |= (SQL_CONCUR_READ_ONLY != self->options.scroll_concurrency ? CREATE_KEYSET : 0); mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name); @@ -964,6 +957,7 @@ SC_execute(StatementClass *self) QR_command_successful(res)) { QR_Destructor(res); + qflag &= (~ GO_INTO_TRANSACTION); /* * That worked, so now send the fetch to start getting data @@ -990,7 +984,7 @@ SC_execute(StatementClass *self) { /* not a SELECT statement so don't use a cursor */ mylog(" it's NOT a select statement: stmt=%u\n", self); - res = CC_send_query(conn, self->stmt_with_params, NULL, 0); + res = CC_send_query(conn, self->stmt_with_params, NULL, qflag); /* * We shouldn't send COMMIT. Postgres backend does the autocommit @@ -1037,8 +1031,9 @@ SC_execute(StatementClass *self) /* now allocate the array to hold the binding info */ if (numcols > 0) { - extend_bindings(self, numcols); - if (self->bindings == NULL) + ARDFields *opts = SC_get_ARD(self); + extend_column_bindings(opts, numcols); + if (opts->bindings == NULL) { QR_Destructor(res); self->errornumber = STMT_NO_MEMORY_ERROR; @@ -1083,12 +1078,13 @@ SC_execute(StatementClass *self) last->next = res; } + apdopts = SC_get_APD(self); if (self->statement_type == STMT_TYPE_PROCCALL && (self->errornumber == STMT_OK || self->errornumber == STMT_INFO_ONLY) && - self->parameters && - self->parameters[0].buffer && - self->parameters[0].paramType == SQL_PARAM_OUTPUT) + apdopts->parameters && + apdopts->parameters[0].buffer && + apdopts->parameters[0].paramType == SQL_PARAM_OUTPUT) { /* get the return value of the procedure * call */ RETCODE ret; @@ -1097,7 +1093,7 @@ SC_execute(StatementClass *self) ret = SC_fetch(hstmt); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - ret = PGAPI_GetData(hstmt, 1, self->parameters[0].CType, self->parameters[0].buffer, self->parameters[0].buflen, self->parameters[0].used); + ret = PGAPI_GetData(hstmt, 1, apdopts->parameters[0].CType, apdopts->parameters[0].buffer, apdopts->parameters[0].buflen, apdopts->parameters[0].used); if (ret != SQL_SUCCESS) { self->errornumber = STMT_EXEC_ERROR; @@ -1133,19 +1129,21 @@ SC_log_error(const char *func, const char *desc, const StatementClass *self) if (self) { QResultClass *res = SC_get_Result(self); + const ARDFields *opts = SC_get_ARD(self); + const APDFields *apdopts = SC_get_APD(self); qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg)); mylog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg)); qlog(" ------------------------------------------------------------\n"); qlog(" hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, res); qlog(" manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal); - qlog(" bindings=%u, bindings_allocated=%d\n", self->bindings, self->bindings_allocated); - qlog(" parameters=%u, parameters_allocated=%d\n", self->parameters, self->parameters_allocated); + qlog(" bindings=%u, bindings_allocated=%d\n", opts->bindings, opts->allocated); + qlog(" parameters=%u, parameters_allocated=%d\n", apdopts->parameters, apdopts->allocated); qlog(" statement_type=%d, statement='%s'\n", self->statement_type, nullcheck(self->statement)); qlog(" stmt_with_params='%s'\n", nullcheck(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->options.maxRows, self->options.rowset_size, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency); + qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->options.maxRows, opts->rowset_size, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency); qlog(" cursor_name='%s'\n", nullcheck(self->cursor_name)); qlog(" ----------------QResult Info -------------------------------\n"); diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index 74583fbb2d..65fa088cb4 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -12,6 +12,7 @@ #include "psqlodbc.h" #include "bind.h" +#include "descriptor.h" #ifndef FALSE @@ -77,6 +78,7 @@ typedef enum #define STMT_INVALID_OPTION_IDENTIFIER 28 #define STMT_RETURN_NULL_WITHOUT_INDICATOR 29 #define STMT_ERROR_IN_ROW 30 +#define STMT_INVALID_DESCRIPTOR_IDENTIFIER 31 /* statement types */ enum @@ -115,33 +117,6 @@ enum STMT_FETCH_EXTENDED, }; -typedef struct -{ - COL_INFO *col_info; /* cached SQLColumns info for this table */ - char name[MAX_TABLE_LEN + 1]; - char alias[MAX_TABLE_LEN + 1]; -} TABLE_INFO; - -typedef struct -{ - TABLE_INFO *ti; /* resolve to explicit table names */ - int precision; - int scale; - int display_size; - int length; - int type; - char nullable; - char func; - char expr; - char quote; - char dquote; - char numeric; - char updatable; - char dot[MAX_TABLE_LEN + 1]; - char name[MAX_COLUMN_LEN + 1]; - char alias[MAX_COLUMN_LEN + 1]; -} FIELD_INFO; - /******** Statement Handle ***********/ struct StatementClass_ @@ -152,19 +127,23 @@ struct StatementClass_ QResultClass *curres; /* the current result in the chain */ HSTMT FAR *phstmt; StatementOptions options; + ARDFields ardopts; + IRDFields irdopts; + APDFields apdopts; + IPDFields ipdopts; STMT_Status status; char *errormsg; int errornumber; /* information on bindings */ - BindInfoClass *bindings; /* array to store the binding information */ - BindInfoClass bookmark; - int bindings_allocated; + /*** BindInfoClass *bindings; ***/ /* array to store the binding information */ + /*** BindInfoClass bookmark; + int bindings_allocated; ***/ /* information on statement parameters */ - int parameters_allocated; - ParameterInfoClass *parameters; + /*** int parameters_allocated; + ParameterInfoClass *parameters; ***/ Int4 currTuple; /* current absolute row number (GetData, * SetPos, SQLFetch) */ @@ -184,8 +163,6 @@ struct StatementClass_ * statement that has been executed */ TABLE_INFO **ti; - FIELD_INFO **fi; - int nfld; int ntab; int parse_status; @@ -219,6 +196,7 @@ struct StatementClass_ char inaccurate_result; /* Current status is PREMATURE but * result is inaccurate */ char miscinfo; + char updatable; SWORD errorpos; SWORD error_recsize; char *load_statement; /* to (re)load updatable individual rows */ @@ -231,6 +209,10 @@ struct StatementClass_ #define SC_get_Result(a) (a->result) #define SC_set_Curres(a, b) (a->curres = b) #define SC_get_Curres(a) (a->curres) +#define SC_get_ARD(a) (&(a->ardopts)) +#define SC_get_APD(a) (&(a->apdopts)) +#define SC_get_IRD(a) (&(a->irdopts)) +#define SC_get_IPD(a) (&(a->ipdopts)) /* options for SC_free_params() */ #define STMT_FREE_PARAMS_ALL 0 @@ -262,5 +244,9 @@ RETCODE SC_fetch(StatementClass *self); void SC_free_params(StatementClass *self, char option); void SC_log_error(const char *func, const char *desc, const StatementClass *self); unsigned long SC_get_bookmark(StatementClass *self); +RETCODE SC_pos_update(StatementClass *self, UWORD irow, UDWORD index); +RETCODE SC_pos_delete(StatementClass *self, UWORD irow, UDWORD index); +RETCODE SC_pos_refresh(StatementClass *self, UWORD irow, UDWORD index); +RETCODE SC_pos_add(StatementClass *self, UWORD irow); #endif diff --git a/src/interfaces/odbc/tuple.h b/src/interfaces/odbc/tuple.h index 388f9fa021..eb00cfd509 100644 --- a/src/interfaces/odbc/tuple.h +++ b/src/interfaces/odbc/tuple.h @@ -38,10 +38,21 @@ struct KeySet_ UDWORD blocknum; UDWORD oid; }; -#define KEYSET_INFO_PUBLIC 0x0f -#define DRV_SELF_ADDED (1L << 4) -#define DRV_SELF_DELETED (1L << 5) -#define DRV_SELF_UPDATED (1L << 6) +/* Rollback(index + original TID) info */ +struct Rollback_ +{ + UDWORD index; + UDWORD blocknum; + UWORD offset; +}; +#define KEYSET_INFO_PUBLIC 0x07 +#define CURS_SELF_ADDING (1L << 3) +#define CURS_SELF_DELETING (1L << 4) +#define CURS_SELF_UPDATING (1L << 5) +#define CURS_SELF_ADDED (1L << 6) +#define CURS_SELF_DELETED (1L << 7) +#define CURS_SELF_UPDATED (1L << 8) +#define CURS_NEEDS_REREAD (1L << 9) /* These macros are wrappers for the corresponding set_tuplefield functions but these handle automatic NULL determination and call set_tuplefield_null() diff --git a/src/interfaces/odbc/win32.mak b/src/interfaces/odbc/win32.mak index 637e376337..1614f4f2c3 100644 --- a/src/interfaces/odbc/win32.mak +++ b/src/interfaces/odbc/win32.mak @@ -96,9 +96,9 @@ CLEAN : CPP=cl.exe !IF "$(CFG)" == "MultibyteRelease" -CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "MULTIBYTE" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "MULTIBYTE" /D "DRIVER_CURSOR_IMPLEMENT" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c !ELSE -CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "DRIVER_CURSOR_IMPLEMENT" /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c !ENDIF .c{$(INTDIR)}.obj:: diff --git a/src/interfaces/odbc/win32_30w.mak b/src/interfaces/odbc/win32_30w.mak index 4455069fda..e4dd84a66c 100644 --- a/src/interfaces/odbc/win32_30w.mak +++ b/src/interfaces/odbc/win32_30w.mak @@ -1,7 +1,7 @@ # # File: win32_30w.mak # -# Description: psqlodbc30 Unicode version Makefile for Win32. +# Description: psqlodbc30w Unicode version Makefile for Win32. # # Configurations: Unicode30Debug, Unicode30 # Build Types: ALL, CLEAN @@ -45,7 +45,7 @@ OUTDIR=.\Unicode30 OUTDIRBIN=.\Unicode30 INTDIR=.\Unicode30 -ALL : "$(OUTDIRBIN)\psqlodbc30.dll" +ALL : "$(OUTDIRBIN)\psqlodbc30w.dll" CLEAN : @@ -82,7 +82,7 @@ CLEAN : -@erase "$(INTDIR)\odbcapi.obj" -@erase "$(INTDIR)\odbcapi30.obj" -@erase "$(INTDIR)\vc60.idb" - -@erase "$(OUTDIR)\psqlodbc30.dll" + -@erase "$(OUTDIR)\psqlodbc30w.dll" -@erase "$(OUTDIR)\psqlodbc.exp" -@erase "$(OUTDIR)\psqlodbc.lib" @@ -131,7 +131,7 @@ BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc" BSC32_SBRS= \ LINK32=link.exe -LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\psqlodbc.pdb" /machine:I386 /def:"psqlodbc_api30w.def" /out:"$(OUTDIRBIN)\psqlodbc30.dll" /implib:"$(OUTDIR)\psqlodbc.lib" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\psqlodbc.pdb" /machine:I386 /def:"psqlodbc_api30w.def" /out:"$(OUTDIRBIN)\psqlodbc30w.dll" /implib:"$(OUTDIR)\psqlodbc.lib" DEF_FILE= "psqlodbc_api30w.def" LINK32_OBJS= \ "$(INTDIR)\bind.obj" \ @@ -167,14 +167,14 @@ LINK32_OBJS= \ "$(INTDIR)\odbcapi30.obj" \ "$(INTDIR)\psqlodbc.res" -"$(OUTDIRBIN)\psqlodbc30.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) +"$(OUTDIRBIN)\psqlodbc30w.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "Unicode30Debug" -ALL : "$(OUTDIR)\psqlodbc30.dll" +ALL : "$(OUTDIR)\psqlodbc30w.dll" CLEAN : @@ -212,7 +212,7 @@ CLEAN : -@erase "$(INTDIR)\odbcapi30.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" - -@erase "$(OUTDIR)\psqlodbc30.dll" + -@erase "$(OUTDIR)\psqlodbc30w.dll" -@erase "$(OUTDIR)\psqlodbc.exp" -@erase "$(OUTDIR)\psqlodbc.ilk" -@erase "$(OUTDIR)\psqlodbc.lib" @@ -263,7 +263,7 @@ BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc" BSC32_SBRS= \ LINK32=link.exe -LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\psqlodbc.pdb" /debug /machine:I386 /def:"psqlodbc_api30w.def" /out:"$(OUTDIR)\psqlodbc30.dll" /implib:"$(OUTDIR)\psqlodbc.lib" /pdbtype:sept +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\psqlodbc.pdb" /debug /machine:I386 /def:"psqlodbc_api30w.def" /out:"$(OUTDIR)\psqlodbc30w.dll" /implib:"$(OUTDIR)\psqlodbc.lib" /pdbtype:sept DEF_FILE= "psqlodbc_api30w.def" LINK32_OBJS= \ "$(INTDIR)\bind.obj" \ @@ -299,7 +299,7 @@ LINK32_OBJS= \ "$(INTDIR)\odbcapi30.obj" \ "$(INTDIR)\psqlodbc.res" -"$(OUTDIR)\psqlodbc30.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) +"$(OUTDIR)\psqlodbc30w.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) <<