mirror of
https://github.com/postgres/postgres.git
synced 2025-08-09 17:03:00 +03:00
Disallow collecting transition tuples from child foreign tables.
Commit9e6104c66
disallowed transition tables on foreign tables, but failed to account for cases where a foreign table is a child table of a partitioned/inherited table on which transition tables exist, leading to incorrect transition tuples collected from such foreign tables for queries on the parent table triggering transition capture. This occurred not only for inherited UPDATE/DELETE but for partitioned INSERT later supported by commit3d956d956
, which should have handled it at least for the INSERT case, but didn't. To fix, modify ExecAR*Triggers to throw an error if the given relation is a foreign table requesting transition capture. Also, this commit fixes make_modifytable so that in case of an inherited UPDATE/DELETE triggering transition capture, FDWs choose normal operations to modify child foreign tables, not DirectModify; which is needed because they would otherwise skip the calls to ExecAR*Triggers at execution, causing unexpected behavior. Author: Etsuro Fujita <etsuro.fujita@gmail.com> Reviewed-by: Amit Langote <amitlangote09@gmail.com> Discussion: https://postgr.es/m/CAPmGK14QJYikKzBDCe3jMbpGENnQ7popFmbEgm-XTNuk55oyHg%40mail.gmail.com Backpatch-through: 13
This commit is contained in:
@@ -8212,6 +8212,119 @@ DELETE FROM rem1; -- can't be pushed down
|
|||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
DROP TRIGGER trig_row_after_delete ON rem1;
|
DROP TRIGGER trig_row_after_delete ON rem1;
|
||||||
|
-- We are allowed to create transition-table triggers on both kinds of
|
||||||
|
-- inheritance even if they contain foreign tables as children, but currently
|
||||||
|
-- collecting transition tuples from such foreign tables is not supported.
|
||||||
|
CREATE TABLE local_tbl (a text, b int);
|
||||||
|
CREATE FOREIGN TABLE foreign_tbl (a text, b int)
|
||||||
|
SERVER loopback OPTIONS (table_name 'local_tbl');
|
||||||
|
INSERT INTO foreign_tbl VALUES ('AAA', 42);
|
||||||
|
-- Test case for partition hierarchy
|
||||||
|
CREATE TABLE parent_tbl (a text, b int) PARTITION BY LIST (a);
|
||||||
|
ALTER TABLE parent_tbl ATTACH PARTITION foreign_tbl FOR VALUES IN ('AAA');
|
||||||
|
CREATE TRIGGER parent_tbl_insert_trig
|
||||||
|
AFTER INSERT ON parent_tbl REFERENCING NEW TABLE AS new_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
CREATE TRIGGER parent_tbl_update_trig
|
||||||
|
AFTER UPDATE ON parent_tbl REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
CREATE TRIGGER parent_tbl_delete_trig
|
||||||
|
AFTER DELETE ON parent_tbl REFERENCING OLD TABLE AS old_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
INSERT INTO parent_tbl VALUES ('AAA', 42);
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
COPY parent_tbl (a, b) FROM stdin;
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
CONTEXT: COPY parent_tbl, line 1: "AAA 42"
|
||||||
|
ALTER SERVER loopback OPTIONS (ADD batch_size '10');
|
||||||
|
INSERT INTO parent_tbl VALUES ('AAA', 42);
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
COPY parent_tbl (a, b) FROM stdin;
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
CONTEXT: COPY parent_tbl, line 1: "AAA 42"
|
||||||
|
ALTER SERVER loopback OPTIONS (DROP batch_size);
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------------------------
|
||||||
|
Update on public.parent_tbl
|
||||||
|
Foreign Update on public.foreign_tbl parent_tbl_1
|
||||||
|
Remote SQL: UPDATE public.local_tbl SET b = $2 WHERE ctid = $1
|
||||||
|
-> Foreign Scan on public.foreign_tbl parent_tbl_1
|
||||||
|
Output: (parent_tbl_1.b + 1), parent_tbl_1.tableoid, parent_tbl_1.ctid, parent_tbl_1.*
|
||||||
|
Remote SQL: SELECT a, b, ctid FROM public.local_tbl FOR UPDATE
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------
|
||||||
|
Delete on public.parent_tbl
|
||||||
|
Foreign Delete on public.foreign_tbl parent_tbl_1
|
||||||
|
Remote SQL: DELETE FROM public.local_tbl WHERE ctid = $1
|
||||||
|
-> Foreign Scan on public.foreign_tbl parent_tbl_1
|
||||||
|
Output: parent_tbl_1.tableoid, parent_tbl_1.ctid
|
||||||
|
Remote SQL: SELECT ctid FROM public.local_tbl FOR UPDATE
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
ALTER TABLE parent_tbl DETACH PARTITION foreign_tbl;
|
||||||
|
DROP TABLE parent_tbl;
|
||||||
|
-- Test case for non-partition hierarchy
|
||||||
|
CREATE TABLE parent_tbl (a text, b int);
|
||||||
|
ALTER FOREIGN TABLE foreign_tbl INHERIT parent_tbl;
|
||||||
|
CREATE TRIGGER parent_tbl_update_trig
|
||||||
|
AFTER UPDATE ON parent_tbl REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
CREATE TRIGGER parent_tbl_delete_trig
|
||||||
|
AFTER DELETE ON parent_tbl REFERENCING OLD TABLE AS old_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------------------------------
|
||||||
|
Update on public.parent_tbl
|
||||||
|
Update on public.parent_tbl parent_tbl_1
|
||||||
|
Foreign Update on public.foreign_tbl parent_tbl_2
|
||||||
|
Remote SQL: UPDATE public.local_tbl SET b = $2 WHERE ctid = $1
|
||||||
|
-> Result
|
||||||
|
Output: (parent_tbl.b + 1), parent_tbl.tableoid, parent_tbl.ctid, (NULL::record)
|
||||||
|
-> Append
|
||||||
|
-> Seq Scan on public.parent_tbl parent_tbl_1
|
||||||
|
Output: parent_tbl_1.b, parent_tbl_1.tableoid, parent_tbl_1.ctid, NULL::record
|
||||||
|
-> Foreign Scan on public.foreign_tbl parent_tbl_2
|
||||||
|
Output: parent_tbl_2.b, parent_tbl_2.tableoid, parent_tbl_2.ctid, parent_tbl_2.*
|
||||||
|
Remote SQL: SELECT a, b, ctid FROM public.local_tbl FOR UPDATE
|
||||||
|
(12 rows)
|
||||||
|
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
Delete on public.parent_tbl
|
||||||
|
Delete on public.parent_tbl parent_tbl_1
|
||||||
|
Foreign Delete on public.foreign_tbl parent_tbl_2
|
||||||
|
Remote SQL: DELETE FROM public.local_tbl WHERE ctid = $1
|
||||||
|
-> Append
|
||||||
|
-> Seq Scan on public.parent_tbl parent_tbl_1
|
||||||
|
Output: parent_tbl_1.tableoid, parent_tbl_1.ctid
|
||||||
|
-> Foreign Scan on public.foreign_tbl parent_tbl_2
|
||||||
|
Output: parent_tbl_2.tableoid, parent_tbl_2.ctid
|
||||||
|
Remote SQL: SELECT ctid FROM public.local_tbl FOR UPDATE
|
||||||
|
(10 rows)
|
||||||
|
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
ERROR: cannot collect transition tuples from child foreign tables
|
||||||
|
ALTER FOREIGN TABLE foreign_tbl NO INHERIT parent_tbl;
|
||||||
|
DROP TABLE parent_tbl;
|
||||||
|
-- Cleanup
|
||||||
|
DROP FOREIGN TABLE foreign_tbl;
|
||||||
|
DROP TABLE local_tbl;
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
-- test inheritance features
|
-- test inheritance features
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
|
@@ -2272,6 +2272,84 @@ EXPLAIN (verbose, costs off)
|
|||||||
DELETE FROM rem1; -- can't be pushed down
|
DELETE FROM rem1; -- can't be pushed down
|
||||||
DROP TRIGGER trig_row_after_delete ON rem1;
|
DROP TRIGGER trig_row_after_delete ON rem1;
|
||||||
|
|
||||||
|
|
||||||
|
-- We are allowed to create transition-table triggers on both kinds of
|
||||||
|
-- inheritance even if they contain foreign tables as children, but currently
|
||||||
|
-- collecting transition tuples from such foreign tables is not supported.
|
||||||
|
|
||||||
|
CREATE TABLE local_tbl (a text, b int);
|
||||||
|
CREATE FOREIGN TABLE foreign_tbl (a text, b int)
|
||||||
|
SERVER loopback OPTIONS (table_name 'local_tbl');
|
||||||
|
|
||||||
|
INSERT INTO foreign_tbl VALUES ('AAA', 42);
|
||||||
|
|
||||||
|
-- Test case for partition hierarchy
|
||||||
|
CREATE TABLE parent_tbl (a text, b int) PARTITION BY LIST (a);
|
||||||
|
ALTER TABLE parent_tbl ATTACH PARTITION foreign_tbl FOR VALUES IN ('AAA');
|
||||||
|
|
||||||
|
CREATE TRIGGER parent_tbl_insert_trig
|
||||||
|
AFTER INSERT ON parent_tbl REFERENCING NEW TABLE AS new_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
CREATE TRIGGER parent_tbl_update_trig
|
||||||
|
AFTER UPDATE ON parent_tbl REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
CREATE TRIGGER parent_tbl_delete_trig
|
||||||
|
AFTER DELETE ON parent_tbl REFERENCING OLD TABLE AS old_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
|
||||||
|
INSERT INTO parent_tbl VALUES ('AAA', 42);
|
||||||
|
|
||||||
|
COPY parent_tbl (a, b) FROM stdin;
|
||||||
|
AAA 42
|
||||||
|
\.
|
||||||
|
|
||||||
|
ALTER SERVER loopback OPTIONS (ADD batch_size '10');
|
||||||
|
|
||||||
|
INSERT INTO parent_tbl VALUES ('AAA', 42);
|
||||||
|
|
||||||
|
COPY parent_tbl (a, b) FROM stdin;
|
||||||
|
AAA 42
|
||||||
|
\.
|
||||||
|
|
||||||
|
ALTER SERVER loopback OPTIONS (DROP batch_size);
|
||||||
|
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
|
||||||
|
ALTER TABLE parent_tbl DETACH PARTITION foreign_tbl;
|
||||||
|
DROP TABLE parent_tbl;
|
||||||
|
|
||||||
|
-- Test case for non-partition hierarchy
|
||||||
|
CREATE TABLE parent_tbl (a text, b int);
|
||||||
|
ALTER FOREIGN TABLE foreign_tbl INHERIT parent_tbl;
|
||||||
|
|
||||||
|
CREATE TRIGGER parent_tbl_update_trig
|
||||||
|
AFTER UPDATE ON parent_tbl REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
CREATE TRIGGER parent_tbl_delete_trig
|
||||||
|
AFTER DELETE ON parent_tbl REFERENCING OLD TABLE AS old_table
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
|
||||||
|
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
UPDATE parent_tbl SET b = b + 1;
|
||||||
|
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
DELETE FROM parent_tbl;
|
||||||
|
|
||||||
|
ALTER FOREIGN TABLE foreign_tbl NO INHERIT parent_tbl;
|
||||||
|
DROP TABLE parent_tbl;
|
||||||
|
|
||||||
|
-- Cleanup
|
||||||
|
DROP FOREIGN TABLE foreign_tbl;
|
||||||
|
DROP TABLE local_tbl;
|
||||||
|
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
-- test inheritance features
|
-- test inheritance features
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
|
@@ -2545,6 +2545,15 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
|
|||||||
{
|
{
|
||||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||||
|
|
||||||
|
if (relinfo->ri_FdwRoutine && transition_capture &&
|
||||||
|
transition_capture->tcs_insert_new_table)
|
||||||
|
{
|
||||||
|
Assert(relinfo->ri_RootResultRelInfo);
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot collect transition tuples from child foreign tables")));
|
||||||
|
}
|
||||||
|
|
||||||
if ((trigdesc && trigdesc->trig_insert_after_row) ||
|
if ((trigdesc && trigdesc->trig_insert_after_row) ||
|
||||||
(transition_capture && transition_capture->tcs_insert_new_table))
|
(transition_capture && transition_capture->tcs_insert_new_table))
|
||||||
AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
|
AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
|
||||||
@@ -2797,6 +2806,15 @@ ExecARDeleteTriggers(EState *estate,
|
|||||||
{
|
{
|
||||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||||
|
|
||||||
|
if (relinfo->ri_FdwRoutine && transition_capture &&
|
||||||
|
transition_capture->tcs_delete_old_table)
|
||||||
|
{
|
||||||
|
Assert(relinfo->ri_RootResultRelInfo);
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot collect transition tuples from child foreign tables")));
|
||||||
|
}
|
||||||
|
|
||||||
if ((trigdesc && trigdesc->trig_delete_after_row) ||
|
if ((trigdesc && trigdesc->trig_delete_after_row) ||
|
||||||
(transition_capture && transition_capture->tcs_delete_old_table))
|
(transition_capture && transition_capture->tcs_delete_old_table))
|
||||||
{
|
{
|
||||||
@@ -3134,6 +3152,16 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
|||||||
{
|
{
|
||||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||||
|
|
||||||
|
if (relinfo->ri_FdwRoutine && transition_capture &&
|
||||||
|
(transition_capture->tcs_update_old_table ||
|
||||||
|
transition_capture->tcs_update_new_table))
|
||||||
|
{
|
||||||
|
Assert(relinfo->ri_RootResultRelInfo);
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot collect transition tuples from child foreign tables")));
|
||||||
|
}
|
||||||
|
|
||||||
if ((trigdesc && trigdesc->trig_update_after_row) ||
|
if ((trigdesc && trigdesc->trig_update_after_row) ||
|
||||||
(transition_capture &&
|
(transition_capture &&
|
||||||
(transition_capture->tcs_update_old_table ||
|
(transition_capture->tcs_update_old_table ||
|
||||||
|
@@ -7174,6 +7174,8 @@ make_modifytable(PlannerInfo *root, Plan *subplan,
|
|||||||
ModifyTable *node = makeNode(ModifyTable);
|
ModifyTable *node = makeNode(ModifyTable);
|
||||||
bool returning_old_or_new = false;
|
bool returning_old_or_new = false;
|
||||||
bool returning_old_or_new_valid = false;
|
bool returning_old_or_new_valid = false;
|
||||||
|
bool transition_tables = false;
|
||||||
|
bool transition_tables_valid = false;
|
||||||
List *fdw_private_list;
|
List *fdw_private_list;
|
||||||
Bitmapset *direct_modify_plans;
|
Bitmapset *direct_modify_plans;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
@@ -7320,8 +7322,8 @@ make_modifytable(PlannerInfo *root, Plan *subplan,
|
|||||||
* callback functions needed for that and (2) there are no local
|
* callback functions needed for that and (2) there are no local
|
||||||
* structures that need to be run for each modified row: row-level
|
* structures that need to be run for each modified row: row-level
|
||||||
* triggers on the foreign table, stored generated columns, WITH CHECK
|
* triggers on the foreign table, stored generated columns, WITH CHECK
|
||||||
* OPTIONs from parent views, or Vars returning OLD/NEW in the
|
* OPTIONs from parent views, Vars returning OLD/NEW in the RETURNING
|
||||||
* RETURNING list.
|
* list, or transition tables on the named relation.
|
||||||
*/
|
*/
|
||||||
direct_modify = false;
|
direct_modify = false;
|
||||||
if (fdwroutine != NULL &&
|
if (fdwroutine != NULL &&
|
||||||
@@ -7333,7 +7335,10 @@ make_modifytable(PlannerInfo *root, Plan *subplan,
|
|||||||
!has_row_triggers(root, rti, operation) &&
|
!has_row_triggers(root, rti, operation) &&
|
||||||
!has_stored_generated_columns(root, rti))
|
!has_stored_generated_columns(root, rti))
|
||||||
{
|
{
|
||||||
/* returning_old_or_new is the same for all result relations */
|
/*
|
||||||
|
* returning_old_or_new and transition_tables are the same for all
|
||||||
|
* result relations, respectively
|
||||||
|
*/
|
||||||
if (!returning_old_or_new_valid)
|
if (!returning_old_or_new_valid)
|
||||||
{
|
{
|
||||||
returning_old_or_new =
|
returning_old_or_new =
|
||||||
@@ -7342,7 +7347,18 @@ make_modifytable(PlannerInfo *root, Plan *subplan,
|
|||||||
returning_old_or_new_valid = true;
|
returning_old_or_new_valid = true;
|
||||||
}
|
}
|
||||||
if (!returning_old_or_new)
|
if (!returning_old_or_new)
|
||||||
direct_modify = fdwroutine->PlanDirectModify(root, node, rti, i);
|
{
|
||||||
|
if (!transition_tables_valid)
|
||||||
|
{
|
||||||
|
transition_tables = has_transition_tables(root,
|
||||||
|
nominalRelation,
|
||||||
|
operation);
|
||||||
|
transition_tables_valid = true;
|
||||||
|
}
|
||||||
|
if (!transition_tables)
|
||||||
|
direct_modify = fdwroutine->PlanDirectModify(root, node,
|
||||||
|
rti, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (direct_modify)
|
if (direct_modify)
|
||||||
direct_modify_plans = bms_add_member(direct_modify_plans, i);
|
direct_modify_plans = bms_add_member(direct_modify_plans, i);
|
||||||
|
@@ -2303,6 +2303,60 @@ has_row_triggers(PlannerInfo *root, Index rti, CmdType event)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* has_transition_tables
|
||||||
|
*
|
||||||
|
* Detect whether the specified relation has any transition tables for event.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
has_transition_tables(PlannerInfo *root, Index rti, CmdType event)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = planner_rt_fetch(rti, root);
|
||||||
|
Relation relation;
|
||||||
|
TriggerDesc *trigDesc;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
Assert(rte->rtekind == RTE_RELATION);
|
||||||
|
|
||||||
|
/* Currently foreign tables cannot have transition tables */
|
||||||
|
if (rte->relkind == RELKIND_FOREIGN_TABLE)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* Assume we already have adequate lock */
|
||||||
|
relation = table_open(rte->relid, NoLock);
|
||||||
|
|
||||||
|
trigDesc = relation->trigdesc;
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case CMD_INSERT:
|
||||||
|
if (trigDesc &&
|
||||||
|
trigDesc->trig_insert_new_table)
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
case CMD_UPDATE:
|
||||||
|
if (trigDesc &&
|
||||||
|
(trigDesc->trig_update_old_table ||
|
||||||
|
trigDesc->trig_update_new_table))
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
case CMD_DELETE:
|
||||||
|
if (trigDesc &&
|
||||||
|
trigDesc->trig_delete_old_table)
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
/* There is no separate event for MERGE, only INSERT/UPDATE/DELETE */
|
||||||
|
case CMD_MERGE:
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized CmdType: %d", (int) event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
table_close(relation, NoLock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* has_stored_generated_columns
|
* has_stored_generated_columns
|
||||||
*
|
*
|
||||||
|
@@ -72,6 +72,8 @@ extern double get_function_rows(PlannerInfo *root, Oid funcid, Node *node);
|
|||||||
|
|
||||||
extern bool has_row_triggers(PlannerInfo *root, Index rti, CmdType event);
|
extern bool has_row_triggers(PlannerInfo *root, Index rti, CmdType event);
|
||||||
|
|
||||||
|
extern bool has_transition_tables(PlannerInfo *root, Index rti, CmdType event);
|
||||||
|
|
||||||
extern bool has_stored_generated_columns(PlannerInfo *root, Index rti);
|
extern bool has_stored_generated_columns(PlannerInfo *root, Index rti);
|
||||||
|
|
||||||
extern Bitmapset *get_dependent_generated_columns(PlannerInfo *root, Index rti,
|
extern Bitmapset *get_dependent_generated_columns(PlannerInfo *root, Index rti,
|
||||||
|
Reference in New Issue
Block a user