mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Fire per-statement triggers on partitioned tables.
Even though no actual tuples are ever inserted into a partitioned table (the actual tuples are in the partitions, not the partitioned table itself), we still need to have a ResultRelInfo for the partitioned table, or per-statement triggers won't get fired. Amit Langote, per a report from Rajkumar Raghuwanshi. Reviewed by me. Discussion: http://postgr.es/m/CAKcux6%3DwYospCRY2J4XEFuVy0L41S%3Dfic7rmkbsU-GXhhSbmBg%40mail.gmail.com
This commit is contained in:
@ -861,17 +861,52 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
||||
|
||||
/*
|
||||
* In the partitioned result relation case, lock the non-leaf result
|
||||
* relations too. We don't however need ResultRelInfos for them.
|
||||
* relations too. A subset of these are the roots of respective
|
||||
* partitioned tables, for which we also allocate ResulRelInfos.
|
||||
*/
|
||||
estate->es_root_result_relations = NULL;
|
||||
estate->es_num_root_result_relations = 0;
|
||||
if (plannedstmt->nonleafResultRelations)
|
||||
{
|
||||
int num_roots = list_length(plannedstmt->rootResultRelations);
|
||||
|
||||
/*
|
||||
* Firstly, build ResultRelInfos for all the partitioned table
|
||||
* roots, because we will need them to fire the statement-level
|
||||
* triggers, if any.
|
||||
*/
|
||||
resultRelInfos = (ResultRelInfo *)
|
||||
palloc(num_roots * sizeof(ResultRelInfo));
|
||||
resultRelInfo = resultRelInfos;
|
||||
foreach(l, plannedstmt->rootResultRelations)
|
||||
{
|
||||
Index resultRelIndex = lfirst_int(l);
|
||||
Oid resultRelOid;
|
||||
Relation resultRelDesc;
|
||||
|
||||
resultRelOid = getrelid(resultRelIndex, rangeTable);
|
||||
resultRelDesc = heap_open(resultRelOid, RowExclusiveLock);
|
||||
InitResultRelInfo(resultRelInfo,
|
||||
resultRelDesc,
|
||||
lfirst_int(l),
|
||||
NULL,
|
||||
estate->es_instrument);
|
||||
resultRelInfo++;
|
||||
}
|
||||
|
||||
estate->es_root_result_relations = resultRelInfos;
|
||||
estate->es_num_root_result_relations = num_roots;
|
||||
|
||||
/* Simply lock the rest of them. */
|
||||
foreach(l, plannedstmt->nonleafResultRelations)
|
||||
{
|
||||
Index resultRelationIndex = lfirst_int(l);
|
||||
Oid resultRelationOid;
|
||||
Index resultRelIndex = lfirst_int(l);
|
||||
|
||||
resultRelationOid = getrelid(resultRelationIndex, rangeTable);
|
||||
LockRelationOid(resultRelationOid, RowExclusiveLock);
|
||||
/* We locked the roots above. */
|
||||
if (!list_member_int(plannedstmt->rootResultRelations,
|
||||
resultRelIndex))
|
||||
LockRelationOid(getrelid(resultRelIndex, rangeTable),
|
||||
RowExclusiveLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -883,6 +918,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
||||
estate->es_result_relations = NULL;
|
||||
estate->es_num_result_relations = 0;
|
||||
estate->es_result_relation_info = NULL;
|
||||
estate->es_root_result_relations = NULL;
|
||||
estate->es_num_root_result_relations = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1565,6 +1602,14 @@ ExecEndPlan(PlanState *planstate, EState *estate)
|
||||
resultRelInfo++;
|
||||
}
|
||||
|
||||
/* Close the root target relation(s). */
|
||||
resultRelInfo = estate->es_root_result_relations;
|
||||
for (i = estate->es_num_root_result_relations; i > 0; i--)
|
||||
{
|
||||
heap_close(resultRelInfo->ri_RelationDesc, NoLock);
|
||||
resultRelInfo++;
|
||||
}
|
||||
|
||||
/*
|
||||
* likewise close any trigger target relations
|
||||
*/
|
||||
|
@ -1328,19 +1328,29 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
|
||||
static void
|
||||
fireBSTriggers(ModifyTableState *node)
|
||||
{
|
||||
ResultRelInfo *resultRelInfo = node->resultRelInfo;
|
||||
|
||||
/*
|
||||
* If the node modifies a partitioned table, we must fire its triggers.
|
||||
* Note that in that case, node->resultRelInfo points to the first leaf
|
||||
* partition, not the root table.
|
||||
*/
|
||||
if (node->rootResultRelInfo != NULL)
|
||||
resultRelInfo = node->rootResultRelInfo;
|
||||
|
||||
switch (node->operation)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
ExecBSInsertTriggers(node->ps.state, node->resultRelInfo);
|
||||
ExecBSInsertTriggers(node->ps.state, resultRelInfo);
|
||||
if (node->mt_onconflict == ONCONFLICT_UPDATE)
|
||||
ExecBSUpdateTriggers(node->ps.state,
|
||||
node->resultRelInfo);
|
||||
resultRelInfo);
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
|
||||
ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
ExecBSDeleteTriggers(node->ps.state, node->resultRelInfo);
|
||||
ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unknown operation");
|
||||
@ -1354,19 +1364,29 @@ fireBSTriggers(ModifyTableState *node)
|
||||
static void
|
||||
fireASTriggers(ModifyTableState *node)
|
||||
{
|
||||
ResultRelInfo *resultRelInfo = node->resultRelInfo;
|
||||
|
||||
/*
|
||||
* If the node modifies a partitioned table, we must fire its triggers.
|
||||
* Note that in that case, node->resultRelInfo points to the first leaf
|
||||
* partition, not the root table.
|
||||
*/
|
||||
if (node->rootResultRelInfo != NULL)
|
||||
resultRelInfo = node->rootResultRelInfo;
|
||||
|
||||
switch (node->operation)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
if (node->mt_onconflict == ONCONFLICT_UPDATE)
|
||||
ExecASUpdateTriggers(node->ps.state,
|
||||
node->resultRelInfo);
|
||||
ExecASInsertTriggers(node->ps.state, node->resultRelInfo);
|
||||
resultRelInfo);
|
||||
ExecASInsertTriggers(node->ps.state, resultRelInfo);
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
ExecASUpdateTriggers(node->ps.state, node->resultRelInfo);
|
||||
ExecASUpdateTriggers(node->ps.state, resultRelInfo);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
ExecASDeleteTriggers(node->ps.state, node->resultRelInfo);
|
||||
ExecASDeleteTriggers(node->ps.state, resultRelInfo);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unknown operation");
|
||||
@ -1652,6 +1672,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
|
||||
mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
|
||||
mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
|
||||
|
||||
/* If modifying a partitioned table, initialize the root table info */
|
||||
if (node->rootResultRelIndex >= 0)
|
||||
mtstate->rootResultRelInfo = estate->es_root_result_relations +
|
||||
node->rootResultRelIndex;
|
||||
|
||||
mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
|
||||
mtstate->mt_nplans = nplans;
|
||||
mtstate->mt_onconflict = node->onConflictAction;
|
||||
|
Reference in New Issue
Block a user