diff options
author | Andrew Gierth <rhodiumtoad@postgresql.org> | 2017-06-28 18:55:03 +0100 |
---|---|---|
committer | Andrew Gierth <rhodiumtoad@postgresql.org> | 2017-06-28 18:55:03 +0100 |
commit | 501ed02cf6f4f60c3357775eb07578aebc912d3a (patch) | |
tree | 6811c9e9181dfff5ae384b9ec69994de2b777a2b /src/backend/executor/nodeModifyTable.c | |
parent | 99255d73c07c89b69be028a1a7b8027a78befed4 (diff) | |
download | postgresql-501ed02cf6f4f60c3357775eb07578aebc912d3a.tar.gz postgresql-501ed02cf6f4f60c3357775eb07578aebc912d3a.zip |
Fix transition tables for partition/inheritance.
We disallow row-level triggers with transition tables on child tables.
Transition tables for triggers on the parent table contain only those
columns present in the parent. (We can't mix tuple formats in a
single transition table.)
Patch by Thomas Munro
Discussion: https://postgr.es/m/CA%2BTgmoZzTBBAsEUh4MazAN7ga%3D8SsMC-Knp-6cetts9yNZUCcg%40mail.gmail.com
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 150 |
1 files changed, 136 insertions, 14 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 5e43a069426..f2534f20622 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -314,6 +314,36 @@ ExecInsert(ModifyTableState *mtstate, estate->es_result_relation_info = resultRelInfo; /* + * If we're capturing transition tuples, we might need to convert from + * the partition rowtype to parent rowtype. + */ + if (mtstate->mt_transition_capture != NULL) + { + if (resultRelInfo->ri_TrigDesc && + (resultRelInfo->ri_TrigDesc->trig_insert_before_row || + resultRelInfo->ri_TrigDesc->trig_insert_instead_row)) + { + /* + * If there are any BEFORE or INSTEAD triggers on the + * partition, we'll have to be ready to convert their result + * back to tuplestore format. + */ + mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL; + mtstate->mt_transition_capture->tcs_map = + mtstate->mt_transition_tupconv_maps[leaf_part_index]; + } + else + { + /* + * Otherwise, just remember the original unconverted tuple, to + * avoid a needless round trip conversion. + */ + mtstate->mt_transition_capture->tcs_original_insert_tuple = tuple; + mtstate->mt_transition_capture->tcs_map = NULL; + } + } + + /* * We might need to convert from the parent rowtype to the partition * rowtype. */ @@ -588,7 +618,8 @@ ExecInsert(ModifyTableState *mtstate, } /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes); + ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes, + mtstate->mt_transition_capture); list_free(recheckIndexes); @@ -636,7 +667,8 @@ ExecInsert(ModifyTableState *mtstate, * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecDelete(ItemPointer tupleid, +ExecDelete(ModifyTableState *mtstate, + ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, @@ -813,7 +845,8 @@ ldelete:; (estate->es_processed)++; /* AFTER ROW DELETE Triggers */ - ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple); + ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple, + mtstate->mt_transition_capture); /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) @@ -894,7 +927,8 @@ ldelete:; * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecUpdate(ItemPointer tupleid, +ExecUpdate(ModifyTableState *mtstate, + ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, @@ -1122,7 +1156,8 @@ lreplace:; /* AFTER ROW UPDATE Triggers */ ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple, - recheckIndexes); + recheckIndexes, + mtstate->mt_transition_capture); list_free(recheckIndexes); @@ -1329,7 +1364,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, */ /* Execute UPDATE with projection */ - *returning = ExecUpdate(&tuple.t_self, NULL, + *returning = ExecUpdate(mtstate, &tuple.t_self, NULL, mtstate->mt_conflproj, planSlot, &mtstate->mt_epqstate, mtstate->ps.state, canSetTag); @@ -1376,20 +1411,31 @@ fireBSTriggers(ModifyTableState *node) } /* - * Process AFTER EACH STATEMENT triggers + * Return the ResultRelInfo for which we will fire AFTER STATEMENT triggers. + * This is also the relation into whose tuple format all captured transition + * tuples must be converted. */ -static void -fireASTriggers(ModifyTableState *node) +static ResultRelInfo * +getASTriggerResultRelInfo(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; + return node->rootResultRelInfo; + else + return node->resultRelInfo; +} + +/* + * Process AFTER EACH STATEMENT triggers + */ +static void +fireASTriggers(ModifyTableState *node) +{ + ResultRelInfo *resultRelInfo = getASTriggerResultRelInfo(node); switch (node->operation) { @@ -1411,6 +1457,72 @@ fireASTriggers(ModifyTableState *node) } } +/* + * Set up the state needed for collecting transition tuples for AFTER + * triggers. + */ +static void +ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate) +{ + ResultRelInfo *targetRelInfo = getASTriggerResultRelInfo(mtstate); + int i; + + /* Check for transition tables on the directly targeted relation. */ + mtstate->mt_transition_capture = + MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc); + + /* + * If we found that we need to collect transition tuples then we may also + * need tuple conversion maps for any children that have TupleDescs that + * aren't compatible with the tuplestores. + */ + if (mtstate->mt_transition_capture != NULL) + { + ResultRelInfo *resultRelInfos; + int numResultRelInfos; + + /* Find the set of partitions so that we can find their TupleDescs. */ + if (mtstate->mt_partition_dispatch_info != NULL) + { + /* + * For INSERT via partitioned table, so we need TupleDescs based + * on the partition routing table. + */ + resultRelInfos = mtstate->mt_partitions; + numResultRelInfos = mtstate->mt_num_partitions; + } + else + { + /* Otherwise we need the ResultRelInfo for each subplan. */ + resultRelInfos = mtstate->resultRelInfo; + numResultRelInfos = mtstate->mt_nplans; + } + + /* + * Build array of conversion maps from each child's TupleDesc to the + * one used in the tuplestore. The map pointers may be NULL when no + * conversion is necessary, which is hopefully a common case for + * partitions. + */ + mtstate->mt_transition_tupconv_maps = (TupleConversionMap **) + palloc0(sizeof(TupleConversionMap *) * numResultRelInfos); + for (i = 0; i < numResultRelInfos; ++i) + { + mtstate->mt_transition_tupconv_maps[i] = + convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc), + RelationGetDescr(targetRelInfo->ri_RelationDesc), + gettext_noop("could not convert row type")); + } + + /* + * Install the conversion map for the first plan for UPDATE and DELETE + * operations. It will be advanced each time we switch to the next + * plan. (INSERT operations set it every time.) + */ + mtstate->mt_transition_capture->tcs_map = + mtstate->mt_transition_tupconv_maps[0]; + } +} /* ---------------------------------------------------------------- * ExecModifyTable @@ -1509,6 +1621,13 @@ ExecModifyTable(ModifyTableState *node) estate->es_result_relation_info = resultRelInfo; EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan, node->mt_arowmarks[node->mt_whichplan]); + if (node->mt_transition_capture != NULL) + { + /* Prepare to convert transition tuples from this child. */ + Assert(node->mt_transition_tupconv_maps != NULL); + node->mt_transition_capture->tcs_map = + node->mt_transition_tupconv_maps[node->mt_whichplan]; + } continue; } else @@ -1618,11 +1737,11 @@ ExecModifyTable(ModifyTableState *node) estate, node->canSetTag); break; case CMD_UPDATE: - slot = ExecUpdate(tupleid, oldtuple, slot, planSlot, + slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot, &node->mt_epqstate, estate, node->canSetTag); break; case CMD_DELETE: - slot = ExecDelete(tupleid, oldtuple, planSlot, + slot = ExecDelete(node, tupleid, oldtuple, planSlot, &node->mt_epqstate, estate, node->canSetTag); break; default: @@ -1804,6 +1923,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->mt_partition_tuple_slot = partition_tuple_slot; } + /* Build state for collecting transition tuples */ + ExecSetupTransitionCaptureState(mtstate, estate); + /* * Initialize any WITH CHECK OPTION constraints if needed. */ |