diff options
| author | Hiroshi Inoue | 2017-06-12 12:09:20 +0000 |
|---|---|---|
| committer | Hiroshi Inoue | 2017-06-14 11:50:02 +0000 |
| commit | 04e2d27c0e918122e42d6757d76f00448f47d555 (patch) | |
| tree | 87eb494ae6b7fa0ed18959f7c0f462e74339a2a4 | |
| parent | b41ab52733a46b72bc269725a0638296c6305c52 (diff) | |
Reduce SAVEPOINT calls especially in use declare/fetch mode by omitting SAVEPOINT calls after read-only statements(queries).
Note that DECLARE command is not thought to be read-only because the cursor disappears when rolled back to a savepoint established before the DECLARE command.
| -rw-r--r-- | connection.c | 89 | ||||
| -rw-r--r-- | connection.h | 25 | ||||
| -rw-r--r-- | execute.c | 32 | ||||
| -rw-r--r-- | info.c | 14 | ||||
| -rw-r--r-- | parse.c | 6 | ||||
| -rw-r--r-- | pgapifunc.h | 1 | ||||
| -rw-r--r-- | qresult.c | 9 | ||||
| -rw-r--r-- | results.c | 12 | ||||
| -rw-r--r-- | statement.c | 15 | ||||
| -rw-r--r-- | statement.h | 15 |
10 files changed, 137 insertions, 81 deletions
diff --git a/connection.c b/connection.c index c72f94d..e2ae5ed 100644 --- a/connection.c +++ b/connection.c @@ -1022,7 +1022,7 @@ LIBPQ_CC_connect(ConnectionClass *self, char *salt_para) if (ret = LIBPQ_connect(self), ret <= 0) return ret; - res = CC_send_query(self, "SET DateStyle = 'ISO';SET extra_float_digits = 2;" ISOLATION_SHOW_QUERY, NULL, 0, NULL); + res = CC_send_query(self, "SET DateStyle = 'ISO';SET extra_float_digits = 2;" ISOLATION_SHOW_QUERY, NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res)) { handle_show_results(res); @@ -1242,7 +1242,7 @@ int CC_get_max_idlen(ConnectionClass *self) { QResultClass *res; - res = CC_send_query(self, "show max_identifier_length", NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + res = CC_send_query(self, "show max_identifier_length", NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res)) len = self->max_identifier_length = QR_get_value_backend_int(res, 0, 0, FALSE); QR_Destructor(res); @@ -1302,7 +1302,7 @@ SQLUINTEGER CC_get_isolation(ConnectionClass *self) SQLUINTEGER isolation = 0; QResultClass *res; - res = CC_send_query(self, ISOLATION_SHOW_QUERY, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + res = CC_send_query(self, ISOLATION_SHOW_QUERY, NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res)) { handle_show_results(res); @@ -1430,7 +1430,7 @@ static void CC_clear_cursors(ConnectionClass *self, BOOL on_abort) { SPRINTF_FIXED(cmd, "MOVE 0 in \"%s\"", QR_get_cursor(res)); CONNLOCK_RELEASE(self); - wres = CC_send_query(self, cmd, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + wres = CC_send_query(self, cmd, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN | READ_ONLY_QUERY, NULL); QR_set_no_survival_check(res); if (QR_command_maybe_successful(wres)) QR_set_permanent(res); @@ -1476,7 +1476,7 @@ void CC_on_commit(ConnectionClass *conn) CC_set_no_trans(conn); CC_set_no_manual_trans(conn); } - conn->internal_svp = conn->is_in_internal_op = 0; + CC_svp_init(conn); CC_start_stmt(conn); CC_clear_cursors(conn, FALSE); CONNLOCK_RELEASE(conn); @@ -1508,7 +1508,7 @@ mylog("CC_on_abort in opt=%x\n", opt); set_no_trans = TRUE; } } - conn->internal_svp = conn->is_in_internal_op = 0; + CC_svp_init(conn); CC_start_stmt(conn); CC_clear_cursors(conn, TRUE); if (0 != (opt & CONN_DEAD)) @@ -1542,11 +1542,13 @@ void CC_on_abort_partial(ConnectionClass *conn) { mylog("CC_on_abort_partial in\n"); CONNLOCK_ACQUIRE(conn); - if (!conn->is_in_internal_op) /* manually rolling back to */ + if (!conn->internal_op) /* manually rolling back to */ { conn->internal_svp = 0; /* possibly an internal savepoint is invalid */ mylog(" %s:reset an internal savepoint\n", __FUNCTION__); + conn->opt_previous = 0; /* unknown */ } + CC_init_opt_in_progress(conn); ProcessRollback(conn, TRUE, TRUE); CC_discard_marked_objects(conn); CONNLOCK_RELEASE(conn); @@ -1623,7 +1625,7 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD BOOL ignore_abort_on_conn = ((flag & IGNORE_ABORT_ON_CONN) != 0), create_keyset = ((flag & CREATE_KEYSET) != 0), issue_begin = ((flag & GO_INTO_TRANSACTION) != 0 && !CC_is_in_trans(self)), - rollback_on_error, query_rollback, end_with_commit; + rollback_on_error, query_rollback, end_with_commit, read_only; char *ptr; BOOL ReadyToReturn = FALSE, @@ -1694,6 +1696,7 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD rollback_on_error = (flag & ROLLBACK_ON_ERROR) != 0; end_with_commit = (flag & END_WITH_COMMIT) != 0; + read_only = (flag & READ_ONLY_QUERY) != 0; #define return DONT_CALL_RETURN_FROM_HERE??? consider_rollback = (issue_begin || (CC_is_in_trans(self) && !CC_is_in_error_trans(self)) || strnicmp(query, "begin", 5) == 0); if (rollback_on_error) @@ -1704,9 +1707,13 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD if (stmt) { StatementClass *astmt = SC_get_ancestor(stmt); + unsigned int svpopt = 0; + + if (read_only) + svpopt |= SVPOPT_RDONLY; if (!CC_started_rbpoint(self)) { - if (SQL_ERROR == SetStatementSvp(astmt)) + if (SQL_ERROR == SetStatementSvp(astmt, svpopt)) { SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal savepoint error", func); goto cleanup; @@ -1722,9 +1729,7 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD + strlen(svpcmd) + 1 + strlen(per_query_svp) + 1 + query_len + (appendq ? (1 + strlen(appendq)) : 0) -#ifdef _RELEASE_INTERNAL_SAVEPOINT + 1 + strlen(rlscmd) + strlen(per_query_svp) -#endif /* _RELEASE_INTERNAL_SAVEPOINT */ + 1; query_buf = malloc(query_buf_len); if (!query_buf) @@ -1748,12 +1753,10 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD { snprintfcat(query_buf, query_buf_len, ";%s", appendq); } -#ifdef _RELEASE_INTERNAL_SAVEPOINT if (query_rollback) { snprintfcat(query_buf, query_buf_len, ";%s %s", rlscmd, per_query_svp); } -#endif /* _RELEASE_INTERNAL_SAVEPOINT */ /* Set up notice receiver */ nrarg.conn = self; @@ -1800,22 +1803,7 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD /* read in the return message from the backend */ cmdbuffer = PQcmdStatus(pgres); mylog("send_query: ok - 'C' - %s\n", cmdbuffer); -#ifdef _RELEASE_INTERNAL_SAVEPOINT - /* - * There are 2 risks to RELEASE an internal savepoint. - * One is to RELEASE the savepoint invalitated - * due to manually issued ROLLBACK or RELEASE. - * Another is to invalitate manual SAVEPOINTs unexpectedly - * by RELEASing the internal savepoint. - */ - if (!self->is_in_internal_op && self->internal_svp) - { - if (0 == stricmp(cmdbuffer, rlscmd) || - 0 == stricmp(cmdbuffer, rbkcmd) || - 0 == stricmp(cmdbuffer, svpcmd)) - self->internal_svp = 0; - } -#endif /* _RELEASE_INTERNAL_SAVEPOINT */ + if (query_completed) /* allow for "show" style notices */ { @@ -1843,6 +1831,13 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD break; /* discard the result */ } } + /* + * There are 2 risks to RELEASE an internal savepoint. + * One is to RELEASE the savepoint invalitated + * due to manually issued ROLLBACK or RELEASE. + * Another is to invalitate manual SAVEPOINTs unexpectedly + * by RELEASing the internal savepoint. + */ else if (strnicmp(cmdbuffer, svpcmd, strlen(svpcmd)) == 0) { if (discard_next_savepoint) @@ -1851,11 +1846,28 @@ inolog("Discarded the first SAVEPOINT\n"); discard_next_savepoint = FALSE; break; /* discard the result */ } + if (SAVEPOINT_IN_PROGRESS == self->internal_op) + { + CC_start_rbpoint(self); + self->internal_op = 0; + break; /* discard the result */ + } + /* Don't release the internal savepoint */ + self->internal_svp = 0; } else if (strnicmp(cmdbuffer, rbkcmd, strlen(rbkcmd)) == 0) { CC_mark_cursors_doubtful(self); CC_set_in_error_trans(self); /* mark the transaction error in case of manual rollback */ + if (!self->internal_op) + self->internal_svp = 0; + } + else if (strnicmp(cmdbuffer, rlscmd, strlen(rlscmd)) == 0) + { + if (SAVEPOINT_IN_PROGRESS == self->internal_op) + break; /* discard the result */ + else /* internal savepoint may become invalid */ + self->internal_svp = 0; } /* * DROP TABLE or ALTER TABLE may change @@ -2027,20 +2039,13 @@ cleanup: if (CC_is_in_error_trans(self)) { char tmpsqlbuf[100]; -#ifdef _RELEASE_INTERNAL_SAVEPOINT + SPRINTF_FIXED(tmpsqlbuf, "%s TO %s" ";%s %s" , rbkcmd, per_query_svp , rlscmd, per_query_svp); -#else - SPRINTF_FIXED(tmpsqlbuf, - "%s TO %s" - , rbkcmd, per_query_svp); -#endif /* _RELEASE_INTERNAL_SAVEPOINT */ - self->is_in_internal_op = 1; pgres = PQexec(self->pqconn, tmpsqlbuf); - self->is_in_internal_op = 0; /* * Though it's not appropriate to * call PQExec for a multiple command, @@ -2366,7 +2371,7 @@ CC_lookup_lo(ConnectionClass *self) mylog("%s: entering...\n", func); res = CC_send_query(self, "select oid, typbasetype from pg_type where typname = '" PG_TYPE_LO_NAME "'", - NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL); + NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res) && QR_get_num_cached_tuples(res) > 0) { OID basetype; @@ -2430,7 +2435,7 @@ CC_get_current_schema(ConnectionClass *conn) { QResultClass *res; - if (res = CC_send_query(conn, "select current_schema()", NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL), QR_command_maybe_successful(res)) + if (res = CC_send_query(conn, "select current_schema()", NULL, READ_ONLY_QUERY, NULL), QR_command_maybe_successful(res)) { if (QR_get_num_total_tuples(res) == 1) { @@ -2478,7 +2483,7 @@ int CC_discard_marked_objects(ConnectionClass *conn) SPRINTF_FIXED(cmd, "DEALLOCATE \"%s\"", pname + 1); else SPRINTF_FIXED(cmd, "CLOSE \"%s\"", pname + 1); - res = CC_send_query(conn, cmd, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + res = CC_send_query(conn, cmd, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN | READ_ONLY_QUERY, NULL); QR_Destructor(res); free(conn->discardp[i]); conn->num_discardp--; @@ -3338,9 +3343,9 @@ CC_set_transact(ConnectionClass *self, UInt4 isolation) if (self->default_isolation == 0) bShow = TRUE; if (bShow) - res = CC_send_query_append(self, ISOLATION_SHOW_QUERY, NULL, 0, NULL, query); + res = CC_send_query_append(self, ISOLATION_SHOW_QUERY, NULL, READ_ONLY_QUERY, NULL, query); else - res = CC_send_query(self, query, NULL, 0, NULL); + res = CC_send_query(self, query, NULL, READ_ONLY_QUERY, NULL); if (!QR_command_maybe_successful(res)) { CC_set_error(self, CONN_EXEC_ERROR, "ISOLATION change request to the server error", __FUNCTION__); diff --git a/connection.h b/connection.h index 5bc0f71..37f04e2 100644 --- a/connection.h +++ b/connection.h @@ -311,9 +311,11 @@ struct ConnectionClass_ * unknown */ /* for per statement rollback */ char internal_svp; /* is set? */ - char is_in_internal_op; /* an operation as to internal savepoint in progress */ + char internal_op; /* operation being executed as to internal savepoint */ unsigned char lock_CC_for_rb; unsigned char rbonerr; + unsigned char opt_in_progress; + unsigned char opt_previous; char *original_client_encoding; char *locale_encoding; @@ -388,7 +390,7 @@ enum { #define CC_is_rb_stmt(a) (((a)->rbonerr & (1L << 2)) != 0) #define CC_set_accessed_db(a) ((a)->rbonerr |= (1L << 3)) #define CC_accessed_db(a) (((a)->rbonerr & (1L << 3)) != 0) -#define CC_start_rbpoint(a) ((a)->rbonerr |= (1L << 4)) +#define CC_start_rbpoint(a) ((a)->rbonerr |= (1L << 4), (a)->internal_svp = 1) #define CC_started_rbpoint(a) (((a)->rbonerr & (1L << 4)) != 0) /* prototypes */ @@ -456,13 +458,32 @@ enum { ,GO_INTO_TRANSACTION = (1L << 2) /* issue BEGIN in advance */ ,ROLLBACK_ON_ERROR = (1L << 3) /* rollback the query when an error occurs */ ,END_WITH_COMMIT = (1L << 4) /* the query ends with COMMIT command */ + ,READ_ONLY_QUERY = (1L << 5) /* the query is read-only */ }; /* CC_on_abort options */ #define NO_TRANS 1L #define CONN_DEAD (1L << 1) /* connection is no longer valid */ +/* + * internal savepoint related + */ + #define _RELEASE_INTERNAL_SAVEPOINT +/* Operations about internal savepoint */ +enum { + SAVEPOINT_IN_PROGRESS = 1 + ,ROLLBACK_IN_PROGRESS = 2 +}; +/* StatementSvp entry option */ +enum { + SVPOPT_RDONLY = 1L +}; +#define INIT_SVPOPT (SVPOPT_RDONLY) +#define CC_svp_init(a) ((a)->internal_svp = (a)->internal_op = 0, (a)->opt_in_progress = (a)->opt_previous = INIT_SVPOPT) +#define CC_init_opt_in_progress(a) ((a)->opt_in_progress = INIT_SVPOPT) +#define CC_init_opt_previous(a) ((a)->opt_previous = INIT_SVPOPT) + #ifdef __cplusplus } #endif @@ -168,6 +168,8 @@ inolog("a2\n"); if (0 != (flag & PODBC_WITH_HOLD)) SC_set_with_hold(stmt); + if (0 != (flag & PODBC_RDONLY)) + SC_set_readonly(stmt); /* * If an SQLPrepare was performed prior to this, but was left in the @@ -588,7 +590,7 @@ inolog("%s:%p->external=%d\n", func, stmt, stmt->external); * invokes a transaction. */ RETCODE -SetStatementSvp(StatementClass *stmt) +SetStatementSvp(StatementClass *stmt, unsigned int option) { CSTR func = "SetStatementSvp"; char esavepoint[32], cmd[64]; @@ -609,13 +611,16 @@ SetStatementSvp(StatementClass *stmt) ENTER_CONN_CS(conn); conn->lock_CC_for_rb++; } +inolog(" !!!! %s:%p->accessed=%d opt=%u in_progress=%u prev=%u\n", __FUNCTION__, conn, CC_accessed_db(conn), option, conn->opt_in_progress, conn->opt_previous); + conn->opt_in_progress &= option; switch (stmt->statement_type) { case STMT_TYPE_SPECIAL: case STMT_TYPE_TRANSACTION: return ret; } - if (!CC_started_rbpoint(conn)) + /* If rbpoint is not yet started and the previous statement was not read-only */ + if (!CC_started_rbpoint(conn) && 0 == (conn->opt_previous & SVPOPT_RDONLY)) { BOOL need_savep = FALSE; @@ -638,13 +643,13 @@ SetStatementSvp(StatementClass *stmt) conn->internal_svp = 1; SPRINTFCAT_FIXED(cmd, "SAVEPOINT %s", GetSvpName(conn, esavepoint, sizeof(esavepoint))); conn->internal_svp = internal_svp; - conn->is_in_internal_op = 1; + conn->internal_op = SAVEPOINT_IN_PROGRESS; res = CC_send_query(conn, cmd, NULL, 0, NULL); - conn->is_in_internal_op = 0; + conn->internal_op = 0; if (QR_command_maybe_successful(res)) { - conn->internal_svp = 1; - CC_start_rbpoint(conn); + // conn->internal_svp = 1; + // CC_start_rbpoint(conn); ret = SQL_SUCCESS; } else @@ -654,8 +659,8 @@ SetStatementSvp(StatementClass *stmt) } QR_Destructor(res); } - CC_set_accessed_db(conn); } + CC_set_accessed_db(conn); inolog("%s:%p->accessed=%d\n", func, conn, CC_accessed_db(conn)); return ret; } @@ -671,6 +676,8 @@ DiscardStatementSvp(StatementClass *stmt, RETCODE ret, BOOL errorOnly) inolog("%s:%p->accessed=%d is_in=%d is_rb=%d is_tc=%d\n", func, conn, CC_accessed_db(conn), CC_is_in_trans(conn), SC_is_rb_stmt(stmt), SC_is_tc_stmt(stmt)); + if (conn->lock_CC_for_rb > 0) + mylog("%s:in_progress=%u previous=%d\n", func, conn->opt_in_progress, conn->opt_previous); switch (ret) { case SQL_NEED_DATA: @@ -694,9 +701,9 @@ CC_is_in_trans(conn), SC_is_rb_stmt(stmt), SC_is_tc_stmt(stmt)); char esavepoint[32]; SPRINTF_FIXED(cmd, "ROLLBACK to %s", GetSvpName(conn, esavepoint, sizeof(esavepoint))); - conn->is_in_internal_op = 1; + conn->internal_op = ROLLBACK_IN_PROGRESS; res = CC_send_query(conn, cmd, NULL, IGNORE_ABORT_ON_CONN, NULL); - conn->is_in_internal_op = 0; + conn->internal_op = 0; cmd_success = QR_command_maybe_successful(res); QR_Destructor(res); if (!cmd_success) @@ -722,10 +729,17 @@ cleanup: #endif if (start_stmt || SQL_ERROR == ret) { + stmt->execinfo = 0; + if (SQL_ERROR != ret && CC_accessed_db(conn)) + { + conn->opt_previous = conn->opt_in_progress; + CC_init_opt_in_progress(conn); + } while (conn->lock_CC_for_rb > 0) { LEAVE_CONN_CS(conn); conn->lock_CC_for_rb--; + inolog(" %s:release conn_lock\n", __FUNCTION__); } CC_start_stmt(conn); } @@ -1894,7 +1894,7 @@ retry_public_schema: else STRCAT_FIXED(tables_query, " and n.oid = relnamespace order by nspname, relname"); - result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, 0); + result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, PODBC_RDONLY); if (!SQL_SUCCEEDED(result)) { SC_full_error_copy(stmt, htbl_stmt, FALSE); @@ -2288,7 +2288,7 @@ retry_public_schema: mylog("%s: hcol_stmt = %p, col_stmt = %p\n", func, hcol_stmt, col_stmt); - result = PGAPI_ExecDirect(hcol_stmt, (SQLCHAR *) columns_query, SQL_NTS, 0); + result = PGAPI_ExecDirect(hcol_stmt, (SQLCHAR *) columns_query, SQL_NTS, PODBC_RDONLY); if (!SQL_SUCCEEDED(result)) { SC_full_error_copy(stmt, col_stmt, FALSE); @@ -2776,7 +2776,7 @@ retry_public_schema: mylog("%s: hcol_stmt = %p, col_stmt = %p\n", func, hcol_stmt, col_stmt); - result = PGAPI_ExecDirect(hcol_stmt, (SQLCHAR *) columns_query, SQL_NTS, 0); + result = PGAPI_ExecDirect(hcol_stmt, (SQLCHAR *) columns_query, SQL_NTS, PODBC_RDONLY); if (!SQL_SUCCEEDED(result)) { SC_full_error_copy(stmt, col_stmt, FALSE); @@ -3160,7 +3160,7 @@ PGAPI_Statistics(HSTMT hstmt, STRCAT_FIXED(index_query, " i.indisprimary desc,"); STRCAT_FIXED(index_query, " i.indisunique, n.nspname, c.relname"); - result = PGAPI_ExecDirect(hindx_stmt, (SQLCHAR *) index_query, SQL_NTS, 0); + result = PGAPI_ExecDirect(hindx_stmt, (SQLCHAR *) index_query, SQL_NTS, PODBC_RDONLY); if (!SQL_SUCCEEDED(result)) { /* @@ -3762,7 +3762,7 @@ retry_public_schema: } mylog("%s: tables_query='%s'\n", func, tables_query); - result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, 0); + result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, PODBC_RDONLY); if (!SQL_SUCCEEDED(result)) { SC_full_error_copy(stmt, tbl_stmt, FALSE); @@ -4141,7 +4141,7 @@ PGAPI_ForeignKeys_old(HSTMT hstmt, eq_string, escFkTableName, eq_string, escSchemaName); free(escSchemaName); - result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, 0); + result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, PODBC_RDONLY); if (!SQL_SUCCEEDED(result)) { @@ -4469,7 +4469,7 @@ PGAPI_ForeignKeys_old(HSTMT hstmt, eq_string, escPkTableName, eq_string, escSchemaName); free(escSchemaName); - result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, 0); + result = PGAPI_ExecDirect(htbl_stmt, (SQLCHAR *) tables_query, SQL_NTS, PODBC_RDONLY); if (!SQL_SUCCEEDED(result)) { SC_error_copy(stmt, tbl_stmt, TRUE); @@ -394,7 +394,7 @@ static BOOL CheckHasOids(StatementClass * stmt) SPRINTF_FIXED(query, "select relhasoids, c.oid, relhassubclass from pg_class c, pg_namespace n where relname = '%s' and nspname = '%s' and c.relnamespace = n.oid", SAFE_NAME(ti->table_name), SAFE_NAME(ti->schema_name)); - res = CC_send_query(conn, query, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + res = CC_send_query(conn, query, NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res)) { stmt->num_key_fields = PG_NUM_NORMAL_KEYS; @@ -433,7 +433,7 @@ static BOOL CheckHasOids(StatementClass * stmt) if (!hasoids && !hassubclass) { SPRINTF_FIXED(query, "select a.attname, a.atttypid from pg_index i, pg_attribute a where indrelid=%u and indnatts=1 and indisunique and indexprs is null and indpred is null and i.indrelid = a.attrelid and a.attnum=i.indkey[0] and attnotnull and atttypid in (%d, %d)", ti->table_oid, PG_TYPE_INT4, PG_TYPE_OID); - res = CC_send_query(conn, query, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + res = CC_send_query(conn, query, NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res) && QR_get_num_total_tuples(res) > 0) { foundKey = TRUE; @@ -772,7 +772,7 @@ COL_INFO **coli) "select nspname from pg_namespace n, pg_class c" " where c.relnamespace=n.oid and c.oid='\"%s\"'::regclass", SAFE_NAME(table_name)); - res = CC_send_query(conn, token, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + res = CC_send_query(conn, token, NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res)) { if (QR_get_num_total_tuples(res) == 1) diff --git a/pgapifunc.h b/pgapifunc.h index 9902a6a..8b311f7 100644 --- a/pgapifunc.h +++ b/pgapifunc.h @@ -24,6 +24,7 @@ extern "C" { #define PODBC_INHERIT_CONNECT_OPTIONS (1L << 1) /* Internal flags for PGAPI_Exec... functions */ #define PODBC_WITH_HOLD 1L +#define PODBC_RDONLY (1L << 1) /* Flags for the error handling */ #define PODBC_ALLOW_PARTIAL_EXTRACT 1L #define PODBC_ERROR_CLEAR (1L << 1) @@ -739,8 +739,9 @@ QR_close(QResultClass *self) UDWORD flag = 0; char buf[64]; + flag = READ_ONLY_QUERY; if (QR_needs_survival_check(self)) - flag = ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN; + flag |= (ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN); SPRINTF_FIXED(buf, "close \"%s\"", QR_get_cursor(self)); /* End the transaction if there are no cursors left on this conn */ @@ -907,7 +908,7 @@ SQLLEN QR_move_cursor_to_last(QResultClass *self, StatementClass *stmt) return 0; SPRINTF_FIXED(movecmd, "move all in \"%s\"", QR_get_cursor(self)); - res = CC_send_query(conn, movecmd, NULL, 0, stmt); + res = CC_send_query(conn, movecmd, NULL, READ_ONLY_QUERY, stmt); if (!QR_command_maybe_successful(res)) { QR_Destructor(res); @@ -1006,7 +1007,7 @@ inolog("cache=%d rowset=%d movement=" FORMAT_ULEN "\n", self->cache_size, req_si QR_get_cursor(self)); movement = INT_MAX; } - mres = CC_send_query(conn, movecmd, NULL, 0, stmt); + mres = CC_send_query(conn, movecmd, NULL, READ_ONLY_QUERY, stmt); if (!QR_command_maybe_successful(mres)) { QR_Destructor(mres); @@ -1158,7 +1159,7 @@ inolog("clear obsolete %d tuples\n", num_backend_rows); qi.fetch_size = fetch_size; qi.result_in = self; qi.cursor = NULL; - res = CC_send_query(conn, fetch, &qi, 0, stmt); + res = CC_send_query(conn, fetch, &qi, READ_ONLY_QUERY, stmt); if (!QR_command_maybe_successful(res)) { if (!QR_get_message(self)) @@ -2177,7 +2177,7 @@ ti_quote(StatementClass *stmt, OID tableoid) char *ret = ""; SPRINTF_FIXED(query, "select relname, nspname from pg_class c, pg_namespace n where c.oid=%u and c.relnamespace=n.oid", tableoid); - res = CC_send_query(SC_get_conn(stmt), query, NULL, 0, stmt); + res = CC_send_query(SC_get_conn(stmt), query, NULL, READ_ONLY_QUERY, stmt); if (QR_command_maybe_successful(res) && QR_get_num_cached_tuples(res) == 1) { @@ -2210,7 +2210,7 @@ static BOOL tupleExists(StatementClass *stmt, const KeySet *keyset) SPRINTFCAT_FIXED(selstr, " and "); SPRINTFCAT_FIXED(selstr, bestqual, keyset->oid); } - res = CC_send_query(SC_get_conn(stmt), selstr, NULL, 0, NULL); + res = CC_send_query(SC_get_conn(stmt), selstr, NULL, READ_ONLY_QUERY, NULL); if (QR_command_maybe_successful(res) && 1 == res->num_cached_rows) ret = TRUE; QR_Destructor(res); @@ -3094,7 +3094,7 @@ inolog("%s bestitem=%s bestqual=%s\n", func, SAFE_NAME(ti->bestitem), SAFE_NAME( } mylog("selstr=%s\n", selstr); - qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, 0, stmt); + qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, READ_ONLY_QUERY, stmt); cleanup: free(selstr); return qres; @@ -3311,7 +3311,7 @@ static SQLLEN LoadFromKeyset(StatementClass *stmt, QResultClass * res, int rows_ QResultClass *qres; strlcat(qval, ")", allen); - qres = CC_send_query(conn, qval, NULL, CREATE_KEYSET, stmt); + qres = CC_send_query(conn, qval, NULL, CREATE_KEYSET | READ_ONLY_QUERY, stmt); if (QR_command_maybe_successful(qres)) { SQLLEN j, k, l; @@ -3404,7 +3404,7 @@ static SQLLEN LoadFromKeyset(StatementClass *stmt, QResultClass * res, int rows_ snprintfcat(qval, allen, ",$%d", j + 1); } strlcat(qval, ")", allen); - qres = CC_send_query(conn, qval, NULL, 0, stmt); + qres = CC_send_query(conn, qval, NULL, READ_ONLY_QUERY, stmt); if (QR_command_maybe_successful(qres)) { res->reload_count = keys_per_fetch; @@ -3486,7 +3486,7 @@ mylog(" %s in rows_per_fetch=%d limitrow=%d\n", __FUNCTION__, rows_per_fetch, li QResultClass *qres; strlcat(qval, ")", allen); - qres = CC_send_query(conn, qval, NULL, CREATE_KEYSET, stmt); + qres = CC_send_query(conn, qval, NULL, CREATE_KEYSET | READ_ONLY_QUERY, stmt); if (QR_command_maybe_successful(qres)) { SQLLEN k, l; diff --git a/statement.c b/statement.c index da326ff..9d2f7f5 100644 --- a/statement.c +++ b/statement.c @@ -445,6 +445,7 @@ SC_Constructor(ConnectionClass *conn) rv, SQL_ATTR_IMP_PARAM_DESC); rv->miscinfo = 0; + rv->execinfo = 0; rv->rb_or_tc = 0; SC_reset_updatable(rv); rv->diag_row_count = 0; @@ -853,6 +854,7 @@ inolog("SC_clear_parse_status\n"); if (SC_get_Result(self)) SC_set_Result(self, NULL); self->miscinfo = 0; + self->execinfo = 0; /* self->rbonerr = 0; Never clear the bits here */ /* @@ -1085,6 +1087,7 @@ SC_describe(StatementClass *self) mylog(" preprocess: status = READY\n"); self->miscinfo = 0; + self->execinfo = 0; decideHowToPrepare(self, FALSE); switch (SC_get_prepare_method(self)) @@ -1932,6 +1935,7 @@ SC_execute(StatementClass *self) qi.fetch_size, SC_cursor_name(self)); qryi = &qi; appendq = fetch; + qflag &= (~READ_ONLY_QUERY); /* must be a SAVEPOINT after DECLARE */ } res = SC_get_Result(self); if (self->curr_param_result && res) @@ -2154,7 +2158,7 @@ inolog("!!! numfield=%d field_type=%u\n", QR_NumResultCols(res), QR_get_field_ty qi.cursor = SC_cursor_name(self); qi.cache_size = qi.row_size = ci->drivers.fetch_max; SPRINTF_FIXED(fetch, "fetch " FORMAT_LEN " in \"%s\"", qi.fetch_size, SC_cursor_name(self)); - res = CC_send_query(conn, fetch, &qi, qflag, SC_get_ancestor(self)); + res = CC_send_query(conn, fetch, &qi, qflag | READ_ONLY_QUERY, SC_get_ancestor(self)); if (NULL != res) SC_set_Result(self, res); } @@ -2346,6 +2350,7 @@ static BOOL RequestStart(StatementClass *stmt, ConnectionClass *conn, const char *func) { BOOL ret = TRUE; + unsigned int svpopt = 0; #ifdef _HANDLE_ENLIST_IN_DTC_ if (conn->asdum) @@ -2358,7 +2363,9 @@ RequestStart(StatementClass *stmt, ConnectionClass *conn, const char *func) } if (CC_started_rbpoint(conn)) return TRUE; - if (SQL_ERROR == SetStatementSvp(stmt)) + if (SC_is_readonly(stmt)) + svpopt |= SVPOPT_RDONLY; + if (SQL_ERROR == SetStatementSvp(stmt, svpopt)) { char emsg[128]; @@ -2403,14 +2410,16 @@ libpq_bind_and_exec(StatementClass *stmt) if (!RequestStart(stmt, conn, func)) return NULL; +#ifdef NOT_USED if (CC_is_in_trans(conn) && !CC_started_rbpoint(conn)) { - if (SQL_ERROR == SetStatementSvp(stmt)) + if (SQL_ERROR == SetStatementSvp(stmt, 0)) { SC_set_error_if_not_set(stmt, STMT_INTERNAL_ERROR, "internal savepoint error in build_libpq_bind_params", func); return NULL; } } +#endif /* NOT_USED */ /* 1. Bind */ mylog("%s: bind stmt=%p\n", func, stmt); diff --git a/statement.h b/statement.h index 9da7712..e936455 100644 --- a/statement.h +++ b/statement.h @@ -281,6 +281,7 @@ struct StatementClass_ SQLLEN exec_current_row; unsigned char miscinfo; + unsigned char execinfo; po_ind_t updatable; SQLLEN diag_row_count; char *load_statement; /* to (re)load updatable individual rows */ @@ -424,10 +425,14 @@ enum #define SC_set_fetchcursor(a) ((a)->miscinfo |= (1L << 1)) #define SC_no_fetchcursor(a) ((a)->miscinfo &= ~(1L << 1)) #define SC_is_fetchcursor(a) (((a)->miscinfo & (1L << 1)) != 0) -#define SC_set_with_hold(a) ((a)->miscinfo |= (1L << 3)) -#define SC_set_without_hold(a) ((a)->miscinfo &= ~(1L << 3)) -#define SC_is_with_hold(a) (((a)->miscinfo & (1L << 3)) != 0) -#define SC_miscinfo_clear(a) ((a)->miscinfo &= (1L << 3)) +#define SC_miscinfo_clear(a) ((a)->miscinfo = 0) +#define SC_set_with_hold(a) ((a)->execinfo |= 1L) +#define SC_set_without_hold(a) ((a)->execinfo &= (~1L)) +#define SC_is_with_hold(a) (((a)->execinfo & 1L) != 0) +#define SC_set_readonly(a) ((a)->execinfo |= (1L << 1)) +#define SC_set_no_readonly(a) ((a)->execinfo &= ~(1L << 1)) +#define SC_is_readonly(a) (((a)->execinfo & (1L << 1)) != 0) +#define SC_execinfo_clear(a) (((a)->execinfo = 0) #define STMT_HAS_OUTER_JOIN 1L #define STMT_HAS_INNER_JOIN (1L << 1) #define SC_has_join(a) (0 != (a)->join_info) @@ -533,7 +538,7 @@ int enqueueNeedDataCallback(StatementClass *self, NeedDataCallfunc, void *); RETCODE dequeueNeedDataCallback(RETCODE, StatementClass *self); void cancelNeedDataState(StatementClass *self); int StartRollbackState(StatementClass *self); -RETCODE SetStatementSvp(StatementClass *self); +RETCODE SetStatementSvp(StatementClass *self, unsigned int option); RETCODE DiscardStatementSvp(StatementClass *self, RETCODE, BOOL errorOnly); QResultClass *ParseAndDescribeWithLibpq(StatementClass *stmt, const char *plan_name, const char *query_p, Int2 num_params, const char *comment, QResultClass *res); |
