]> granicus.if.org Git - postgresql/commitdiff
Fix interaction of triggers, partitioning, and EXPLAIN ANALYZE.
authorRobert Haas <rhaas@postgresql.org>
Fri, 18 Aug 2017 17:01:05 +0000 (13:01 -0400)
committerRobert Haas <rhaas@postgresql.org>
Fri, 18 Aug 2017 17:06:25 +0000 (13:06 -0400)
Add a new EState member es_leaf_result_relations, so that the trigger
code knows about ResultRelInfos created by tuple routing.  Also make
sure ExplainPrintTriggers knows about partition-related
ResultRelInfos.

Etsuro Fujita, reviewed by Amit Langote

Discussion: http://postgr.es/m/57163e18-8e56-da83-337a-22f2c0008051@lab.ntt.co.jp

src/backend/commands/copy.c
src/backend/commands/explain.c
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/executor/nodeModifyTable.c
src/include/executor/executor.h
src/include/nodes/execnodes.h

index a258965c200941521d60bfd29f51d7426b8fdf58..375a25fbcf843a52b19c89c1e512e6b0a65adc43 100644 (file)
@@ -1415,59 +1415,6 @@ 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
        {
@@ -2482,6 +2429,63 @@ CopyFrom(CopyState cstate)
        /* Triggers might need a slot as well */
        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()
index 7648201218e18ad6a88aa5544c58fe1dc30d178f..953e74d73cae354f394c70dd037c6bee8bc6edb2 100644 (file)
@@ -656,17 +656,30 @@ ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
        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 || targrels != NIL);
+       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);
index 74071eede6e30adf61402c477fe14894883fb6b1..4582a3caa00de7f6c67085cb8c0ac7ede8dcc736 100644 (file)
@@ -1365,16 +1365,18 @@ 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.
+ * 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)
@@ -1395,6 +1397,23 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
                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)
        {
@@ -3238,6 +3257,7 @@ EvalPlanQualEnd(EPQState *epqstate)
 void
 ExecSetupPartitionTupleRouting(Relation rel,
                                                           Index resultRTindex,
+                                                          EState *estate,
                                                           PartitionDispatch **pd,
                                                           ResultRelInfo **partitions,
                                                           TupleConversionMap ***tup_conv_maps,
@@ -3301,7 +3321,10 @@ ExecSetupPartitionTupleRouting(Relation rel,
                                                  partrel,
                                                  resultRTindex,
                                                  rel,
-                                                 0);
+                                                 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
index 25772fc603122917e4322db3c40c365aeafc028d..c3988468795e2920010ad072eac8c34e4417400f 100644 (file)
@@ -115,6 +115,11 @@ CreateExecutorState(void)
        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;
index 36b2b43bc6215cab135f2e222a8abdbd2bf0ce8b..70a6b847a0ead693e8fc4c0c60b296b78d5dc730 100644 (file)
@@ -1919,6 +1919,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 
                ExecSetupPartitionTupleRouting(rel,
                                                                           node->nominalRelation,
+                                                                          estate,
                                                                           &partition_dispatch_info,
                                                                           &partitions,
                                                                           &partition_tupconv_maps,
index 60326f9d0372425fc8865f3e20cedfa685ea75cd..eacbea3c3655aec2314954648515f17235629777 100644 (file)
@@ -208,6 +208,7 @@ extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
 extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
 extern void ExecSetupPartitionTupleRouting(Relation rel,
                                                           Index resultRTindex,
+                                                          EState *estate,
                                                           PartitionDispatch **pd,
                                                           ResultRelInfo **partitions,
                                                           TupleConversionMap ***tup_conv_maps,
index 577499465d60ec90f48c60b0b7dbbfa58c4ab749..3272c4b315575f6f17cbc67afe1f68decb6f4338 100644 (file)
@@ -452,6 +452,9 @@ typedef struct EState
        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 */