*** a/src/backend/commands/copy.c --- b/src/backend/commands/copy.c *************** *** 1415,1473 **** BeginCopy(ParseState *pstate, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("table \"%s\" does not have OIDs", RelationGetRelationName(cstate->rel)))); - - /* - * If there are any triggers with transition tables on the named - * relation, we need to be prepared to capture transition tuples. - */ - cstate->transition_capture = MakeTransitionCaptureState(rel->trigdesc); - - /* Initialize state for CopyFrom tuple routing. */ - if (is_from && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - PartitionDispatch *partition_dispatch_info; - ResultRelInfo *partitions; - TupleConversionMap **partition_tupconv_maps; - TupleTableSlot *partition_tuple_slot; - int num_parted, - num_partitions; - - ExecSetupPartitionTupleRouting(rel, - 1, - &partition_dispatch_info, - &partitions, - &partition_tupconv_maps, - &partition_tuple_slot, - &num_parted, &num_partitions); - cstate->partition_dispatch_info = partition_dispatch_info; - cstate->num_dispatch = num_parted; - cstate->partitions = partitions; - cstate->num_partitions = num_partitions; - cstate->partition_tupconv_maps = partition_tupconv_maps; - cstate->partition_tuple_slot = partition_tuple_slot; - - /* - * If we are capturing transition tuples, they may need to be - * converted from partition format back to partitioned table - * format (this is only ever necessary if a BEFORE trigger - * modifies the tuple). - */ - if (cstate->transition_capture != NULL) - { - int i; - - cstate->transition_tupconv_maps = (TupleConversionMap **) - palloc0(sizeof(TupleConversionMap *) * - cstate->num_partitions); - for (i = 0; i < cstate->num_partitions; ++i) - { - cstate->transition_tupconv_maps[i] = - convert_tuples_by_name(RelationGetDescr(cstate->partitions[i].ri_RelationDesc), - RelationGetDescr(rel), - gettext_noop("could not convert row type")); - } - } - } } else { --- 1415,1420 ---- *************** *** 2483,2488 **** CopyFrom(CopyState cstate) --- 2430,2492 ---- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate); /* + * If there are any triggers with transition tables on the named relation, + * we need to be prepared to capture transition tuples. + */ + cstate->transition_capture = + MakeTransitionCaptureState(cstate->rel->trigdesc); + + /* + * If the named relation is a partitioned table, initialize state for + * CopyFrom tuple routing. + */ + if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + PartitionDispatch *partition_dispatch_info; + ResultRelInfo *partitions; + TupleConversionMap **partition_tupconv_maps; + TupleTableSlot *partition_tuple_slot; + int num_parted, + num_partitions; + + ExecSetupPartitionTupleRouting(cstate->rel, + 1, + estate, + &partition_dispatch_info, + &partitions, + &partition_tupconv_maps, + &partition_tuple_slot, + &num_parted, &num_partitions); + cstate->partition_dispatch_info = partition_dispatch_info; + cstate->num_dispatch = num_parted; + cstate->partitions = partitions; + cstate->num_partitions = num_partitions; + cstate->partition_tupconv_maps = partition_tupconv_maps; + cstate->partition_tuple_slot = partition_tuple_slot; + + /* + * If we are capturing transition tuples, they may need to be + * converted from partition format back to partitioned table format + * (this is only ever necessary if a BEFORE trigger modifies the + * tuple). + */ + if (cstate->transition_capture != NULL) + { + int i; + + cstate->transition_tupconv_maps = (TupleConversionMap **) + palloc0(sizeof(TupleConversionMap *) * cstate->num_partitions); + for (i = 0; i < cstate->num_partitions; ++i) + { + cstate->transition_tupconv_maps[i] = + convert_tuples_by_name(RelationGetDescr(cstate->partitions[i].ri_RelationDesc), + RelationGetDescr(cstate->rel), + gettext_noop("could not convert row type")); + } + } + } + + /* * It's more efficient to prepare a bunch of tuples for insertion, and * insert them in one heap_multi_insert() call, than call heap_insert() * separately for every tuple. However, we can't do that if there are *** a/src/backend/commands/explain.c --- b/src/backend/commands/explain.c *************** *** 656,672 **** ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc) ResultRelInfo *rInfo; bool show_relname; int numrels = queryDesc->estate->es_num_result_relations; List *targrels = queryDesc->estate->es_trig_target_relations; int nr; ListCell *l; ExplainOpenGroup("Triggers", "Triggers", false, es); ! show_relname = (numrels > 1 || targrels != NIL); rInfo = queryDesc->estate->es_result_relations; for (nr = 0; nr < numrels; rInfo++, nr++) report_triggers(rInfo, show_relname, es); foreach(l, targrels) { rInfo = (ResultRelInfo *) lfirst(l); --- 656,685 ---- ResultRelInfo *rInfo; bool show_relname; int numrels = queryDesc->estate->es_num_result_relations; + int numrootrels = queryDesc->estate->es_num_root_result_relations; + List *leafrels = queryDesc->estate->es_leaf_result_relations; List *targrels = queryDesc->estate->es_trig_target_relations; int nr; ListCell *l; ExplainOpenGroup("Triggers", "Triggers", false, es); ! show_relname = (numrels > 1 || numrootrels > 0 || ! leafrels != NIL || targrels != NIL); rInfo = queryDesc->estate->es_result_relations; for (nr = 0; nr < numrels; rInfo++, nr++) report_triggers(rInfo, show_relname, es); + rInfo = queryDesc->estate->es_root_result_relations; + for (nr = 0; nr < numrootrels; rInfo++, nr++) + report_triggers(rInfo, show_relname, es); + + foreach(l, leafrels) + { + rInfo = (ResultRelInfo *) lfirst(l); + report_triggers(rInfo, show_relname, es); + } + foreach(l, targrels) { rInfo = (ResultRelInfo *) lfirst(l); *** a/src/backend/executor/execMain.c --- b/src/backend/executor/execMain.c *************** *** 1364,1379 **** InitResultRelInfo(ResultRelInfo *resultRelInfo, * * Get a ResultRelInfo for a trigger target relation. Most of the time, * triggers are fired on one of the result relations of the query, and so ! * we can just return a member of the es_result_relations array. (Note: in ! * self-join situations there might be multiple members with the same OID; ! * if so it doesn't matter which one we pick.) However, it is sometimes ! * necessary to fire triggers on other relations; this happens mainly when an ! * RI update trigger queues additional triggers on other relations, which will ! * be processed in the context of the outer query. For efficiency's sake, ! * we want to have a ResultRelInfo for those triggers too; that can avoid ! * repeated re-opening of the relation. (It also provides a way for EXPLAIN ! * ANALYZE to report the runtimes of such triggers.) So we make additional ! * ResultRelInfo's as needed, and save them in es_trig_target_relations. */ ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid) --- 1364,1381 ---- * * Get a ResultRelInfo for a trigger target relation. Most of the time, * triggers are fired on one of the result relations of the query, and so ! * we can just return a member of the es_result_relations array, the ! * es_root_result_relations array (if any), or the es_leaf_result_relations ! * list (if any). (Note: in self-join situations there might be multiple ! * members with the same OID; if so it doesn't matter which one we pick.) ! * However, it is sometimes necessary to fire triggers on other relations; ! * this happens mainly when an RI update trigger queues additional triggers ! * on other relations, which will be processed in the context of the outer ! * query. For efficiency's sake, we want to have a ResultRelInfo for those ! * triggers too; that can avoid repeated re-opening of the relation. (It ! * also provides a way for EXPLAIN ANALYZE to report the runtimes of such ! * triggers.) So we make additional ResultRelInfo's as needed, and save them ! * in es_trig_target_relations. */ ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid) *************** *** 1394,1399 **** ExecGetTriggerResultRel(EState *estate, Oid relid) --- 1396,1418 ---- rInfo++; nr--; } + /* Second, search through the root result relations, if any */ + rInfo = estate->es_root_result_relations; + nr = estate->es_num_root_result_relations; + while (nr > 0) + { + if (RelationGetRelid(rInfo->ri_RelationDesc) == relid) + return rInfo; + rInfo++; + nr--; + } + /* Third, search through the leaf result relations, if any */ + foreach(l, estate->es_leaf_result_relations) + { + rInfo = (ResultRelInfo *) lfirst(l); + if (RelationGetRelid(rInfo->ri_RelationDesc) == relid) + return rInfo; + } /* Nope, but maybe we already made an extra ResultRelInfo for it */ foreach(l, estate->es_trig_target_relations) { *************** *** 3237,3242 **** EvalPlanQualEnd(EPQState *epqstate) --- 3256,3262 ---- void ExecSetupPartitionTupleRouting(Relation rel, Index resultRTindex, + EState *estate, PartitionDispatch **pd, ResultRelInfo **partitions, TupleConversionMap ***tup_conv_maps, *************** *** 3297,3303 **** ExecSetupPartitionTupleRouting(Relation rel, partrel, resultRTindex, rel, ! 0); /* * Open partition indices (remember we do not support ON CONFLICT in --- 3317,3326 ---- partrel, resultRTindex, rel, ! estate->es_instrument); ! ! estate->es_leaf_result_relations = ! lappend(estate->es_leaf_result_relations, leaf_part_rri); /* * Open partition indices (remember we do not support ON CONFLICT in *** a/src/backend/executor/execUtils.c --- b/src/backend/executor/execUtils.c *************** *** 115,120 **** CreateExecutorState(void) --- 115,125 ---- 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; + + estate->es_leaf_result_relations = NIL; + estate->es_trig_target_relations = NIL; estate->es_trig_tuple_slot = NULL; estate->es_trig_oldtup_slot = NULL; *** a/src/backend/executor/nodeModifyTable.c --- b/src/backend/executor/nodeModifyTable.c *************** *** 1919,1924 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) --- 1919,1925 ---- ExecSetupPartitionTupleRouting(rel, node->nominalRelation, + estate, &partition_dispatch_info, &partitions, &partition_tupconv_maps, *** a/src/include/executor/executor.h --- b/src/include/executor/executor.h *************** *** 208,213 **** extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti, --- 208,214 ---- extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti); extern void ExecSetupPartitionTupleRouting(Relation rel, Index resultRTindex, + EState *estate, PartitionDispatch **pd, ResultRelInfo **partitions, TupleConversionMap ***tup_conv_maps, *** a/src/include/nodes/execnodes.h --- b/src/include/nodes/execnodes.h *************** *** 452,457 **** typedef struct EState --- 452,460 ---- ResultRelInfo *es_root_result_relations; /* array of ResultRelInfos */ int es_num_root_result_relations; /* length of the array */ + /* Info about leaf partitions of partitioned table(s) for insert queries: */ + List *es_leaf_result_relations; /* List of ResultRelInfos */ + /* Stuff used for firing triggers: */ List *es_trig_target_relations; /* trigger-only ResultRelInfos */ TupleTableSlot *es_trig_tuple_slot; /* for trigger output tuples */