]> granicus.if.org Git - postgresql/commitdiff
1) Internal improvements to handle updatable cursors(1st cut).
authorHiroshi Inoue <inoue@tpf.co.jp>
Thu, 14 Mar 2002 05:42:04 +0000 (05:42 +0000)
committerHiroshi Inoue <inoue@tpf.co.jp>
Thu, 14 Mar 2002 05:42:04 +0000 (05:42 +0000)
2) Fix a bug in SQLColAttribute().

20 files changed:
src/interfaces/odbc/connection.c
src/interfaces/odbc/connection.h
src/interfaces/odbc/convert.c
src/interfaces/odbc/environ.c
src/interfaces/odbc/execute.c
src/interfaces/odbc/info.c
src/interfaces/odbc/info30.c
src/interfaces/odbc/multibyte.c
src/interfaces/odbc/odbcapi30.c
src/interfaces/odbc/options.c
src/interfaces/odbc/parse.c
src/interfaces/odbc/psqlodbc.h
src/interfaces/odbc/psqlodbc_api30.def
src/interfaces/odbc/psqlodbc_api30w.def
src/interfaces/odbc/qresult.c
src/interfaces/odbc/qresult.h
src/interfaces/odbc/results.c
src/interfaces/odbc/statement.c
src/interfaces/odbc/statement.h
src/interfaces/odbc/tuple.h

index d176d925d4ada5669e0b2c65ef81de7b073eeda1..898b41ec289ea53ccf41f6fff2aa7172f15eb7e6 100644 (file)
@@ -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;
index 408d11836c5c0def54ee43ae9a26b7bc949a80db..ee19d27dcb784d4338ac44f8c6e92744855a8238 100644 (file)
@@ -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
index 6ba6587f6406e0896a4dc15843d1c748464fb667..c0bf5cf221931975f3aae3a91d205274bf92f18d 100644 (file)
@@ -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))
        {
index 69718a1914974d33ac120ddcd48c905a2cb732c1..78737fbb3320f7309422906a7b523f27a3745135 100644 (file)
@@ -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;
index ae095e9691e2698cf7051537ad80fa4eb0d80def..8f05024f454b02dec68e788db42f5795cfaec28d 100644 (file)
@@ -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)
index 10a900207c61e4e2409386abbaccd0f45d93c321..4d929803311e8975a9a420f1d401cca9b28818a4 100644 (file)
@@ -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] == '{')
index b606f0dc62253bc160e0a1d122a781a488a9f287..414110b893831046a09ded9b331f91720a14f868 100644 (file)
@@ -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:
index 11d35369ddf26f17baa1cdb2a8393697da4b4b84..2458abbe69b4094477f50b34a5bb39183f1b8157 100644 (file)
@@ -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);
index 6d4ad1f287714ebd7a3728be469ae3203dfd93eb..c4efdd56e5286258cfc61f1dbb645f6f4905d792 100644 (file)
@@ -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");
index c8e07700c836a79652a13e51748b7f549a57c907..ea11fae76be54a15f4a62a07290e8278a0382b68 100644 (file)
@@ -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      */
index 55bb08bd93b44ccb661265f590f2375cdb591d84..8a7e6d7415cc378e3647f1c5464d5cf811a0ad04 100644 (file)
@@ -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, &quote, &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++;
                }
        }
index 8fba1abd30d77d064f505779c6e4334bc3f9d92b..958a3b6b102d6832ac37d25e3928eb4bb9e06ef5 100644 (file)
@@ -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;
index bafd2e6d5756f75e08e946307d9dbdbaa36845d2..a2031c7aba60c953d233f8c1e42061e5e7c77f51 100755 (executable)
@@ -5,7 +5,7 @@ SQLAllocEnv @2
 SQLAllocStmt @3
 SQLBindCol @4
 SQLCancel @5
-SQLColAttributes @6
+; SQLColAttributes @6 */
 SQLConnect @7
 SQLDescribeCol @8
 SQLDisconnect @9
index 71ea830cb43343d625888356efdb48cac93206e1..ef7cdfdf33d07da64700f6919b9441f0346286b6 100644 (file)
@@ -5,7 +5,7 @@ SQLAllocEnv @2
 SQLAllocStmt @3
 SQLBindCol @4
 SQLCancel @5
-SQLColAttributes @6
+SQLColAttributes @6
 SQLConnect @7
 SQLDescribeCol @8
 SQLDisconnect @9
index 2e462bf5d5bec69e27529fbf32ab10a9605f57ed..d4445ec1ef0ae8aa8cac77cc3a04c32d955a9fc8 100644 (file)
@@ -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;
 }
index b304fd5d351d718925c646927b3db8464d118a39..dbb6f46901eba8853bebf9826d3e90a2f3ad4592 100644 (file)
@@ -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)
index 13f223b7205fbd1d315639bbe544728242ea7774..af7c8b30f8b6b219fc18aa3c2fc35258eae4eb5d 100644 (file)
@@ -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)
index 4bb884dbf8fff890540c0234692c080234be189d..b33ccc2cc951ec7a51537e4f8272c36c324513c6 100644 (file)
@@ -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
index cf104aa7d1314b1b88dde1058a4cf86bc218a569..74583fbb2da6bb767ec51836348670a2fcde8370 100644 (file)
@@ -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() */
index fdc1a5f9eaab04b5b46989e16250e21672e9e5d4..388f9fa0210fc95323eb1cd6569901e7cdb30406 100644 (file)
@@ -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).