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
This commit is contained in:
Tom Lane 2021-07-09 11:02:26 -04:00
parent ca2e4472ba
commit a9da1934e9
3 changed files with 63 additions and 3 deletions

View File

@ -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)

View File

@ -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

View File

@ -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