pgpool2.git / summary / log / commit / refs
commit 8bd0baeb9c0ca737eca6bd0e77b87e4c51c2fd39
Author: Tatsuo Ishii <ishii@postgresql.org>
Date: Thu Jul 12 01:39:53 2012 +0000
Fix bug with treatment of BEGIN TRANSACTION in master/slave
mode. Original complain is [pgpool-general: 714]. From 3.1, pgpool-II
sends BEGIN.. to all DB nodes. Of course we cannot send BEGIN
TRANSACTION READ WRITE to standby nodes. Problem is, we did not check
BEGIN WORK ISOLATION LEVEL SERIALIZABLE; and sent to standby nodes.
Of course this is wrong, since it's not allowed to run transactions in
serializable mode on standby nodes. So I added check for BEGIN WORK
ISOLATION LEVEL SERIALIZABLE case, with some refactoring because
there's lots of redundant codes.
pool_proto_modules.c | 9 +++----
pool_query_context.c | 69 +++++++++++++++++++++++++++++++++++++++++++---------
pool_query_context.h | 2 ++
3 files changed, 64 insertions(+), 16 deletions(-)
diff --git a/pool_proto_modules.c b/pool_proto_modules.c
index a6356d936..de986c908 100644
--- a/pool_proto_modules.c
+++ b/pool_proto_modules.c
@@ -545,14 +545,13 @@ POOL_STATUS SimpleQuery(POOL_CONNECTION *frontend,
rewrite_query = rewrite_timestamp(backend, query_context->parse_tree, false, msg);
/*
- * If the query is BEGIN READ WRITE in master/slave mode,
- * we send BEGIN instead of it to slaves/standbys.
+ * If the query is BEGIN READ WRITE or
+ * BEGIN ... SERIALIZABLE in master/slave mode,
+ * we send BEGIN to slaves/standbys instead.
* original_query which is BEGIN READ WRITE is sent to primary.
* rewritten_query which is BEGIN is sent to standbys.
*/
- if (is_start_transaction_query(query_context->parse_tree) &&
- is_read_write((TransactionStmt *)query_context->parse_tree) &&
- MASTER_SLAVE)
+ if (pool_need_to_treat_as_if_default_transaction(query_context))
{
rewrite_query = pstrdup("BEGIN");
}
diff --git a/pool_query_context.c b/pool_query_context.c
index a2c3c277c..45f6c968c 100644
--- a/pool_query_context.c
+++ b/pool_query_context.c
@@ -574,12 +574,13 @@ POOL_STATUS pool_send_and_wait(POOL_QUERY_CONTEXT *query_context,
string = NULL;
/*
- * If the query is BEGIN READ WRITE in master/slave mode,
- * we send BEGIN instead of it to slaves/standbys.
+ * If the query is BEGIN READ WRITE or
+ * BEGIN ... SERIALIZABLE in master/slave mode,
+ * we send BEGIN to slaves/standbys instead.
+ * original_query which is BEGIN READ WRITE is sent to primary.
+ * rewritten_query which is BEGIN is sent to standbys.
*/
- if (is_start_transaction_query(query_context->parse_tree) &&
- is_read_write((TransactionStmt *)query_context->parse_tree) &&
- MASTER_SLAVE)
+ if (pool_need_to_treat_as_if_default_transaction(query_context))
{
is_begin_read_write = true;
}
@@ -738,12 +739,13 @@ POOL_STATUS pool_extended_send_and_wait(POOL_QUERY_CONTEXT *query_context,
rewritten_begin = NULL;
/*
- * If the query is BEGIN READ WRITE in master/slave mode,
- * we send BEGIN instead of it to slaves/standbys.
+ * If the query is BEGIN READ WRITE or
+ * BEGIN ... SERIALIZABLE in master/slave mode,
+ * we send BEGIN to slaves/standbys instead.
+ * original_query which is BEGIN READ WRITE is sent to primary.
+ * rewritten_query which is BEGIN is sent to standbys.
*/
- if (is_start_transaction_query(query_context->parse_tree) &&
- is_read_write((TransactionStmt *)query_context->parse_tree) &&
- MASTER_SLAVE)
+ if (pool_need_to_treat_as_if_default_transaction(query_context))
{
is_begin_read_write = true;
@@ -1372,14 +1374,59 @@ bool is_read_write(TransactionStmt *node)
bool read_only;
read_only = ((A_Const *)opt->arg)->val.val.ival;
- if (!read_only)
+ if (read_only)
+ return false; /* TRANSACTION READ ONLY */
+ else
+ /*
+ * TRANSACTION READ WRITE specified. This sounds a little bit strange,
+ * but actually the parse code works in the way.
+ */
return true;
}
}
+ /*
+ * No TRANSACTION READ ONLY/READ WRITE clause specified.
+ */
return false;
}
+/*
+ * Return true if start transaction query with "SERIALIZABLE" option.
+ */
+bool is_serializable(TransactionStmt *node)
+{
+ ListCell *list_item;
+
+ List *options = node->options;
+ foreach(list_item, options)
+ {
+ DefElem *opt = (DefElem *) lfirst(list_item);
+
+ if (!strcmp("transaction_isolation", opt->defname) &&
+ IsA(opt->arg, A_Const) &&
+ ((A_Const *)opt->arg)->val.type == T_String &&
+ !strcmp("serializable", ((A_Const *)opt->arg)->val.val.str))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * If the query is BEGIN READ WRITE or
+ * BEGIN ... SERIALIZABLE in master/slave mode,
+ * we send BEGIN to slaves/standbys instead.
+ * original_query which is BEGIN READ WRITE is sent to primary.
+ * rewritten_query which is BEGIN is sent to standbys.
+ */
+bool pool_need_to_treat_as_if_default_transaction(POOL_QUERY_CONTEXT *query_context)
+{
+ return (MASTER_SLAVE &&
+ is_start_transaction_query(query_context->parse_tree) &&
+ (is_read_write((TransactionStmt *)query_context->parse_tree) ||
+ is_serializable((TransactionStmt *)query_context->parse_tree)));
+}
+
/*
* Return true if the query is SAVEPOINT related query.
*/
diff --git a/pool_query_context.h b/pool_query_context.h
index c48db973a..eab1f673c 100644
--- a/pool_query_context.h
+++ b/pool_query_context.h
@@ -89,6 +89,8 @@ extern char *pool_get_query_string(void);
extern bool is_set_transaction_serializable(Node *node, char *query);
extern bool is_start_transaction_query(Node *node);
extern bool is_read_write(TransactionStmt *node);
+extern bool is_serializable(TransactionStmt *node);
+extern bool pool_need_to_treat_as_if_default_transaction(POOL_QUERY_CONTEXT *query_context);
extern bool is_savepoint_query(Node *node);
extern bool is_2pc_transaction_query(Node *node);
extern void pool_set_query_state(POOL_QUERY_CONTEXT *query_context, POOL_QUERY_STATE state);
[parent: a36819d8a23b]