Reject cases where a query in WITH rewrites to just NOTIFY.
authorTom Lane <[email protected]>
Fri, 9 Jul 2021 15:02:26 +0000 (11:02 -0400)
committerTom Lane <[email protected]>
Fri, 9 Jul 2021 15:02:26 +0000 (11:02 -0400)
Since the executor can't cope with a utility statement appearing
as a node of a plan tree, we can't support cases where a rewrite
rule inserts a NOTIFY into an INSERT/UPDATE/DELETE command appearing
in a WITH clause of a larger query.  (One can imagine ways around
that, but it'd be a new feature not a bug fix, and so far there's
been no demand for it.)  RewriteQuery checked for this, but it
missed the case where the DML command rewrites to *only* a NOTIFY.
That'd lead to crashes later on in planning.  Add the missed check,
and improve the level of testing of this area.

Per bug #17094 from Yaoguang Chen.  It's been busted since WITH
was introduced, so back-patch to all supported branches.

Discussion: https://postgr.es/m/17094-bf15dff55eaf2e28@postgresql.org

src/backend/rewrite/rewriteHandler.c
src/test/regress/expected/with.out
src/test/regress/sql/with.sql

index fd63dd7a3bdc728e8213b0c4dd804d53ba431a68..8f3470987abcce5620bdcab73c8903ab6432861d 100644 (file)
@@ -3366,16 +3366,30 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 
        /*
         * Currently we can only handle unconditional, single-statement DO
-        * INSTEAD rules correctly; we have to get exactly one Query out of
-        * the rewrite operation to stuff back into the CTE node.
+        * INSTEAD rules correctly; we have to get exactly one non-utility
+        * Query out of the rewrite operation to stuff back into the CTE node.
         */
        if (list_length(newstuff) == 1)
        {
-           /* Push the single Query back into the CTE node */
+           /* Must check it's not a utility command */
            ctequery = (Query *) linitial(newstuff);
            Assert(IsA(ctequery, Query));
+           if (!(ctequery->commandType == CMD_SELECT ||
+                 ctequery->commandType == CMD_UPDATE ||
+                 ctequery->commandType == CMD_INSERT ||
+                 ctequery->commandType == CMD_DELETE))
+           {
+               /*
+                * Currently it could only be NOTIFY; this error message will
+                * need work if we ever allow other utility commands in rules.
+                */
+               ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("DO INSTEAD NOTIFY rules are not supported for data-modifying statements in WITH")));
+           }
            /* WITH queries should never be canSetTag */
            Assert(!ctequery->canSetTag);
+           /* Push the single Query back into the CTE node */
            cte->ctequery = (Node *) ctequery;
        }
        else if (newstuff == NIL)
index 137420d9b7ea25b5361c9c15a23ebc4d2f42fd34..c9cac36089037d8c4364e5730f298dd6aa70bae5 100644 (file)
@@ -2244,6 +2244,31 @@ WITH t AS (
 )
 VALUES(FALSE);
 ERROR:  conditional DO INSTEAD rules are not supported for data-modifying statements in WITH
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTHING;
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
+ERROR:  DO INSTEAD NOTHING rules are not supported for data-modifying statements in WITH
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTIFY foo;
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
+ERROR:  DO INSTEAD NOTIFY rules are not supported for data-modifying statements in WITH
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO ALSO NOTIFY foo;
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
+ERROR:  DO ALSO rules are not supported for data-modifying statements in WITH
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y
+  DO INSTEAD (NOTIFY foo; NOTIFY bar);
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
+ERROR:  multi-statement DO INSTEAD rules are not supported for data-modifying statements in WITH
 DROP RULE y_rule ON y;
 -- check that parser lookahead for WITH doesn't cause any odd behavior
 create table foo (with baz);  -- fail, WITH is a reserved word
index 133ff0b19570c212896647af16a5c6893bcacab9..8bb9817e7ce3107b7df7b3dc54ca0e8d9f707363 100644 (file)
@@ -1013,6 +1013,27 @@ WITH t AS (
    INSERT INTO y VALUES(0)
 )
 VALUES(FALSE);
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTHING;
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTIFY foo;
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO ALSO NOTIFY foo;
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
+CREATE OR REPLACE RULE y_rule AS ON INSERT TO y
+  DO INSTEAD (NOTIFY foo; NOTIFY bar);
+WITH t AS (
+   INSERT INTO y VALUES(0)
+)
+VALUES(FALSE);
 DROP RULE y_rule ON y;
 
 -- check that parser lookahead for WITH doesn't cause any odd behavior