Fix bug with "SET TRANSACTION READ ONLY".
authorTatsuo Ishii <ishii@postgresql.org>
Wed, 2 Dec 2015 12:48:59 +0000 (21:48 +0900)
committerTatsuo Ishii <ishii@postgresql.org>
Wed, 2 Dec 2015 12:48:59 +0000 (21:48 +0900)
Pgpool-II remembers that non read only queries (including SET) were
executed in an explicit transaction and adds a "writing transaction"
mark to the transaction. The mark affects the query routing behavior
of pgpool-II while running in streaming replication mode. Pgpool-II
starts sending queries to the primary after the mark is set. Because
the effect of writing queries may appear on standbys after some delay
in streaming replication mode, it is safer to route read queries to
the primary after the mark is set.

However there's oversight here. "SET TRANSACTION READ ONLY" does no
data modification and should be treated as an exception.

Per bug #157.

pool_proto_modules.c
pool_query_context.c
pool_query_context.h

index 2d13de0c0e5e136da769891a095cdc4fc054eb0f..d6618953d8b56df818c08fc2d058ded2ba2c7619 100644 (file)
@@ -1497,6 +1497,15 @@ POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
                                 */
                                else if (!is_select_query(node, query))
                                {
+                                       /* However, if the query is "SET TRANSACTION READ ONLY" or its variant,
+                                        * don't set it.
+                                        */
+                                       if (!pool_is_transaction_read_only(node))
+                                       {
+                                               pool_debug("not SET TRANSACTION READ ONLY");
+                                               pool_set_writing_transaction();
+                                       }
+
                                        pool_set_writing_transaction();
                                }
                        }
index f3fe0e38bf74988172233f755969f3b7d93ee3b5..c34e2f3709401d7daa3c76d070a60c9ecd5f500e 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL 
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2014     PgPool Global Development Group
+ * Copyright (c) 2003-2015     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -1571,3 +1571,77 @@ char* remove_read_write(int len, const char* contents, int *rewritten_len)
 
        return rewritten_contents;
 }
+
+/*
+ * Return true if one of followings is true
+ *
+ * SET transaction_read_only TO on
+ * SET TRANSACTION READ ONLY
+ * SET TRANSACTION CHARACTERISTICS AS TRANSACTION READ ONLY
+ *
+ * Note that if the node is not a variable statement, returns false.
+ */
+bool pool_is_transaction_read_only(Node *node)
+{
+       ListCell   *list_item;
+       bool ret = false;
+
+       if (!IsA(node, VariableSetStmt))
+               return ret;
+
+       /*
+        * SET transaction_read_only TO on
+        */
+       if (((VariableSetStmt *)node)->kind == VAR_SET_VALUE &&
+               !strcmp(((VariableSetStmt *)node)->name, "transaction_read_only"))
+       {
+               List *options = ((VariableSetStmt *)node)->args;
+               foreach(list_item, options)
+               {
+                       A_Const *v = (A_Const *)lfirst(list_item);
+
+                       switch (v->val.type)
+                       {
+                               case T_String:
+                                       if (!strcasecmp(v->val.val.str, "on") ||
+                                               !strcasecmp(v->val.val.str, "t") ||
+                                               !strcasecmp(v->val.val.str, "true"))
+                                               ret = true;
+                                       break;
+                               case T_Integer:
+                                       if (v->val.val.ival)
+                                               ret = true;
+                               default:
+                                       break;
+                       }
+               }
+       }
+
+       /*
+        * SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY
+        * SET TRANSACTION READ ONLY
+        */
+       else if (((VariableSetStmt *)node)->kind == VAR_SET_MULTI &&
+                        (!strcmp(((VariableSetStmt *)node)->name, "TRANSACTION") ||
+                         !strcmp(((VariableSetStmt *)node)->name, "SESSION CHARACTERISTICS")))
+       {
+               List *options = ((VariableSetStmt *)node)->args;
+               foreach(list_item, options)
+               {
+                       DefElem *opt = (DefElem *) lfirst(list_item);
+
+                       if (!strcmp("transaction_read_only", opt->defname))
+                       {
+                               bool read_only;
+
+                               read_only = ((A_Const *)opt->arg)->val.val.ival;
+                               if (read_only)
+                               {
+                                       ret = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+       return ret;
+}
index 417fbf0f5f11959e60ec081014b13fe19e627cac..469fb42fb00e49e071cfb33aec1f4506ba555078 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL 
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2011     PgPool Global Development Group
+ * Copyright (c) 2003-2015     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -83,5 +83,6 @@ 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);
 extern int statecmp(POOL_QUERY_STATE s1, POOL_QUERY_STATE s2);
+extern bool pool_is_transaction_read_only(Node *node);
 
 #endif /* POOL_QUERY_CONTEXT_H */