summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi Inoue2017-06-12 12:09:20 +0000
committerHiroshi Inoue2017-06-14 11:50:02 +0000
commit04e2d27c0e918122e42d6757d76f00448f47d555 (patch)
tree87eb494ae6b7fa0ed18959f7c0f462e74339a2a4
parentb41ab52733a46b72bc269725a0638296c6305c52 (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.c89
-rw-r--r--connection.h25
-rw-r--r--execute.c32
-rw-r--r--info.c14
-rw-r--r--parse.c6
-rw-r--r--pgapifunc.h1
-rw-r--r--qresult.c9
-rw-r--r--results.c12
-rw-r--r--statement.c15
-rw-r--r--statement.h15
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
diff --git a/execute.c b/execute.c
index 65d3bb9..df0abc7 100644
--- a/execute.c
+++ b/execute.c
@@ -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);
}
diff --git a/info.c b/info.c
index b1b5f16..d1ac550 100644
--- a/info.c
+++ b/info.c
@@ -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);
diff --git a/parse.c b/parse.c
index 3380c9f..5866c7a 100644
--- a/parse.c
+++ b/parse.c
@@ -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)
diff --git a/qresult.c b/qresult.c
index ff48814..96a8410 100644
--- a/qresult.c
+++ b/qresult.c
@@ -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))
diff --git a/results.c b/results.c
index a120c32..74dd828 100644
--- a/results.c
+++ b/results.c
@@ -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);