Fix bugs with data modifying WITH clause reported in bug#153.
authorTatsuo Ishii <ishii@postgresql.org>
Wed, 14 Oct 2015 03:09:42 +0000 (12:09 +0900)
committerTatsuo Ishii <ishii@postgresql.org>
Wed, 14 Oct 2015 03:09:42 +0000 (12:09 +0900)
pgpool-II does not recognize the data modifying WITH clause despite
pgpool-II 3.3 or after has SQL parser which recognizes it. The bug has
been fixed in pgpool-II 3.5 (having PostgreSQL 9.5 parser).

Data modifying WITH clause was introduced in PostgreSQL 9.1. Since
pgpool-II 3.3 has 9.2 parser and pgpool-II 3.4 has 9.4 parser, the fix
is back ported to pgpool-II 3.3 and 3.4.

pool_process_query.c
pool_query_context.c
test/regression/tests/064.bug153/test.sh [new file with mode: 0755]

index a23b0de64d7da7941b7f972079453580fbc2e870..9250a50d2ebbc41635eb2b715373bdf8788ff994 100644 (file)
@@ -1502,6 +1502,19 @@ int is_select_query(Node *node, char *sql)
                if (select_stmt->intoClause || select_stmt->lockingClause)
                        return 0;
 
+               /* non-SELECT query in WITH clause ? */
+               if (select_stmt->withClause)
+               {
+                       List *ctes = select_stmt->withClause->ctes;
+                       ListCell   *cte_item;
+                       foreach(cte_item, ctes)
+                       {
+                               CommonTableExpr *cte = (CommonTableExpr *)lfirst(cte_item);
+                               if (!IsA(cte->ctequery, SelectStmt))
+                                       return false;
+                       }
+               }
+
                /* '\0' and ';' signify empty query */
                return (*sql == 's' || *sql == 'S' || *sql == '(' ||
                                *sql == 'w' || *sql == 'W' || *sql == 't' || *sql == 'T' ||
index 305b191ca6ae067168566537bccaa6f0a2428903..54c0e840f9a8d5be191db160f186426f797aeb77 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
@@ -1080,6 +1080,19 @@ static POOL_DEST send_to_where(Node *node, char *query)
                        if (pool_has_insertinto_or_locking_clause(node))
                                return POOL_PRIMARY;
 
+                       /* non-SELECT query in WITH clause ? */
+                       if (((SelectStmt *)node)->withClause)
+                       {
+                               List *ctes = ((SelectStmt *)node)->withClause->ctes;
+                               ListCell   *cte_item;
+                               foreach(cte_item, ctes)
+                               {
+                                       CommonTableExpr *cte = (CommonTableExpr *)lfirst(cte_item);
+                                       if (!IsA(cte->ctequery, SelectStmt))
+                                               return POOL_PRIMARY;
+                               }
+                       }
+
                        return POOL_EITHER;
                }
 
diff --git a/test/regression/tests/064.bug153/test.sh b/test/regression/tests/064.bug153/test.sh
new file mode 100755 (executable)
index 0000000..7ee9aed
--- /dev/null
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+#-------------------------------------------------------------------
+# Test script for bug reported in bug#153
+# CTE queries including DMLs are incorrectly handled.
+
+source $TESTLIBS
+TESTDIR=testdir
+PSQL=$PGBIN/psql
+
+for mode in s r
+do
+       rm -fr $TESTDIR
+       mkdir $TESTDIR
+       cd $TESTDIR
+
+# create test environment
+       echo -n "creating test environment..."
+       $PGPOOL_SETUP -m $mode -n 2 || exit 1
+       echo "done."
+
+       source ./bashrc.ports
+
+       echo "backend_weight0 = 0" >> etc/pgpool.conf
+       echo "backend_weight1 = 1" >> etc/pgpool.conf
+
+       ./startall
+
+       export PGPORT=$PGPOOL_PORT
+
+       wait_for_pgpool_startup
+
+       $PSQL test <<EOF
+CREATE TABLE foo(x serial);
+-- Below should be executed on the primary node in streaming replicatoion mode.
+-- Below should be executed on both nodes in native replicatoion mode.
+WITH r AS (INSERT INTO foo VALUES (default) RETURNING x) SELECT x FROM r;
+
+-- Below should be executed on the primary node in streaming replicatoion mode.
+-- Below should be executed on both nodes in native replicatoion mode.
+WITH t AS (SELECT nextval('foo_x_seq')) SELECT * FROM t;
+
+-- Below should be executed on node 1.
+WITH s AS (SELECT 1) SELECT * FROM s;
+EOF
+
+# check if read query is load balanced.
+       fgrep "SELECT 1" log/pgpool.log |grep "DB node id: 1">/dev/null 2>&1
+       if [ $? != 0 ];then
+       # expected result not found
+               echo 'fail: WITH s AS (SELECT 1) SELECT * FROM s;'
+               ./shutdownall
+               exit 1
+       fi
+
+       if [ $mode = "r" ];then
+           fgrep "SELECT 1" log/pgpool.log |grep "DB node id: 0">/dev/null 2>&1
+           if [ $? = 0 ];then
+               # expected result not found
+               echo 'fail: WITH s AS (SELECT 1) SELECT * FROM s;'
+               ./shutdownall
+               exit 1
+           fi
+       fi
+
+       echo ok: read query load balance works.
+
+# check if data modifying WITH worked
+       fgrep "WITH r AS" log/pgpool.log |grep "DB node id: 0">/dev/null 2>&1
+       if [ $? != 0 ];then
+       # expected result not found
+               echo 'fail: data modifying WITH.'
+               ./shutdownall
+               exit 1
+       fi
+
+       if [ $mode = "r" ];then
+           fgrep "WITH r AS" log/pgpool.log |grep "DB node id: 1">/dev/null 2>&1
+           if [ $? != 0 ];then
+               # expected result not found
+               echo 'fail: data modifying WITH in replication mode.'
+               ./shutdownall
+               exit 1
+           fi
+       fi
+
+# check if WITH SELECT including data modifying function call worked.
+       fgrep "WITH t AS" log/pgpool.log |grep "DB node id: 0">/dev/null 2>&1
+       if [ $? != 0 ];then
+       # expected result not found
+               echo 'fail: WITH SELECT including data modifying function call.'
+               ./shutdownall
+               exit 1
+       fi
+
+       if [ $mode = "r" ];then
+           fgrep "WITH t AS" log/pgpool.log |grep "DB node id: 1">/dev/null 2>&1
+           if [ $? != 0 ];then
+               # expected result not found
+               echo 'fail: WITH SELECT including data modifying function call.'
+               ./shutdownall
+               exit 1
+           fi
+       fi
+       ./shutdownall
+
+       cd ..
+done
+
+exit 0