mirror of
https://github.com/postgres/postgres.git
synced 2025-12-19 17:02:53 +03:00
Avoid rewriting data-modifying CTEs more than once.
Formerly, when updating an auto-updatable view, or a relation with rules, if the original query had any data-modifying CTEs, the rewriter would rewrite those CTEs multiple times as RewriteQuery() recursed into the product queries. In most cases that was harmless, because RewriteQuery() is mostly idempotent. However, if the CTE involved updating an always-generated column, it would trigger an error because any subsequent rewrite would appear to be attempting to assign a non-default value to the always-generated column. This could perhaps be fixed by attempting to make RewriteQuery() fully idempotent, but that looks quite tricky to achieve, and would probably be quite fragile, given that more generated-column-type features might be added in the future. Instead, fix by arranging for RewriteQuery() to rewrite each CTE exactly once (by tracking the number of CTEs already rewritten as it recurses). This has the advantage of being simpler and more efficient, but it does make RewriteQuery() dependent on the order in which rewriteRuleAction() joins the CTE lists from the original query and the rule action, so care must be taken if that is ever changed. Reported-by: Bernice Southey <bernice.southey@gmail.com> Author: Bernice Southey <bernice.southey@gmail.com> Author: Dean Rasheed <dean.a.rasheed@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Kirill Reshke <reshkekirill@gmail.com> Discussion: https://postgr.es/m/CAEDh4nyD6MSH9bROhsOsuTqGAv_QceU_GDvN9WcHLtZTCYM1kA@mail.gmail.com Backpatch-through: 14
This commit is contained in:
@@ -2872,6 +2872,47 @@ SELECT * FROM bug6051_3;
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
-- check that recursive CTE processing doesn't rewrite a CTE more than once
|
||||
-- (must not try to expand GENERATED ALWAYS IDENTITY columns more than once)
|
||||
CREATE TEMP TABLE id_alw1 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
CREATE TEMP TABLE id_alw2 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
CREATE TEMP VIEW id_alw2_view AS SELECT * FROM id_alw2;
|
||||
CREATE TEMP TABLE id_alw3 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
CREATE RULE id_alw3_ins AS ON INSERT TO id_alw3 DO INSTEAD
|
||||
WITH t1 AS (INSERT INTO id_alw1 DEFAULT VALUES RETURNING i)
|
||||
INSERT INTO id_alw2_view DEFAULT VALUES RETURNING i;
|
||||
CREATE TEMP VIEW id_alw3_view AS SELECT * FROM id_alw3;
|
||||
CREATE TEMP TABLE id_alw4 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
WITH t4 AS (INSERT INTO id_alw4 DEFAULT VALUES RETURNING i)
|
||||
INSERT INTO id_alw3_view DEFAULT VALUES RETURNING i;
|
||||
i
|
||||
---
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * from id_alw1;
|
||||
i
|
||||
---
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * from id_alw2;
|
||||
i
|
||||
---
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * from id_alw3;
|
||||
i
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
SELECT * from id_alw4;
|
||||
i
|
||||
---
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- check case where CTE reference is removed due to optimization
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT q1 FROM
|
||||
|
||||
@@ -1360,6 +1360,29 @@ COMMIT;
|
||||
|
||||
SELECT * FROM bug6051_3;
|
||||
|
||||
-- check that recursive CTE processing doesn't rewrite a CTE more than once
|
||||
-- (must not try to expand GENERATED ALWAYS IDENTITY columns more than once)
|
||||
CREATE TEMP TABLE id_alw1 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
|
||||
CREATE TEMP TABLE id_alw2 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
CREATE TEMP VIEW id_alw2_view AS SELECT * FROM id_alw2;
|
||||
|
||||
CREATE TEMP TABLE id_alw3 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
CREATE RULE id_alw3_ins AS ON INSERT TO id_alw3 DO INSTEAD
|
||||
WITH t1 AS (INSERT INTO id_alw1 DEFAULT VALUES RETURNING i)
|
||||
INSERT INTO id_alw2_view DEFAULT VALUES RETURNING i;
|
||||
CREATE TEMP VIEW id_alw3_view AS SELECT * FROM id_alw3;
|
||||
|
||||
CREATE TEMP TABLE id_alw4 (i int GENERATED ALWAYS AS IDENTITY);
|
||||
|
||||
WITH t4 AS (INSERT INTO id_alw4 DEFAULT VALUES RETURNING i)
|
||||
INSERT INTO id_alw3_view DEFAULT VALUES RETURNING i;
|
||||
|
||||
SELECT * from id_alw1;
|
||||
SELECT * from id_alw2;
|
||||
SELECT * from id_alw3;
|
||||
SELECT * from id_alw4;
|
||||
|
||||
-- check case where CTE reference is removed due to optimization
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
SELECT q1 FROM
|
||||
|
||||
Reference in New Issue
Block a user