From: Hiroshi Inoue Date: Thu, 14 Mar 2002 05:42:04 +0000 (+0000) Subject: 1) Internal improvements to handle updatable cursors(1st cut). X-Git-Tag: REL7_3~1896 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=01e322652b14c2aeac1737f29f8bcf9e18d1060e;p=postgresql 1) Internal improvements to handle updatable cursors(1st cut). 2) Fix a bug in SQLColAttribute(). --- diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c index d176d925d4..898b41ec28 100644 --- a/src/interfaces/odbc/connection.c +++ b/src/interfaces/odbc/connection.c @@ -374,7 +374,7 @@ CC_begin(ConnectionClass *self) char ret = TRUE; if (!CC_is_in_trans(self)) { - QResultClass *res = CC_send_query(self, "BEGIN", NULL, TRUE); + QResultClass *res = CC_send_query(self, "BEGIN", NULL, CLEAR_RESULT_ON_ABORT); mylog("CC_begin: sending BEGIN!\n"); if (res != NULL) @@ -401,7 +401,7 @@ CC_commit(ConnectionClass *self) char ret = FALSE; if (CC_is_in_trans(self)) { - QResultClass *res = CC_send_query(self, "COMMIT", NULL, TRUE); + QResultClass *res = CC_send_query(self, "COMMIT", NULL, CLEAR_RESULT_ON_ABORT); mylog("CC_commit: sending COMMIT!\n"); CC_set_no_trans(self); @@ -427,7 +427,7 @@ CC_abort(ConnectionClass *self) { if (CC_is_in_trans(self)) { - QResultClass *res = CC_send_query(self, "ROLLBACK", NULL, TRUE); + QResultClass *res = CC_send_query(self, "ROLLBACK", NULL, CLEAR_RESULT_ON_ABORT); mylog("CC_abort: sending ABORT!\n"); CC_set_no_trans(self); @@ -919,7 +919,7 @@ another_version_retry: */ mylog("sending an empty query...\n"); - res = CC_send_query(self, " ", NULL, TRUE); + res = CC_send_query(self, " ", NULL, CLEAR_RESULT_ON_ABORT); if (res == NULL || QR_get_status(res) != PGRES_EMPTY_QUERY) { mylog("got no result from the empty query. (probably database does not exist)\n"); @@ -956,7 +956,7 @@ another_version_retry: * Multibyte handling is available ? */ #ifdef MULTIBYTE - if (PG_VERSION_GE(self, 7.0)) + if (PG_VERSION_GE(self, 6.4)) { CC_lookup_characterset(self); if (self->errornumber != 0) @@ -977,7 +977,7 @@ another_version_retry: if (self->client_encoding) free(self->client_encoding); self->client_encoding = NULL; - if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, TRUE), res) + if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, CLEAR_RESULT_ON_ABORT), res) { self->client_encoding = strdup("UNICODE"); QR_Destructor(res); @@ -991,7 +991,7 @@ another_version_retry: else if (self->unicode) { self->errornumber = CONN_NOT_IMPLEMENTED_ERROR; - self->errormsg = "Unicode isn't supported before 7.0"; + self->errormsg = "Unicode isn't supported before 6.4"; return 0; } #endif /* UNICODE_SUPPORT */ @@ -1128,12 +1128,14 @@ CC_get_error(ConnectionClass *self, int *number, char **message) * 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements. */ QResultClass * -CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL clear_result_on_abort) +CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { QResultClass *result_in = NULL, *cmdres = NULL, *retres = NULL, *res = NULL; + BOOL clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0), + create_keyset = ((flag & CREATE_KEYSET) != 0); char swallow, *wq; int id; @@ -1376,6 +1378,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL clear_resu 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) { @@ -1392,6 +1396,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL clear_resu } if (!used_passed_result_object) { + if (create_keyset) + QR_set_haskeyset(res); if (!QR_fetch_tuples(res, self, qi ? qi->cursor : NULL)) { self->errornumber = CONNECTION_COULD_NOT_RECEIVE; diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h index 408d11836c..ee19d27dcb 100644 --- a/src/interfaces/odbc/connection.h +++ b/src/interfaces/odbc/connection.h @@ -305,7 +305,7 @@ char CC_connect(ConnectionClass *self, char do_password); char CC_add_statement(ConnectionClass *self, StatementClass *stmt); char CC_remove_statement(ConnectionClass *self, StatementClass *stmt); char CC_get_error(ConnectionClass *self, int *number, char **message); -QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL); +QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag); void CC_clear_error(ConnectionClass *self); char *CC_create_errormsg(ConnectionClass *self); int CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs); @@ -316,4 +316,7 @@ 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); +/* CC_send_query_options */ +#define CLEAR_RESULT_ON_ABORT 1L +#define CREATE_KEYSET (1L << 1) /* create keyset for updatable curosrs */ #endif diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c index 6ba6587f64..c0bf5cf221 100644 --- a/src/interfaces/odbc/convert.c +++ b/src/interfaces/odbc/convert.c @@ -1291,6 +1291,7 @@ copy_statement_with_parameters(StatementClass *stmt) #ifdef DRIVER_CURSOR_IMPLEMENT BOOL search_from_pos = FALSE; #endif /* DRIVER_CURSOR_IMPLEMENT */ + Int4 from_pos = -1, where_pos = -1; if (ci->disallow_premature) prepare_dummy_cursor = stmt->pre_executing; @@ -1326,7 +1327,11 @@ copy_statement_with_parameters(StatementClass *stmt) else if (!stmt->ti || stmt->ntab != 1) stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; else - search_from_pos = TRUE; + { + /** search_from_pos = TRUE; **/ + from_pos = stmt->from_pos; + where_pos = stmt->where_pos; + } } #endif /* DRIVER_CURSOR_IMPLEMENT */ @@ -1366,9 +1371,18 @@ copy_statement_with_parameters(StatementClass *stmt) #ifdef MULTIBYTE make_encoded_str(&encstr, conn, old_statement); #endif - for (opos = 0; opos < oldstmtlen; opos++) { + if (from_pos == (Int4) opos) + { + CVT_APPEND_STR(", CTID, OID "); + } + else if (where_pos == (Int4) opos) + { + stmt->load_statement = malloc(npos + 1); + memcpy(stmt->load_statement, new_statement, npos); + stmt->load_statement[npos] = '\0'; + } #ifdef MULTIBYTE oldchar = encoded_byte_check(&encstr, opos); if (ENCODE_STATUS(encstr) != 0) @@ -2033,6 +2047,12 @@ 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'; + } #endif /* DRIVER_CURSOR_IMPLEMENT */ if (prepare_dummy_cursor && SC_is_pre_executable(stmt)) { diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c index 69718a1914..78737fbb33 100644 --- a/src/interfaces/odbc/environ.c +++ b/src/interfaces/odbc/environ.c @@ -245,6 +245,9 @@ PGAPI_StmtError( HSTMT hstmt, case STMT_INVALID_CURSOR_STATE_ERROR: strcpy(szSqlState, "24000"); break; + case STMT_ERROR_IN_ROW: + strcpy(szSqlState, "01S01"); + break; case STMT_OPTION_VALUE_CHANGED: strcpy(szSqlState, "01S02"); break; diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c index ae095e9691..8f05024f45 100644 --- a/src/interfaces/odbc/execute.c +++ b/src/interfaces/odbc/execute.c @@ -94,6 +94,12 @@ PGAPI_Prepare(HSTMT hstmt, if (self->statement) free(self->statement); + if (self->stmt_with_params) + free(self->stmt_with_params); + self->stmt_with_params = NULL; + if (self->load_statement) + free(self->load_statement); + self->load_statement = NULL; self->statement = make_string(szSqlStr, cbSqlStr, NULL); if (!self->statement) @@ -141,6 +147,12 @@ PGAPI_ExecDirect( if (stmt->statement) free(stmt->statement); + if (stmt->stmt_with_params) + free(stmt->stmt_with_params); + stmt->stmt_with_params = NULL; + if (stmt->load_statement) + free(stmt->load_statement); + stmt->load_statement = NULL; /* * keep a copy of the un-parametrized statement, in case they try to @@ -421,7 +433,7 @@ next_param_row: BOOL in_trans = CC_is_in_trans(conn); BOOL issued_begin = FALSE, begin_included = FALSE; - QResultClass *res; + QResultClass *res, *curres; if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0) begin_included = TRUE; @@ -436,7 +448,7 @@ next_param_row: } /* we are now in a transaction */ CC_set_in_trans(conn); - res = CC_send_query(conn, stmt->stmt_with_params, NULL, TRUE); + res = CC_send_query(conn, stmt->stmt_with_params, NULL, CLEAR_RESULT_ON_ABORT); if (!res) { CC_abort(conn); @@ -445,6 +457,9 @@ next_param_row: return SQL_ERROR; } SC_set_Result(stmt, res); + for (curres = res; !curres->num_fields; curres = curres->next) + ; + SC_set_Curres(stmt, curres); if (CC_is_in_autocommit(conn)) { if (issued_begin) @@ -518,7 +533,7 @@ PGAPI_Transact( { mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string); - res = CC_send_query(conn, stmt_string, NULL, TRUE); + res = CC_send_query(conn, stmt_string, NULL, CLEAR_RESULT_ON_ABORT); CC_set_no_trans(conn); if (!res) diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index 10a900207c..4d92980331 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -2858,7 +2858,7 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc return ret; if (!conn->server_encoding) { - if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, TRUE), res) + if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, CLEAR_RESULT_ON_ABORT), res) { if (QR_get_num_tuples(res) > 0) conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0)); @@ -2868,11 +2868,11 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc if (!conn->server_encoding) return ret; sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding); - bError = (CC_send_query(conn, query, NULL, TRUE) == NULL); + bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL); if (!bError && continueExec) { sprintf(query, "select OID from pg_class where relname = '%s'", serverTableName); - if (res = CC_send_query(conn, query, NULL, TRUE), res) + if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res) { if (QR_get_num_tuples(res) > 0) strcpy(saveoid, QR_get_value_backend_row(res, 0, 0)); @@ -2891,11 +2891,11 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc } /* restore the client encoding */ sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding); - bError = (CC_send_query(conn, query, NULL, TRUE) == NULL); + bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL); if (bError || !continueExec) return ret; sprintf(query, "select relname from pg_class where OID = %s", saveoid); - if (res = CC_send_query(conn, query, NULL, TRUE), res) + if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res) { if (QR_get_num_tuples(res) > 0) { @@ -2922,7 +2922,7 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se return ret; if (!conn->server_encoding) { - if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, TRUE), res) + if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, CLEAR_RESULT_ON_ABORT), res) { if (QR_get_num_tuples(res) > 0) conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0)); @@ -2932,13 +2932,13 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se if (!conn->server_encoding) return ret; sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding); - bError = (CC_send_query(conn, query, NULL, TRUE) == NULL); + bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL); if (!bError && continueExec) { sprintf(query, "select attrelid, attnum from pg_class, pg_attribute " "where relname = '%s' and attrelid = pg_class.oid " "and attname = '%s'", serverTableName, serverColumnName); - if (res = CC_send_query(conn, query, NULL, TRUE), res) + if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res) { if (QR_get_num_tuples(res) > 0) { @@ -2960,11 +2960,11 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se } /* restore the cleint encoding */ sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding); - bError = (CC_send_query(conn, query, NULL, TRUE) == NULL); + bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL); if (bError || !continueExec) return ret; sprintf(query, "select attname from pg_attribute where attrelid = %s and attnum = %s", saveattrelid, saveattnum); - if (res = CC_send_query(conn, query, NULL, TRUE), res) + if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res) { if (QR_get_num_tuples(res) > 0) { @@ -3790,7 +3790,7 @@ PGAPI_Procedures( " case when prorettype = 0 then 1::int2 else 2::int2 end as " "PROCEDURE_TYPE" " from pg_proc"); my_strcat(proc_query, " where proname like '%.*s'", szProcName, cbProcName); - if (res = CC_send_query(conn, proc_query, NULL, TRUE), !res) + if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res) { stmt->errornumber = STMT_EXEC_ERROR; stmt->errormsg = "PGAPI_Procedures query error"; @@ -3927,7 +3927,7 @@ PGAPI_TablePrivileges( my_strcat(proc_query, " relname like '%.*s' and", esc_table_name, escTbnamelen); } strcat(proc_query, " pg_user.usesysid = relowner"); - if (res = CC_send_query(conn, proc_query, NULL, TRUE), !res) + if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res) { stmt->errornumber = STMT_EXEC_ERROR; stmt->errormsg = "PGAPI_TablePrivileges query error"; @@ -3935,7 +3935,7 @@ PGAPI_TablePrivileges( } strncpy_null(proc_query, "select usename, usesysid, usesuper from pg_user", sizeof(proc_query)); tablecount = QR_get_num_tuples(res); - if (allures = CC_send_query(conn, proc_query, NULL, TRUE), !allures) + if (allures = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !allures) { QR_Destructor(res); stmt->errornumber = STMT_EXEC_ERROR; @@ -3983,7 +3983,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, TRUE)) + if (gres = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT)) { 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 b606f0dc62..414110b893 100644 --- a/src/interfaces/odbc/info30.c +++ b/src/interfaces/odbc/info30.c @@ -47,14 +47,15 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, break; case SQL_KEYSET_CURSOR_ATTRIBUTES1: len = 4; - value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE + value = 0; + if (ci->updatable_cursors || ci->drivers.lie) + value |= (SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | 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; + | SQL_CA1_POS_REFRESH); if (ci->drivers.lie) - value |= - ( SQL_CA1_BULK_ADD + value |= (SQL_CA1_BULK_ADD | SQL_CA1_BULK_UPDATE_BY_BOOKMARK | SQL_CA1_BULK_DELETE_BY_BOOKMARK | SQL_CA1_BULK_FETCH_BY_BOOKMARK @@ -68,13 +69,14 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, break; case SQL_KEYSET_CURSOR_ATTRIBUTES2: len = 4; - value = SQL_CA2_OPT_ROWVER_CONCURRENCY + 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); if (ci->drivers.lie) - value |= - ( SQL_CA2_READ_ONLY_CONCURRENCY + value |= (SQL_CA2_READ_ONLY_CONCURRENCY | SQL_CA2_LOCK_CONCURRENCY | SQL_CA2_OPT_VALUES_CONCURRENCY | SQL_CA2_MAX_ROWS_SELECT @@ -95,16 +97,19 @@ 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_POS_UPDATE | SQL_CA1_POS_DELETE - | SQL_CA1_POS_REFRESH; + | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION; + 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 = SQL_CA2_OPT_ROWVER_CONCURRENCY + value = 0; + if (ci->updatable_cursors) + value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY | 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/multibyte.c b/src/interfaces/odbc/multibyte.c index 11d35369dd..2458abbe69 100644 --- a/src/interfaces/odbc/multibyte.c +++ b/src/interfaces/odbc/multibyte.c @@ -300,7 +300,7 @@ CC_lookup_cs_new(ConnectionClass *self) char *encstr = NULL; QResultClass *res; - res = CC_send_query(self, "select pg_client_encoding()", NULL, TRUE); + res = CC_send_query(self, "select pg_client_encoding()", NULL, CLEAR_RESULT_ON_ABORT); if (res) { char *enc = QR_get_value_backend_row(res, 0, 0); diff --git a/src/interfaces/odbc/odbcapi30.c b/src/interfaces/odbc/odbcapi30.c index 6d4ad1f287..c4efdd56e5 100644 --- a/src/interfaces/odbc/odbcapi30.c +++ b/src/interfaces/odbc/odbcapi30.c @@ -419,177 +419,17 @@ SQLSetConnectAttr(HDBC ConnectionHandle, return PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value); } -static RETCODE SQL_API -ARDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, - SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) -{ - RETCODE ret = SQL_SUCCESS; - PTR tptr; - switch (FieldIdentifier) - { - case SQL_DESC_ARRAY_SIZE: - stmt->options.rowset_size = (SQLUINTEGER) Value; - break; - case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.row_operation_ptr = Value; - break; - case SQL_DESC_BIND_OFFSET_PTR: - stmt->options.row_offset_ptr = Value; - break; - case SQL_DESC_BIND_TYPE: - stmt->options.bind_size = (SQLUINTEGER) Value; - break; - - case SQL_DESC_DATA_PTR: - if (!RecNumber) - stmt->bookmark.buffer = Value; - else - stmt->bindings[RecNumber - 1].buffer = Value; - break; - case SQL_DESC_INDICATOR_PTR: - if (!RecNumber) - tptr = stmt->bookmark.used; - else - tptr = stmt->bindings[RecNumber - 1].used; - if (Value != tptr) - { - ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR"; - } - break; - case SQL_DESC_OCTET_LENGTH_PTR: - if (!RecNumber) - stmt->bookmark.used = Value; - else - stmt->bindings[RecNumber - 1].used = Value; - break; - default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - stmt->errormsg = "not implemedted yet"; - } - return ret; -} - -static RETCODE SQL_API -APDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, - SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) -{ - RETCODE ret = SQL_SUCCESS; - switch (FieldIdentifier) - { - case SQL_DESC_ARRAY_SIZE: - stmt->options.paramset_size = (SQLUINTEGER) Value; - break; - case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.param_operation_ptr = Value; - break; - case SQL_DESC_BIND_OFFSET_PTR: - stmt->options.param_offset_ptr = Value; - break; - case SQL_DESC_BIND_TYPE: - stmt->options.param_bind_type = (SQLUINTEGER) 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; - break; - case SQL_DESC_INDICATOR_PTR: - if (stmt->parameters_allocated < RecNumber || - Value != stmt->parameters[RecNumber - 1].used) - { - ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_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; - break; - default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - } - return ret; -} - -static RETCODE SQL_API -IRDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, - SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) -{ - RETCODE ret = SQL_SUCCESS; - switch (FieldIdentifier) - { - case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.rowStatusArray = (SQLUSMALLINT *) Value; - break; - case SQL_DESC_ROWS_PROCESSED_PTR: - stmt->options.rowsFetched = (SQLUINTEGER *) Value; - break; - default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - } - return ret; -} - -static RETCODE SQL_API -IPDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, - SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) -{ - RETCODE ret = SQL_SUCCESS; - switch (FieldIdentifier) - { - case SQL_DESC_ARRAY_STATUS_PTR: - stmt->options.param_status_ptr = (SQLUSMALLINT *) Value; - break; - case SQL_DESC_ROWS_PROCESSED_PTR: - stmt->options.param_processed_ptr = (SQLUINTEGER *) Value; - break; - default:ret = SQL_ERROR; - stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; - } - return ret; -} - /* new function */ RETCODE SQL_API SQLSetDescField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) { - RETCODE ret = SQL_SUCCESS; - HSTMT hstmt; - SQLUINTEGER descType; - StatementClass *stmt; - static const char *func = "SQLSetDescField"; + RETCODE ret; mylog("[[SQLSetDescField]] h=%u rec=%d field=%d val=%x\n", DescriptorHandle, RecNumber, FieldIdentifier, Value); - hstmt = statementHandleFromDescHandle(DescriptorHandle, &descType); - mylog("stmt=%x type=%d\n", hstmt, descType); - stmt = (StatementClass *) hstmt; - switch (descType) - { - case SQL_ATTR_APP_ROW_DESC: - ret = ARDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength); - break; - case SQL_ATTR_APP_PARAM_DESC: - ret = APDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength); - break; - case SQL_ATTR_IMP_ROW_DESC: - ret = IRDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength); - break; - case SQL_ATTR_IMP_PARAM_DESC: - ret = IPDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength); - break; - default:ret = SQL_ERROR; - stmt->errornumber = STMT_INTERNAL_ERROR; - stmt->errormsg = "Error not implemented"; - } - if (ret == SQL_ERROR) - SC_log_error(func, "", stmt); + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, FieldIdentifier, + Value, BufferLength); return ret; } @@ -602,7 +442,7 @@ SQLSetDescRec(SQLHDESC DescriptorHandle, PTR Data, SQLINTEGER *StringLength, SQLINTEGER *Indicator) { - const char *func = "SQLSetDescField"; + const char *func = "SQLSetDescRec"; mylog("[[SQLSetDescRec]]\n"); mylog("Error not implemented\n"); diff --git a/src/interfaces/odbc/options.c b/src/interfaces/odbc/options.c index c8e07700c8..ea11fae76b 100644 --- a/src/interfaces/odbc/options.c +++ b/src/interfaces/odbc/options.c @@ -39,6 +39,7 @@ set_statement_option(ConnectionClass *conn, static char *func = "set_statement_option"; char changed = FALSE; ConnInfo *ci = NULL; + UDWORD setval; if (conn) ci = &(conn->connInfo); @@ -63,22 +64,21 @@ set_statement_option(ConnectionClass *conn, * positioned update isn't supported so cursor concurrency is * read-only */ - mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam); - if (ci->drivers.lie || vParam == SQL_CONCUR_READ_ONLY || vParam == SQL_CONCUR_ROWVER) - { - if (conn) - conn->stmtOptions.scroll_concurrency = vParam; - if (stmt) - stmt->options.scroll_concurrency = vParam; - } - else - { - if (conn) - conn->stmtOptions.scroll_concurrency = SQL_CONCUR_ROWVER; - if (stmt) - stmt->options.scroll_concurrency = SQL_CONCUR_ROWVER; + mylog("SetStmtOption(): SQL_CONCURRENCY = %d ", vParam); + setval = SQL_CONCUR_READ_ONLY; + if (SQL_CONCUR_READ_ONLY == vParam) + ; + if (ci->drivers.lie) + setval = vParam; + else if (ci->updatable_cursors) + setval = SQL_CONCUR_ROWVER; + if (conn) + conn->stmtOptions.scroll_concurrency = setval; + else if (stmt) + stmt->options.scroll_concurrency = setval; + if (setval != vParam) changed = TRUE; - } + mylog("-> %d\n", setval); break; case SQL_CURSOR_TYPE: @@ -87,47 +87,24 @@ set_statement_option(ConnectionClass *conn, * if declare/fetch, then type can only be forward. otherwise, * it can only be forward or static. */ - mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam); - + mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d ", vParam); + setval = SQL_CURSOR_FORWARD_ONLY; if (ci->drivers.lie) - { - if (conn) - conn->stmtOptions.cursor_type = vParam; - if (stmt) - stmt->options.cursor_type = vParam; - } - else - { - if (ci->drivers.use_declarefetch) - { - if (conn) - conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY; - if (stmt) - stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY; + setval = vParam; + else if (ci->drivers.use_declarefetch) + ; + else if (SQL_CURSOR_STATIC == vParam) + setval = vParam; + /** else if (SQL_CURSOR_KEYSET_DRIVEN == vParam && ci->updatable) + setval = vParam; **/ - if (vParam != SQL_CURSOR_FORWARD_ONLY) - changed = TRUE; - } - else - { - if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC) - { - if (conn) - conn->stmtOptions.cursor_type = vParam; /* valid type */ - if (stmt) - stmt->options.cursor_type = vParam; /* valid type */ - } - else - { - if (conn) - conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC; - if (stmt) - stmt->options.cursor_type = SQL_CURSOR_STATIC; - - changed = TRUE; - } - } - } + if (conn) + conn->stmtOptions.cursor_type = setval; + else if (stmt) + stmt->options.cursor_type = setval; + if (setval != vParam) + changed = TRUE; + mylog("-> %d\n", setval); break; case SQL_KEYSET_SIZE: /* ignored, but saved and returned */ diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c index 55bb08bd93..8a7e6d7415 100644 --- a/src/interfaces/odbc/parse.c +++ b/src/interfaces/odbc/parse.c @@ -297,7 +297,6 @@ parse_statement(StatementClass *stmt) in_distinct = FALSE, in_on = FALSE, in_from = FALSE, - from_found = FALSE, in_where = FALSE, in_table = FALSE; char in_field = FALSE, @@ -309,7 +308,6 @@ parse_statement(StatementClass *stmt) i, k = 0, n, - first_where = 0, blevel = 0; FIELD_INFO **fi; TABLE_INFO **ti; @@ -318,6 +316,7 @@ parse_statement(StatementClass *stmt) HSTMT hcol_stmt; StatementClass *col_stmt; RETCODE result; + BOOL updatable = TRUE; mylog("%s: entering...\n", func); @@ -327,6 +326,8 @@ parse_statement(StatementClass *stmt) stmt->nfld = 0; stmt->ntab = 0; + stmt->from_pos = -1; + stmt->where_pos = -1; #ifdef MULTIBYTE while (pptr = ptr, (ptr = getNextToken(conn->ccsc, pptr, token, sizeof(token), &delim, "e, &dquote, &numeric)) != NULL) @@ -343,6 +344,7 @@ parse_statement(StatementClass *stmt) if (!stricmp(token, "distinct")) { in_distinct = TRUE; + updatable = FALSE; mylog("DISTINCT\n"); continue; @@ -359,11 +361,11 @@ parse_statement(StatementClass *stmt) { in_select = FALSE; in_from = TRUE; - if (!from_found && + if (stmt->from_pos < 0 && (!strnicmp(pptr, "from", 4))) { mylog("First "); - from_found = TRUE; + stmt->from_pos = pptr - stmt->statement; } mylog("FROM\n"); @@ -384,9 +386,13 @@ parse_statement(StatementClass *stmt) in_from = FALSE; in_where = TRUE; - if (!first_where && - (!stricmp(token, "where"))) - first_where = ptr - stmt->statement; + if (!stricmp(token, "where")) + { + if (stmt->where_pos < 0) + stmt->where_pos = pptr - stmt->statement; + } + else if (stricmp(token, "order")) + updatable = FALSE; mylog("WHERE...\n"); break; @@ -733,6 +739,10 @@ parse_statement(StatementClass *stmt) */ /* Call SQLColumns for each table and store the result */ + if (stmt->ntab > 1) + updatable = FALSE; + else if (stmt->from_pos < 0) + updatable = FALSE; for (i = 0; i < stmt->ntab; i++) { /* See if already got it */ @@ -828,9 +838,11 @@ parse_statement(StatementClass *stmt) */ for (i = 0; i < stmt->nfld;) { + fi[i]->updatable = updatable; /* Dont worry about functions or quotes */ if (fi[i]->func || fi[i]->quote || fi[i]->numeric) { + fi[i]->updatable = FALSE; i++; continue; } @@ -928,6 +940,7 @@ parse_statement(StatementClass *stmt) mylog("about to copy at %d\n", n + i); getColInfo(the_ti->col_info, fi[n + i], n); + fi[n + i]->updatable = updatable; mylog("done copying\n"); } @@ -945,24 +958,29 @@ parse_statement(StatementClass *stmt) else if (fi[i]->ti) { if (!searchColInfo(fi[i]->ti->col_info, fi[i])) + { parse = FALSE; - + fi[i]->updatable = FALSE; + } i++; } /* Don't know the table -- search all tables in "from" list */ else { - parse = FALSE; for (k = 0; k < stmt->ntab; k++) { if (searchColInfo(ti[k]->col_info, fi[i])) { fi[i]->ti = ti[k]; /* now know the table */ - parse = TRUE; break; } } + if (k >= stmt->ntab) + { + parse = FALSE; + fi[i]->updatable = FALSE; + } i++; } } diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index 8fba1abd30..958a3b6b10 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.59 2002/03/11 10:25:57 inoue Exp $ + * $Id: psqlodbc.h,v 1.60 2002/03/14 05:42:03 inoue Exp $ * */ @@ -166,6 +166,7 @@ typedef struct TupleListClass_ TupleListClass; typedef struct EnvironmentClass_ EnvironmentClass; typedef struct TupleNode_ TupleNode; typedef struct TupleField_ TupleField; +typedef struct KeySet_ KeySet; typedef struct col_info COL_INFO; typedef struct lo_arg LO_ARG; diff --git a/src/interfaces/odbc/psqlodbc_api30.def b/src/interfaces/odbc/psqlodbc_api30.def index bafd2e6d57..a2031c7aba 100755 --- a/src/interfaces/odbc/psqlodbc_api30.def +++ b/src/interfaces/odbc/psqlodbc_api30.def @@ -5,7 +5,7 @@ SQLAllocEnv @2 SQLAllocStmt @3 SQLBindCol @4 SQLCancel @5 -SQLColAttributes @6 +; SQLColAttributes @6 */ SQLConnect @7 SQLDescribeCol @8 SQLDisconnect @9 diff --git a/src/interfaces/odbc/psqlodbc_api30w.def b/src/interfaces/odbc/psqlodbc_api30w.def index 71ea830cb4..ef7cdfdf33 100644 --- a/src/interfaces/odbc/psqlodbc_api30w.def +++ b/src/interfaces/odbc/psqlodbc_api30w.def @@ -5,7 +5,7 @@ SQLAllocEnv @2 SQLAllocStmt @3 SQLBindCol @4 SQLCancel @5 -SQLColAttributes @6 +; SQLColAttributes @6 SQLConnect @7 SQLDescribeCol @8 SQLDisconnect @9 diff --git a/src/interfaces/odbc/qresult.c b/src/interfaces/odbc/qresult.c index 2e462bf5d5..d4445ec1ef 100644 --- a/src/interfaces/odbc/qresult.c +++ b/src/interfaces/odbc/qresult.c @@ -121,6 +121,8 @@ QR_Constructor() rv->cache_size = 0; rv->rowset_size = 1; + rv->haskeyset = 0; + rv->keyset = NULL; } mylog("exit QR_Constructor\n"); @@ -221,6 +223,11 @@ QR_free_memory(QResultClass *self) free(self->backend_tuples); self->backend_tuples = NULL; } + if (self->keyset) + { + free(self->keyset); + self->keyset = NULL; + } self->fcount = 0; @@ -296,6 +303,8 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) 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->haskeyset) + self->keyset = (KeySet *) calloc(sizeof(KeySet), tuple_size); if (!self->backend_tuples) { self->status = PGRES_FATAL_ERROR; @@ -347,7 +356,7 @@ QR_close(QResultClass *self) sprintf(buf, "close %s", self->cursor); mylog("QResult: closing cursor: '%s'\n", buf); - res = CC_send_query(self->conn, buf, NULL, TRUE); + res = CC_send_query(self->conn, buf, NULL, CLEAR_RESULT_ON_ABORT); self->inTuples = FALSE; self->currTuple = -1; @@ -482,6 +491,8 @@ QR_next_tuple(QResultClass *self) 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); self->count_allocated = self->cache_size; } sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor); @@ -492,7 +503,7 @@ QR_next_tuple(QResultClass *self) qi.row_size = self->cache_size; qi.result_in = self; qi.cursor = NULL; - res = CC_send_query(self->conn, fetch, &qi, TRUE); + res = CC_send_query(self->conn, fetch, &qi, CLEAR_RESULT_ON_ABORT); if (res == NULL) { self->status = PGRES_FATAL_ERROR; @@ -552,6 +563,8 @@ QR_next_tuple(QResultClass *self) 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); self->count_allocated = tuple_size; } @@ -626,6 +639,7 @@ QR_read_tuple(QResultClass *self, char binary) { Int2 field_lf; TupleField *this_tuplefield; + KeySet *this_keyset = NULL; char bmp, bitmap[MAX_FIELDS]; /* Max. len of the bitmap */ Int2 bitmaplen; /* len of the bitmap in bytes */ @@ -639,6 +653,11 @@ QR_read_tuple(QResultClass *self, char binary) /* set the current row to read the fields into */ this_tuplefield = self->backend_tuples + (self->fcount * num_fields); + if (self->haskeyset) + { + this_keyset = self->keyset + self->fcount; + this_keyset->status = 0; + } bitmaplen = (Int2) num_fields / BYTELEN; if ((num_fields % BYTELEN) > 0) @@ -709,6 +728,15 @@ 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, "(%u,%hu)", + &this_keyset->blocknum, &this_keyset->offset); + if (this_tuplefield[num_fields - 1].value) + sscanf(this_tuplefield[num_fields - 1].value, "%u", + &this_keyset->oid); + } self->currTuple++; return TRUE; } diff --git a/src/interfaces/odbc/qresult.h b/src/interfaces/odbc/qresult.h index b304fd5d35..dbb6f46901 100644 --- a/src/interfaces/odbc/qresult.h +++ b/src/interfaces/odbc/qresult.h @@ -72,6 +72,9 @@ struct QResultClass_ char inTuples; /* is a fetch of rows from the backend in * progress? */ char aborted; /* was aborted? */ + char haskeyset; /* this result contains keyset ? */ + KeySet *keyset; + }; #define QR_get_fields(self) (self->fields) @@ -102,6 +105,7 @@ struct QResultClass_ #define QR_set_status(self, condition) ( self->status = condition ) #define QR_set_message(self, message_) ( self->message = message_) #define QR_set_aborted(self, aborted_) ( self->aborted = aborted_) +#define QR_set_haskeyset(self) (self->haskeyset = TRUE) #define QR_get_message(self) (self->message) #define QR_get_command(self) (self->command) diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c index 13f223b720..af7c8b30f8 100644 --- a/src/interfaces/odbc/results.c +++ b/src/interfaces/odbc/results.c @@ -160,8 +160,7 @@ PGAPI_NumResultCols( *pccol = QR_NumResultCols(result); /* updatable cursors */ - if (ci->updatable_cursors && - stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY) + if (result->keyset) *pccol -= 2; } @@ -433,7 +432,7 @@ PGAPI_ColAttributes( */ #if (ODBCVER >= 0x0300) - if (0 == icol) /* bookmark column */ + if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */ { switch (fDescType) { @@ -473,7 +472,11 @@ PGAPI_ColAttributes( * Column Count is a special case. The Column number is ignored * in this case. */ +#if (ODBCVER >= 0x0300) + if (fDescType == SQL_DESC_COUNT) +#else if (fDescType == SQL_COLUMN_COUNT) +#endif /* ODBCVER */ { if (pfDesc) *pfDesc = cols; @@ -539,6 +542,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]; } mylog("colAttr: col %d field_type = %d\n", col_idx, field_type); @@ -549,6 +554,7 @@ PGAPI_ColAttributes( value = pgtype_auto_increment(stmt, field_type); if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */ value = FALSE; +inolog("AUTO_INCREMENT=%d\n", value); break; @@ -581,9 +587,8 @@ PGAPI_ColAttributes( #if (ODBCVER >= 0x0300) case SQL_DESC_NAME: -#else - case SQL_COLUMN_NAME: #endif /* ODBCVER */ + case SQL_COLUMN_NAME: 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); @@ -597,14 +602,15 @@ PGAPI_ColAttributes( case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */ value = pgtype_money(stmt, field_type); +inolog("COLUMN_MONEY=%d\n", value); break; #if (ODBCVER >= 0x0300) case SQL_DESC_NULLABLE: -#else - case SQL_COLUMN_NULLABLE: #endif /* ODBCVER */ + case SQL_COLUMN_NULLABLE: value = fi ? fi->nullable : pgtype_nullable(stmt, field_type); +inolog("COLUMN_NULLABLE=%d\n", value); break; case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */ @@ -623,6 +629,7 @@ PGAPI_ColAttributes( case SQL_COLUMN_SCALE: value = pgtype_scale(stmt, field_type, col_idx); +inolog("COLUMN_SCALE=%d\n", value); break; case SQL_COLUMN_SEARCHABLE: /* SQL_DESC_SEARCHABLE */ @@ -637,6 +644,7 @@ PGAPI_ColAttributes( case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */ value = pgtype_to_sqltype(stmt, field_type); +inolog("COLUMN_TYPE=%d\n", value); break; case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */ @@ -658,7 +666,7 @@ PGAPI_ColAttributes( * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY; * else */ - value = SQL_ATTR_WRITE; + value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : SQL_ATTR_READWRITE_UNKNOWN; mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value); break; @@ -1292,9 +1300,17 @@ PGAPI_ExtendedFetch( if (result == SQL_ERROR) *(rgfRowStatus + i) = SQL_ROW_ERROR; #ifdef DRIVER_CURSOR_IMPLEMENT - /* this should be refined */ - else if (result > 10 && result < 20) - *(rgfRowStatus + i) = result - 10; + else if (res->keyset) + { + UWORD pstatus = res->keyset[stmt->currTuple].status & KEYSET_INFO_PUBLIC; + if (pstatus != 0) + { + rgfRowStatus[i] = pstatus; + res->keyset[stmt->currTuple].status &= (~KEYSET_INFO_PUBLIC); + } + else + rgfRowStatus[i] = SQL_ROW_SUCCESS; + } #endif /* DRIVER_CURSOR_IMPLEMENT */ else *(rgfRowStatus + i) = SQL_ROW_SUCCESS; @@ -1347,7 +1363,7 @@ PGAPI_MoreResults( mylog("%s: entering...\n", func); if (stmt && (res = SC_get_Curres(stmt))) - SC_get_Curres(stmt) = res->next; + SC_set_Curres(stmt, res->next); if (SC_get_Curres(stmt)) return SQL_SUCCESS; return SQL_NO_DATA_FOUND; @@ -1358,28 +1374,61 @@ PGAPI_MoreResults( /* * Stuff for updatable cursors. */ +static const char *getOidValue(const QResultClass *res, int index) +{ + return QR_get_value_backend_row(res, index, QR_NumResultCols(res) - 1); +} +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) +{ + 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); +} + static QResultClass * positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval) { - int i; QResultClass *qres; - char selstr[4096]; + char *selstr; + UInt4 len; - sprintf(selstr, "select"); - for (i = 0; i < res_cols; i++) - sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name); - sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name); + len = strlen(stmt->load_statement); if (tidval) { + len += 100; + selstr = malloc(len); if (latest) - sprintf(selstr, "%s ctid = currtid2('%s', '%s') and", - selstr, stmt->ti[0]->name, tidval); - else - sprintf(selstr, "%s ctid = '%s' and", selstr, tidval); + sprintf(selstr, "%s where ctid = currtid2('%s', '%s') and oid = %u", stmt->load_statement, stmt->ti[0]->name, tidval, oid); + else + sprintf(selstr, "%s where ctid = '%s' and oid = %u", stmt->load_statement, tidval, oid); } - sprintf(selstr, "%s oid = %u", selstr, oid), - mylog("selstr=%s\n", selstr); - qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, TRUE); + else + { + len += 20; + selstr = malloc(len); + sprintf(selstr, "%s where oid = %u", stmt->load_statement, oid); + } + + mylog("selstr=%s\n", selstr); + qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, CLEAR_RESULT_ON_ABORT); +free(selstr); return qres; } @@ -1388,14 +1437,12 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) { int i, res_cols; - UWORD rcnt, - global_ridx; - UInt4 oid; + UWORD rcnt, global_ridx, offset; + UInt4 oid, blocknum; QResultClass *res, *qres; RETCODE ret = SQL_ERROR; - char *tidval, - *oidval; + char tidval[32]; mylog("positioned load fi=%x ti=%x\n", stmt->fi, stmt->ti); rcnt = 0; @@ -1412,15 +1459,18 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) } global_ridx = irow + stmt->rowset_start; res_cols = QR_NumResultCols(res); - if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1))) + 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 = QR_get_value_backend_row(res, global_ridx, res_cols - 2); + tidval = getTidValue(res, global_ridx);*/ res_cols -= 2; if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres) { - TupleField *tupleo, - *tuplen; + TupleField *tupleo, *tuplen; rcnt = QR_get_num_tuples(qres); tupleo = res->backend_tuples + res->num_fields * global_ridx; @@ -1437,6 +1487,13 @@ 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 @@ -1450,6 +1507,7 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) free(tupleo[res_cols + 1].value); tupleo[res_cols + 1].value = NULL; tupleo[res_cols + 1].len = 0; + res->keyset[global_ridx].status |= SQL_ROW_DELETED; } } QR_Destructor(qres); @@ -1481,8 +1539,7 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) } if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres) { - TupleField *tupleo, - *tuplen; + TupleField *tupleo, *tuplen; int count = QR_get_num_tuples(qres); QR_set_position(qres, 0); @@ -1507,6 +1564,8 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) QR_Destructor(qres); return SQL_ERROR; } + if (res->haskeyset) + res->keyset = (KeySet *) realloc(res->keyset, sizeof(KeySet) * tuple_size); res->count_allocated = tuple_size; } tupleo = res->backend_tuples + res->num_fields * res->fcount; @@ -1517,6 +1576,7 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) tupleo[i].value = tuplen[i].value; tuplen[i].value = NULL; } + KeySetSet(res, res->fcount); res->fcount++; ret = SQL_SUCCESS; } @@ -1533,12 +1593,12 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval) } static RETCODE SQL_API -irow_update(RETCODE ret, StatementClass *stmt, UWORD irow) +irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow) { if (ret != SQL_ERROR) { int updcnt; - const char *cmdstr = QR_get_command(SC_get_Curres(stmt)); + const char *cmdstr = QR_get_command(SC_get_Curres(ustmt)); if (cmdstr && sscanf(cmdstr, "UPDATE %d", &updcnt) == 1) @@ -1580,9 +1640,8 @@ SC_pos_update(StatementClass *stmt, BindInfoClass *bindings = stmt->bindings; char updstr[4096]; RETCODE ret; - char *tidval, - *oidval; - UInt4 offset; + UInt4 oid, offset, blocknum; + UInt2 pgoffset; Int4 *used; mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, SC_get_Curres(stmt)->base, stmt->fi, stmt->ti); @@ -1597,12 +1656,14 @@ SC_pos_update(StatementClass *stmt, } global_ridx = irow + stmt->rowset_start; res_cols = QR_NumResultCols(res); - if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1))) + /*if (!(oidval = getOidValue(res, global_ridx)))*/ + if (!(oid = getOid(res, global_ridx))) { stmt->errormsg = "The row is already deleted"; return SQL_ERROR; } - tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2); + /*tidval = getTidValue(res, global_ridx);*/ + getTid(res, global_ridx, &blocknum, &pgoffset); sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name); num_cols = stmt->nfld; @@ -1635,8 +1696,10 @@ SC_pos_update(StatementClass *stmt, int res_cols = QR_NumResultCols(res); StatementClass *qstmt; - sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr, - tidval, oidval); + /*sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr, + tidval, oidval);*/ + sprintf(updstr, "%s where ctid = '(%u, %u)' and oid = %u", updstr, + blocknum, pgoffset, oid); mylog("updstr=%s\n", updstr); if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS) return SQL_ERROR; @@ -1676,11 +1739,13 @@ SC_pos_update(StatementClass *stmt, stmt->errormsg = "SetPos with data_at_exec not yet supported"; ret = SQL_ERROR; } - ret = irow_update(ret, qstmt, irow); + ret = irow_update(ret, stmt, qstmt, irow); PGAPI_FreeStmt(hstmt, SQL_DROP); } else ret = SQL_SUCCESS_WITH_INFO; + if (SQL_SUCCESS == ret && res->keyset) + res->keyset[global_ridx].status |= (SQL_ROW_UPDATED | DRV_SELF_UPDATED); #if (ODBCVER >= 0x0300) if (stmt->options.rowStatusArray) { @@ -1689,9 +1754,8 @@ SC_pos_update(StatementClass *stmt, case SQL_SUCCESS: stmt->options.rowStatusArray[irow] = SQL_ROW_UPDATED; break; - case SQL_SUCCESS_WITH_INFO: - stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO; - break; + default: + stmt->options.rowStatusArray[irow] = ret; } } #endif /* ODBCVER */ @@ -1703,13 +1767,13 @@ SC_pos_delete(StatementClass *stmt, UWORD irow) { int res_cols; - UWORD global_ridx; - QResultClass *res, - *qres; + UWORD global_ridx, offset; + QResultClass *res, *qres; BindInfoClass *bindings = stmt->bindings; char dltstr[4096]; RETCODE ret; - char *oidval; + /*const char *oidval;*/ + UInt4 oid, blocknum; mylog("POS DELETE fi=%x ti=%x\n", stmt->fi, stmt->ti); if (!(res = SC_get_Curres(stmt))) @@ -1723,18 +1787,20 @@ SC_pos_delete(StatementClass *stmt, } res_cols = QR_NumResultCols(res); global_ridx = irow + stmt->rowset_start; - if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1))) + /* if (!(oidval = getOidValue(res, global_ridx)))*/ + if (!(oid = getOid(res, global_ridx))) { stmt->errormsg = "The row is already deleted"; return SQL_ERROR; } - sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s", - stmt->ti[0]->name, - QR_get_value_backend_row(SC_get_Curres(stmt), global_ridx, res_cols - 2), - oidval); + getTid(res, global_ridx, &blocknum, &offset); + /*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",*/ + sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)' and oid = %u", + stmt->ti[0]->name, blocknum, offset, oid); mylog("dltstr=%s\n", dltstr); - qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL, TRUE); + qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL, CLEAR_RESULT_ON_ABORT); + ret = SQL_SUCCESS; if (qres && QR_command_successful(qres)) { int dltcnt; @@ -1769,6 +1835,8 @@ 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 (ODBCVER >= 0x0300) if (stmt->options.rowStatusArray) { @@ -1777,9 +1845,8 @@ SC_pos_delete(StatementClass *stmt, case SQL_SUCCESS: stmt->options.rowStatusArray[irow] = SQL_ROW_DELETED; break; - case SQL_SUCCESS_WITH_INFO: - stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO; - break; + default: + stmt->options.rowStatusArray[irow] = ret; } } #endif /* ODBCVER */ @@ -1787,13 +1854,13 @@ SC_pos_delete(StatementClass *stmt, } static RETCODE SQL_API -irow_insert(RETCODE ret, StatementClass *stmt, int addpos) +irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt, int addpos) { if (ret != SQL_ERROR) { int addcnt; UInt4 oid; - const char *cmdstr = QR_get_command(SC_get_Curres(stmt)); + const char *cmdstr = QR_get_command(SC_get_Curres(istmt)); if (cmdstr && sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 && @@ -1802,12 +1869,14 @@ irow_insert(RETCODE ret, StatementClass *stmt, int addpos) SC_pos_newload(stmt, oid, NULL); if (stmt->bookmark.buffer) { - char buf[32]; + char buf[32]; + UInt4 offset = stmt->options.row_offset_ptr ? *stmt->options.row_offset_ptr : 0; - sprintf(buf, "%ld", addpos); + sprintf(buf, "%ld", addpos + 1); copy_and_convert_field(stmt, 0, buf, - SQL_C_ULONG, stmt->bookmark.buffer, - 0, stmt->bookmark.used); + SQL_C_ULONG, stmt->bookmark.buffer + offset, + 0, stmt->bookmark.used ? stmt->bookmark.used + + (offset >> 2) : NULL); } } else @@ -1882,6 +1951,7 @@ SC_pos_add(StatementClass *stmt, } if (add_cols > 0) { + int brow_save; sprintf(addstr, "%s) values (", addstr); for (i = 0; i < add_cols; i++) @@ -1907,11 +1977,16 @@ SC_pos_add(StatementClass *stmt, stmt->errormsg = "SetPos with data_at_exec not yet supported"; ret = SQL_ERROR; } - ret = irow_insert(ret, qstmt, res->fcount); + brow_save = stmt->bind_row; + stmt->bind_row = irow; + ret = irow_insert(ret, stmt, qstmt, res->fcount); + stmt->bind_row = brow_save; } else ret = SQL_SUCCESS_WITH_INFO; PGAPI_FreeStmt(hstmt, SQL_DROP); + if (SQL_SUCCESS == ret && res->keyset) + res->keyset[res->fcount - 1].status |= DRV_SELF_ADDED; #if (ODBCVER >= 0x0300) if (stmt->options.rowStatusArray) { @@ -1920,9 +1995,8 @@ SC_pos_add(StatementClass *stmt, case SQL_SUCCESS: stmt->options.rowStatusArray[irow] = SQL_ROW_ADDED; break; - case SQL_SUCCESS_WITH_INFO: - stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO; - break; + default: + stmt->options.rowStatusArray[irow] = ret; } } #endif /* ODBCVER */ @@ -1947,6 +2021,7 @@ PGAPI_SetPos( UWORD fLock) { static char *func = "PGAPI_SetPos"; + RETCODE ret; StatementClass *stmt = (StatementClass *) hstmt; QResultClass *res; int num_cols, @@ -1960,7 +2035,7 @@ PGAPI_SetPos( } #ifdef DRIVER_CURSOR_IMPLEMENT - mylog("SetPos fOption=%d irow=%d lock=%d currt=%d\n", fOption, irow, fLock, stmt->currTuple); + 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) ; else @@ -1982,12 +2057,40 @@ PGAPI_SetPos( } num_cols = QR_NumResultCols(res); - if (irow == 0) + if (irow == 0) /* bulk operation */ { - stmt->errornumber = STMT_ROW_OUT_OF_RANGE; - stmt->errormsg = "Driver does not support Bulk operations."; - SC_log_error(func, "", stmt); - return SQL_ERROR; + int processed; + + if (SQL_POSITION == fOption) + { + stmt->errornumber = STMT_ROW_OUT_OF_RANGE; + stmt->errormsg = "Bulk Fresh operations not allowed."; + 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; } if (irow > stmt->last_fetch_count) diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index 4bb884dbf8..b33ccc2cc9 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -242,6 +242,7 @@ SC_Constructor(void) rv->statement = NULL; rv->stmt_with_params = NULL; + rv->load_statement = NULL; rv->stmt_size_limit = -1; rv->statement_type = STMT_TYPE_UNKNOWN; @@ -318,6 +319,8 @@ SC_Destructor(StatementClass *self) free(self->stmt_with_params); self->stmt_with_params = NULL; } + if (self->load_statement) + free(self->load_statement); SC_free_params(self, STMT_FREE_PARAMS_ALL); @@ -548,6 +551,12 @@ SC_recycle_statement(StatementClass *self) * SQLParamData/SQLPutData is called. */ SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY); + if (self->stmt_with_params) + free(self->stmt_with_params); + self->stmt_with_params = NULL; + if (self->load_statement) + free(self->load_statement); + self->load_statement = NULL; return TRUE; } @@ -635,12 +644,16 @@ SC_create_errormsg(StatementClass *self) QResultClass *res = SC_get_Curres(self); ConnectionClass *conn = self->hdbc; int pos; + BOOL detailmsg = FALSE; static char msg[4096]; msg[0] = '\0'; if (res && res->message) + { strcpy(msg, res->message); + detailmsg = TRUE; + } else if (self->errormsg) strcpy(msg, self->errormsg); @@ -660,10 +673,10 @@ SC_create_errormsg(StatementClass *self) { SocketClass *sock = conn->sock; - if (conn->errormsg && conn->errormsg[0] != '\0') + if (!detailmsg && conn->errormsg && conn->errormsg[0] != '\0') { pos = strlen(msg); - /*sprintf(&msg[pos], ";\n%s", conn->errormsg);*/ + sprintf(&msg[pos], ";\n%s", conn->errormsg); } if (sock && sock->errormsg && sock->errormsg[0] != '\0') @@ -722,9 +735,6 @@ SC_fetch(StatementClass *self) int retval, result; -#ifdef DRIVER_CURSOR_IMPLEMENT - int updret; -#endif /* DRIVER_CURSOR_IMPLEMENT */ Int2 num_cols, lf; Oid type; @@ -799,20 +809,13 @@ SC_fetch(StatementClass *self) } #ifdef DRIVER_CURSOR_IMPLEMENT - updret = 0; if (self->options.scroll_concurrency != SQL_CONCUR_READ_ONLY) { - if (!QR_get_value_backend_row(res, self->currTuple, num_cols - 1)) - updret = SQL_ROW_DELETED; num_cols -= 2; } #endif /* DRIVER_CURSOR_IMPLEMENT */ if (self->options.retrieve_data == SQL_RD_OFF) /* data isn't required */ -#ifdef DRIVER_CURSOR_IMPLEMENT - return updret ? updret + 10 : SQL_SUCCESS; -#else return SQL_SUCCESS; -#endif /* DRIVER_CURSOR_IMPLEMENT */ 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); @@ -893,10 +896,6 @@ SC_fetch(StatementClass *self) } } -#ifdef DRIVER_CURSOR_IMPLEMENT - if (updret) - result = updret + 10; -#endif /* DRIVER_CURSOR_IMPLEMENT */ return result; } @@ -955,11 +954,12 @@ 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); mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name); /* send the declare/select */ - res = CC_send_query(conn, self->stmt_with_params, NULL, FALSE); + res = CC_send_query(conn, self->stmt_with_params, NULL, qflag); if (SC_is_fetchcursor(self) && res != NULL && QR_command_successful(res)) { @@ -982,7 +982,7 @@ SC_execute(StatementClass *self) */ sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name); - res = CC_send_query(conn, fetch, &qi, FALSE); + res = CC_send_query(conn, fetch, &qi, qflag); } mylog(" done sending the query:\n"); } @@ -990,7 +990,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, FALSE); + res = CC_send_query(conn, self->stmt_with_params, NULL, 0); /* * We shouldn't send COMMIT. Postgres backend does the autocommit diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index cf104aa7d1..74583fbb2d 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -76,6 +76,7 @@ typedef enum #define STMT_BAD_ERROR 27 #define STMT_INVALID_OPTION_IDENTIFIER 28 #define STMT_RETURN_NULL_WITHOUT_INDICATOR 29 +#define STMT_ERROR_IN_ROW 30 /* statement types */ enum @@ -135,6 +136,7 @@ typedef struct 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]; @@ -219,11 +221,15 @@ struct StatementClass_ char miscinfo; SWORD errorpos; SWORD error_recsize; + char *load_statement; /* to (re)load updatable individual rows */ + Int4 from_pos; + Int4 where_pos; }; #define SC_get_conn(a) (a->hdbc) #define SC_set_Result(a, b) (a->result = a->curres = b) #define SC_get_Result(a) (a->result) +#define SC_set_Curres(a, b) (a->curres = b) #define SC_get_Curres(a) (a->curres) /* options for SC_free_params() */ diff --git a/src/interfaces/odbc/tuple.h b/src/interfaces/odbc/tuple.h index fdc1a5f9ea..388f9fa021 100644 --- a/src/interfaces/odbc/tuple.h +++ b/src/interfaces/odbc/tuple.h @@ -30,6 +30,19 @@ struct TupleNode_ TupleField tuple[1]; }; +/* keyset(TID + OID) info */ +struct KeySet_ +{ + UWORD status; + UWORD offset; + 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) + /* These macros are wrappers for the corresponding set_tuplefield functions but these handle automatic NULL determination and call set_tuplefield_null() if appropriate for the datatype (used by SQLGetTypeInfo).