aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-07-09 11:02:26 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2021-07-09 11:02:26 -0400
commit39b6e85f135f7a1dcf43c0551d7d10e8c57b7fce (patch)
tree148e10f54310282dd8ffc389a15f6133a9ebb9bf /src
parent8d48a3436dd83f7d6e3cde2d76f91f49f27ba16d (diff)
downloadpostgresql-39b6e85f135f7a1dcf43c0551d7d10e8c57b7fce.tar.gz
postgresql-39b6e85f135f7a1dcf43c0551d7d10e8c57b7fce.zip
Reject cases where a query in WITH rewrites to just NOTIFY.
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
Diffstat (limited to 'src')
-rw-r--r--src/backend/rewrite/rewriteHandler.c20
-rw-r--r--src/test/regress/expected/with.out25
-rw-r--r--src/test/regress/sql/with.sql21
3 files changed, 63 insertions, 3 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 88a9e95e339..54fd6d6fb28 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3585,15 +3585,29 @@ 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 = linitial_node(Query, newstuff);
+ 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)
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 584bdc6600b..3523a7dcc16 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -2969,6 +2969,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
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index 1a9bdc9f3ea..8b213ee4086 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -1375,6 +1375,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