Avoid rewriting data-modifying CTEs more than once.
authorDean Rasheed <[email protected]>
Sat, 29 Nov 2025 12:28:59 +0000 (12:28 +0000)
committerDean Rasheed <[email protected]>
Sat, 29 Nov 2025 12:28:59 +0000 (12:28 +0000)
commit3881561d7715647dbb4a5bc27f116504903daf1b
treeea144706a996505f4a76dc8e58e8d293d0443a3a
parent87c6f8b047d5b0790e6f8b8532f4adf58dc60f67
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 <[email protected]>
Author: Bernice Southey <[email protected]>
Author: Dean Rasheed <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Reviewed-by: Kirill Reshke <[email protected]>
Discussion: https://postgr.es/m/CAEDh4nyD6MSH9bROhsOsuTqGAv_QceU_GDvN9WcHLtZTCYM1kA@mail.gmail.com
Backpatch-through: 14
src/backend/rewrite/rewriteHandler.c
src/test/regress/expected/with.out
src/test/regress/sql/with.sql