diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2021-07-09 11:02:26 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2021-07-09 11:02:26 -0400 |
commit | 39b6e85f135f7a1dcf43c0551d7d10e8c57b7fce (patch) | |
tree | 148e10f54310282dd8ffc389a15f6133a9ebb9bf /src | |
parent | 8d48a3436dd83f7d6e3cde2d76f91f49f27ba16d (diff) | |
download | postgresql-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.c | 20 | ||||
-rw-r--r-- | src/test/regress/expected/with.out | 25 | ||||
-rw-r--r-- | src/test/regress/sql/with.sql | 21 |
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 |