diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index dfbcaa2cdcc..7d5bdb9a7d9 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -14,13 +14,16 @@ */ #include "postgres.h" +#include "access/heapam.h" #include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "nodes/nodeFuncs.h" #include "parser/analyze.h" +#include "parser/parsetree.h" #include "parser/parse_cte.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +#include "utils/rel.h" /* Enumeration of contexts in which a self-reference is disallowed */ @@ -265,6 +268,35 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) parser_errposition(pstate, cte->location))); /* + * We disallow data-modifying WITH on tables that have triggers with + * transition tables, because we don't yet have separate transition tables + * for them. This restriction will be lifted in a future release. + */ + if (query->commandType == CMD_INSERT || + query->commandType == CMD_UPDATE || + query->commandType == CMD_DELETE) + { + RangeTblEntry *rte = rt_fetch(query->resultRelation, query->rtable); + + if (rte->rtekind == RTE_RELATION) + { + Relation rel = heap_open(rte->relid, NoLock); /* lock held by rte */ + + if (rel->trigdesc != NULL && + (rel->trigdesc->trig_insert_new_table || + rel->trigdesc->trig_update_old_table || + rel->trigdesc->trig_update_new_table || + rel->trigdesc->trig_delete_old_table)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH clause cannot modify a table that has triggers with transition tables"), + parser_errposition(pstate, cte->location))); + + heap_close(rel, NoLock); + } + } + + /* * CTE queries are always marked not canSetTag. (Currently this only * matters for data-modifying statements, for which the flag will be * propagated to the ModifyTable plan node.) diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 0d560fb3eed..112a74c1ff8 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1892,4 +1892,18 @@ NOTICE: trigger on parted_stmt_trig AFTER INSERT for STATEMENT copy parted_stmt_trig1(a) from stdin; NOTICE: trigger on parted_stmt_trig1 BEFORE INSERT for ROW NOTICE: trigger on parted_stmt_trig1 AFTER INSERT for ROW +-- +-- Verify that wCTEs are not (yet) allowed to modify tables with +-- triggers with transition tables. +-- +create trigger trig_del_after_transition after delete on parted_stmt_trig1 + referencing old table as old_table + for each statement execute procedure trigger_notice(); +with del as ( + delete from parted_stmt_trig1 returning * +) +select; +ERROR: WITH clause cannot modify a table that has triggers with transition tables +LINE 1: with del as ( + ^ drop table parted_stmt_trig, parted2_stmt_trig; diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql index 5581fcb1648..62c0b8aef24 100644 --- a/src/test/regress/sql/triggers.sql +++ b/src/test/regress/sql/triggers.sql @@ -1359,4 +1359,18 @@ copy parted_stmt_trig1(a) from stdin; 1 \. +-- +-- Verify that wCTEs are not (yet) allowed to modify tables with +-- triggers with transition tables. +-- + +create trigger trig_del_after_transition after delete on parted_stmt_trig1 + referencing old table as old_table + for each statement execute procedure trigger_notice(); + +with del as ( + delete from parted_stmt_trig1 returning * +) +select; + drop table parted_stmt_trig, parted2_stmt_trig;