Centralize executor-related partitioning code.
authorRobert Haas
Wed, 15 Nov 2017 15:23:28 +0000 (10:23 -0500)
committerRobert Haas
Wed, 15 Nov 2017 15:26:25 +0000 (10:26 -0500)
Some code is moved from partition.c, which has grown very quickly lately;
splitting the executor parts out might help to keep it from getting
totally out of control.  Other code is moved from execMain.c.  All is
moved to a new file execPartition.c.  get_partition_for_tuple now has
a new interface that more clearly separates executor concerns from
generic concerns.

Amit Langote.  A slight comment tweak by me.

Discussion: http://postgr.es/m/1f0985f8-3b61-8bc4-4350-baa6d804cb6d@lab.ntt.co.jp

src/backend/catalog/partition.c
src/backend/commands/copy.c
src/backend/executor/Makefile
src/backend/executor/execMain.c
src/backend/executor/execPartition.c [new file with mode: 0644]
src/backend/executor/nodeModifyTable.c
src/include/catalog/partition.h
src/include/executor/execPartition.h [new file with mode: 0644]
src/include/executor/executor.h

index cff59ed0551a74d309b9e89c4a2425f29e4a9fe9..ce29ba2eda9dfddb2090f65b728f77a771ae2a52 100644 (file)
@@ -170,8 +170,6 @@ static int32 partition_bound_cmp(PartitionKey key,
 static int partition_bound_bsearch(PartitionKey key,
                        PartitionBoundInfo boundinfo,
                        void *probe, bool probe_is_bound, bool *is_equal);
-static void get_partition_dispatch_recurse(Relation rel, Relation parent,
-                              List **pds, List **leaf_part_oids);
 static int get_partition_bound_num_indexes(PartitionBoundInfo b);
 static int get_greatest_modulus(PartitionBoundInfo b);
 static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
@@ -1530,148 +1528,6 @@ get_partition_qual_relid(Oid relid)
    return result;
 }
 
-/*
- * RelationGetPartitionDispatchInfo
- *     Returns information necessary to route tuples down a partition tree
- *
- * The number of elements in the returned array (that is, the number of
- * PartitionDispatch objects for the partitioned tables in the partition tree)
- * is returned in *num_parted and a list of the OIDs of all the leaf
- * partitions of rel is returned in *leaf_part_oids.
- *
- * All the relations in the partition tree (including 'rel') must have been
- * locked (using at least the AccessShareLock) by the caller.
- */
-PartitionDispatch *
-RelationGetPartitionDispatchInfo(Relation rel,
-                                int *num_parted, List **leaf_part_oids)
-{
-   List       *pdlist = NIL;
-   PartitionDispatchData **pd;
-   ListCell   *lc;
-   int         i;
-
-   Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
-
-   *num_parted = 0;
-   *leaf_part_oids = NIL;
-
-   get_partition_dispatch_recurse(rel, NULL, &pdlist, leaf_part_oids);
-   *num_parted = list_length(pdlist);
-   pd = (PartitionDispatchData **) palloc(*num_parted *
-                                          sizeof(PartitionDispatchData *));
-   i = 0;
-   foreach(lc, pdlist)
-   {
-       pd[i++] = lfirst(lc);
-   }
-
-   return pd;
-}
-
-/*
- * get_partition_dispatch_recurse
- *     Recursively expand partition tree rooted at rel
- *
- * As the partition tree is expanded in a depth-first manner, we maintain two
- * global lists: of PartitionDispatch objects corresponding to partitioned
- * tables in *pds and of the leaf partition OIDs in *leaf_part_oids.
- *
- * Note that the order of OIDs of leaf partitions in leaf_part_oids matches
- * the order in which the planner's expand_partitioned_rtentry() processes
- * them.  It's not necessarily the case that the offsets match up exactly,
- * because constraint exclusion might prune away some partitions on the
- * planner side, whereas we'll always have the complete list; but unpruned
- * partitions will appear in the same order in the plan as they are returned
- * here.
- */
-static void
-get_partition_dispatch_recurse(Relation rel, Relation parent,
-                              List **pds, List **leaf_part_oids)
-{
-   TupleDesc   tupdesc = RelationGetDescr(rel);
-   PartitionDesc partdesc = RelationGetPartitionDesc(rel);
-   PartitionKey partkey = RelationGetPartitionKey(rel);
-   PartitionDispatch pd;
-   int         i;
-
-   check_stack_depth();
-
-   /* Build a PartitionDispatch for this table and add it to *pds. */
-   pd = (PartitionDispatch) palloc(sizeof(PartitionDispatchData));
-   *pds = lappend(*pds, pd);
-   pd->reldesc = rel;
-   pd->key = partkey;
-   pd->keystate = NIL;
-   pd->partdesc = partdesc;
-   if (parent != NULL)
-   {
-       /*
-        * For every partitioned table other than the root, we must store a
-        * tuple table slot initialized with its tuple descriptor and a tuple
-        * conversion map to convert a tuple from its parent's rowtype to its
-        * own. That is to make sure that we are looking at the correct row
-        * using the correct tuple descriptor when computing its partition key
-        * for tuple routing.
-        */
-       pd->tupslot = MakeSingleTupleTableSlot(tupdesc);
-       pd->tupmap = convert_tuples_by_name(RelationGetDescr(parent),
-                                           tupdesc,
-                                           gettext_noop("could not convert row type"));
-   }
-   else
-   {
-       /* Not required for the root partitioned table */
-       pd->tupslot = NULL;
-       pd->tupmap = NULL;
-   }
-
-   /*
-    * Go look at each partition of this table.  If it's a leaf partition,
-    * simply add its OID to *leaf_part_oids.  If it's a partitioned table,
-    * recursively call get_partition_dispatch_recurse(), so that its
-    * partitions are processed as well and a corresponding PartitionDispatch
-    * object gets added to *pds.
-    *
-    * About the values in pd->indexes: for a leaf partition, it contains the
-    * leaf partition's position in the global list *leaf_part_oids minus 1,
-    * whereas for a partitioned table partition, it contains the partition's
-    * position in the global list *pds multiplied by -1.  The latter is
-    * multiplied by -1 to distinguish partitioned tables from leaf partitions
-    * when going through the values in pd->indexes.  So, for example, when
-    * using it during tuple-routing, encountering a value >= 0 means we found
-    * a leaf partition.  It is immediately returned as the index in the array
-    * of ResultRelInfos of all the leaf partitions, using which we insert the
-    * tuple into that leaf partition.  A negative value means we found a
-    * partitioned table.  The value multiplied by -1 is returned as the index
-    * in the array of PartitionDispatch objects of all partitioned tables in
-    * the tree.  This value is used to continue the search in the next level
-    * of the partition tree.
-    */
-   pd->indexes = (int *) palloc(partdesc->nparts * sizeof(int));
-   for (i = 0; i < partdesc->nparts; i++)
-   {
-       Oid         partrelid = partdesc->oids[i];
-
-       if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE)
-       {
-           *leaf_part_oids = lappend_oid(*leaf_part_oids, partrelid);
-           pd->indexes[i] = list_length(*leaf_part_oids) - 1;
-       }
-       else
-       {
-           /*
-            * We assume all tables in the partition tree were already locked
-            * by the caller.
-            */
-           Relation    partrel = heap_open(partrelid, NoLock);
-
-           pd->indexes[i] = -list_length(*pds);
-           get_partition_dispatch_recurse(partrel, rel, pds, leaf_part_oids);
-       }
-   }
-}
-
 /* Module-local functions */
 
 /*
@@ -2617,259 +2473,108 @@ generate_partition_qual(Relation rel)
    return result;
 }
 
-/* ----------------
- *     FormPartitionKeyDatum
- *         Construct values[] and isnull[] arrays for the partition key
- *         of a tuple.
- *
- * pd              Partition dispatch object of the partitioned table
- * slot            Heap tuple from which to extract partition key
- * estate          executor state for evaluating any partition key
- *                 expressions (must be non-NULL)
- * values          Array of partition key Datums (output area)
- * isnull          Array of is-null indicators (output area)
- *
- * the ecxt_scantuple slot of estate's per-tuple expr context must point to
- * the heap tuple passed in.
- * ----------------
- */
-void
-FormPartitionKeyDatum(PartitionDispatch pd,
-                     TupleTableSlot *slot,
-                     EState *estate,
-                     Datum *values,
-                     bool *isnull)
-{
-   ListCell   *partexpr_item;
-   int         i;
-
-   if (pd->key->partexprs != NIL && pd->keystate == NIL)
-   {
-       /* Check caller has set up context correctly */
-       Assert(estate != NULL &&
-              GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
-
-       /* First time through, set up expression evaluation state */
-       pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
-   }
-
-   partexpr_item = list_head(pd->keystate);
-   for (i = 0; i < pd->key->partnatts; i++)
-   {
-       AttrNumber  keycol = pd->key->partattrs[i];
-       Datum       datum;
-       bool        isNull;
-
-       if (keycol != 0)
-       {
-           /* Plain column; get the value directly from the heap tuple */
-           datum = slot_getattr(slot, keycol, &isNull);
-       }
-       else
-       {
-           /* Expression; need to evaluate it */
-           if (partexpr_item == NULL)
-               elog(ERROR, "wrong number of partition key expressions");
-           datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
-                                             GetPerTupleExprContext(estate),
-                                             &isNull);
-           partexpr_item = lnext(partexpr_item);
-       }
-       values[i] = datum;
-       isnull[i] = isNull;
-   }
-
-   if (partexpr_item != NULL)
-       elog(ERROR, "wrong number of partition key expressions");
-}
-
 /*
  * get_partition_for_tuple
- *     Finds a leaf partition for tuple contained in *slot
+ *     Finds partition of relation which accepts the partition key specified
+ *     in values and isnull
  *
- * Returned value is the sequence number of the leaf partition thus found,
- * or -1 if no leaf partition is found for the tuple.  *failed_at is set
- * to the OID of the partitioned table whose partition was not found in
- * the latter case.
+ * Return value is index of the partition (>= 0 and < partdesc->nparts) if one
+ * found or -1 if none found.
  */
 int
-get_partition_for_tuple(PartitionDispatch *pd,
-                       TupleTableSlot *slot,
-                       EState *estate,
-                       PartitionDispatchData **failed_at,
-                       TupleTableSlot **failed_slot)
+get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
 {
-   PartitionDispatch parent;
-   Datum       values[PARTITION_MAX_KEYS];
-   bool        isnull[PARTITION_MAX_KEYS];
-   int         result;
-   ExprContext *ecxt = GetPerTupleExprContext(estate);
-   TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
-
-   /* start with the root partitioned table */
-   parent = pd[0];
-   while (true)
-   {
-       PartitionKey key = parent->key;
-       PartitionDesc partdesc = parent->partdesc;
-       TupleTableSlot *myslot = parent->tupslot;
-       TupleConversionMap *map = parent->tupmap;
-       int         cur_index = -1;
-
-       if (myslot != NULL && map != NULL)
-       {
-           HeapTuple   tuple = ExecFetchSlotTuple(slot);
-
-           ExecClearTuple(myslot);
-           tuple = do_convert_tuple(tuple, map);
-           ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
-           slot = myslot;
-       }
+   int     bound_offset;
+   int     part_index = -1;
+   PartitionKey  key = RelationGetPartitionKey(relation);
+   PartitionDesc partdesc = RelationGetPartitionDesc(relation);
 
-       /* Quick exit */
-       if (partdesc->nparts == 0)
-       {
-           *failed_at = parent;
-           *failed_slot = slot;
-           result = -1;
-           goto error_exit;
-       }
-
-       /*
-        * Extract partition key from tuple. Expression evaluation machinery
-        * that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
-        * point to the correct tuple slot.  The slot might have changed from
-        * what was used for the parent table if the table of the current
-        * partitioning level has different tuple descriptor from the parent.
-        * So update ecxt_scantuple accordingly.
-        */
-       ecxt->ecxt_scantuple = slot;
-       FormPartitionKeyDatum(parent, slot, estate, values, isnull);
-
-       /* Route as appropriate based on partitioning strategy. */
-       switch (key->strategy)
-       {
-           case PARTITION_STRATEGY_HASH:
-               {
-                   PartitionBoundInfo boundinfo = partdesc->boundinfo;
-                   int         greatest_modulus = get_greatest_modulus(boundinfo);
-                   uint64      rowHash = compute_hash_value(key, values,
-                                                            isnull);
+   /* Route as appropriate based on partitioning strategy. */
+   switch (key->strategy)
+   {
+       case PARTITION_STRATEGY_HASH:
+           {
+               PartitionBoundInfo boundinfo = partdesc->boundinfo;
+               int     greatest_modulus = get_greatest_modulus(boundinfo);
+               uint64  rowHash = compute_hash_value(key, values, isnull);
 
-                   cur_index = boundinfo->indexes[rowHash % greatest_modulus];
-               }
-               break;
+               part_index = boundinfo->indexes[rowHash % greatest_modulus];
+           }
+           break;
 
-           case PARTITION_STRATEGY_LIST:
+       case PARTITION_STRATEGY_LIST:
+           if (isnull[0])
+           {
+               if (partition_bound_accepts_nulls(partdesc->boundinfo))
+                   part_index = partdesc->boundinfo->null_index;
+           }
+           else
+           {
+               bool        equal = false;
+
+               bound_offset = partition_bound_bsearch(key,
+                                                      partdesc->boundinfo,
+                                                      values,
+                                                      false,
+                                                      &equal);
+               if (bound_offset >= 0 && equal)
+                   part_index = partdesc->boundinfo->indexes[bound_offset];
+           }
+           break;
 
-               if (isnull[0])
-               {
-                   if (partition_bound_accepts_nulls(partdesc->boundinfo))
-                       cur_index = partdesc->boundinfo->null_index;
-               }
-               else
-               {
-                   bool        equal = false;
-                   int         cur_offset;
-
-                   cur_offset = partition_bound_bsearch(key,
-                                                        partdesc->boundinfo,
-                                                        values,
-                                                        false,
-                                                        &equal);
-                   if (cur_offset >= 0 && equal)
-                       cur_index = partdesc->boundinfo->indexes[cur_offset];
-               }
-               break;
+       case PARTITION_STRATEGY_RANGE:
+           {
+               bool        equal = false,
+                           range_partkey_has_null = false;
+               int         i;
 
-           case PARTITION_STRATEGY_RANGE:
+               /*
+                * No range includes NULL, so this will be accepted by the
+                * default partition if there is one, and otherwise
+                * rejected.
+                */
+               for (i = 0; i < key->partnatts; i++)
                {
-                   bool        equal = false,
-                               range_partkey_has_null = false;
-                   int         cur_offset;
-                   int         i;
-
-                   /*
-                    * No range includes NULL, so this will be accepted by the
-                    * default partition if there is one, and otherwise
-                    * rejected.
-                    */
-                   for (i = 0; i < key->partnatts; i++)
+                   if (isnull[i] &&
+                       partition_bound_has_default(partdesc->boundinfo))
                    {
-                       if (isnull[i] &&
-                           partition_bound_has_default(partdesc->boundinfo))
-                       {
-                           range_partkey_has_null = true;
-                           break;
-                       }
-                       else if (isnull[i])
-                       {
-                           *failed_at = parent;
-                           *failed_slot = slot;
-                           result = -1;
-                           goto error_exit;
-                       }
+                       range_partkey_has_null = true;
+                       part_index = partdesc->boundinfo->default_index;
                    }
+               }
 
-                   /*
-                    * No need to search for partition, as the null key will
-                    * be routed to the default partition.
-                    */
-                   if (range_partkey_has_null)
-                       break;
-
-                   cur_offset = partition_bound_bsearch(key,
-                                                        partdesc->boundinfo,
-                                                        values,
-                                                        false,
-                                                        &equal);
+               if (!range_partkey_has_null)
+               {
+                   bound_offset = partition_bound_bsearch(key,
+                                                      partdesc->boundinfo,
+                                                          values,
+                                                          false,
+                                                          &equal);
 
                    /*
-                    * The offset returned is such that the bound at
-                    * cur_offset is less than or equal to the tuple value, so
-                    * the bound at offset+1 is the upper bound.
+                    * The bound at bound_offset is less than or equal to the
+                    * tuple value, so the bound at offset+1 is the upper
+                    * bound of the partition we're looking for, if there
+                    * actually exists one.
                     */
-                   cur_index = partdesc->boundinfo->indexes[cur_offset + 1];
+                   part_index = partdesc->boundinfo->indexes[bound_offset + 1];
                }
-               break;
-
-           default:
-               elog(ERROR, "unexpected partition strategy: %d",
-                    (int) key->strategy);
-       }
-
-       /*
-        * cur_index < 0 means we failed to find a partition of this parent.
-        * Use the default partition, if there is one.
-        */
-       if (cur_index < 0)
-           cur_index = partdesc->boundinfo->default_index;
-
-       /*
-        * If cur_index is still less than 0 at this point, there's no
-        * partition for this tuple.  Otherwise, we either found the leaf
-        * partition, or a child partitioned table through which we have to
-        * route the tuple.
-        */
-       if (cur_index < 0)
-       {
-           result = -1;
-           *failed_at = parent;
-           *failed_slot = slot;
-           break;
-       }
-       else if (parent->indexes[cur_index] >= 0)
-       {
-           result = parent->indexes[cur_index];
+           }
            break;
-       }
-       else
-           parent = pd[-parent->indexes[cur_index]];
+
+       default:
+           elog(ERROR, "unexpected partition strategy: %d",
+                (int) key->strategy);
    }
 
-error_exit:
-   ecxt->ecxt_scantuple = ecxt_scantuple_old;
-   return result;
+   /*
+    * part_index < 0 means we failed to find a partition of this parent.
+    * Use the default partition, if there is one.
+    */
+   if (part_index < 0)
+       part_index = partdesc->boundinfo->default_index;
+
+   return part_index;
 }
 
 /*
index 8f1a8ede333a6d469bfe2df8f6c935a54d27810a..d6b235ca79ff177790b3348681b002275c9141da 100644 (file)
@@ -27,6 +27,7 @@
 #include "commands/copy.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
+#include "executor/execPartition.h"
 #include "executor/executor.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
index 083b20f3fee0cb957f59d754e5128e153952a58b..cc09895fa5c469c4ae6535f941f65ef3ccb5bfa2 100644 (file)
@@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
        execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o \
+       execMain.o execParallel.o execPartition.o execProcnode.o \
        execReplication.o execScan.o execSRF.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
index 47f21316429543cd73300cff4adf1bc698b558f2..dbaa47f2d30b057247ed8efae2ea860adc2d6735 100644 (file)
@@ -43,7 +43,6 @@
 #include "access/xact.h"
 #include "catalog/namespace.h"
 #include "catalog/partition.h"
-#include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_publication.h"
 #include "commands/matview.h"
 #include "commands/trigger.h"
@@ -98,14 +97,8 @@ static char *ExecBuildSlotValueDescription(Oid reloid,
                              TupleDesc tupdesc,
                              Bitmapset *modifiedCols,
                              int maxfieldlen);
-static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
-                                    Datum *values,
-                                    bool *isnull,
-                                    int maxfieldlen);
 static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
                  Plan *planTree);
-static void ExecPartitionCheck(ResultRelInfo *resultRelInfo,
-                  TupleTableSlot *slot, EState *estate);
 
 /*
  * Note that GetUpdatedColumns() also exists in commands/trigger.c.  There does
@@ -1854,8 +1847,10 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 
 /*
  * ExecPartitionCheck --- check that tuple meets the partition constraint.
+ *
+ * Exported in executor.h for outside use.
  */
-static void
+void
 ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
                   EState *estate)
 {
@@ -3245,258 +3240,3 @@ EvalPlanQualEnd(EPQState *epqstate)
    epqstate->planstate = NULL;
    epqstate->origslot = NULL;
 }
-
-/*
- * ExecSetupPartitionTupleRouting - set up information needed during
- * tuple routing for partitioned tables
- *
- * Output arguments:
- * 'pd' receives an array of PartitionDispatch objects with one entry for
- *     every partitioned table in the partition tree
- * 'partitions' receives an array of ResultRelInfo* objects with one entry for
- *     every leaf partition in the partition tree
- * 'tup_conv_maps' receives an array of TupleConversionMap objects with one
- *     entry for every leaf partition (required to convert input tuple based
- *     on the root table's rowtype to a leaf partition's rowtype after tuple
- *     routing is done)
- * 'partition_tuple_slot' receives a standalone TupleTableSlot to be used
- *     to manipulate any given leaf partition's rowtype after that partition
- *     is chosen by tuple-routing.
- * 'num_parted' receives the number of partitioned tables in the partition
- *     tree (= the number of entries in the 'pd' output array)
- * 'num_partitions' receives the number of leaf partitions in the partition
- *     tree (= the number of entries in the 'partitions' and 'tup_conv_maps'
- *     output arrays
- *
- * Note that all the relations in the partition tree are locked using the
- * RowExclusiveLock mode upon return from this function.
- */
-void
-ExecSetupPartitionTupleRouting(Relation rel,
-                              Index resultRTindex,
-                              EState *estate,
-                              PartitionDispatch **pd,
-                              ResultRelInfo ***partitions,
-                              TupleConversionMap ***tup_conv_maps,
-                              TupleTableSlot **partition_tuple_slot,
-                              int *num_parted, int *num_partitions)
-{
-   TupleDesc   tupDesc = RelationGetDescr(rel);
-   List       *leaf_parts;
-   ListCell   *cell;
-   int         i;
-   ResultRelInfo *leaf_part_rri;
-
-   /*
-    * Get the information about the partition tree after locking all the
-    * partitions.
-    */
-   (void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL);
-   *pd = RelationGetPartitionDispatchInfo(rel, num_parted, &leaf_parts);
-   *num_partitions = list_length(leaf_parts);
-   *partitions = (ResultRelInfo **) palloc(*num_partitions *
-                                           sizeof(ResultRelInfo *));
-   *tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions *
-                                                    sizeof(TupleConversionMap *));
-
-   /*
-    * Initialize an empty slot that will be used to manipulate tuples of any
-    * given partition's rowtype.  It is attached to the caller-specified node
-    * (such as ModifyTableState) and released when the node finishes
-    * processing.
-    */
-   *partition_tuple_slot = MakeTupleTableSlot();
-
-   leaf_part_rri = (ResultRelInfo *) palloc0(*num_partitions *
-                                             sizeof(ResultRelInfo));
-   i = 0;
-   foreach(cell, leaf_parts)
-   {
-       Relation    partrel;
-       TupleDesc   part_tupdesc;
-
-       /*
-        * We locked all the partitions above including the leaf partitions.
-        * Note that each of the relations in *partitions are eventually
-        * closed by the caller.
-        */
-       partrel = heap_open(lfirst_oid(cell), NoLock);
-       part_tupdesc = RelationGetDescr(partrel);
-
-       /*
-        * Save a tuple conversion map to convert a tuple routed to this
-        * partition from the parent's type to the partition's.
-        */
-       (*tup_conv_maps)[i] = convert_tuples_by_name(tupDesc, part_tupdesc,
-                                                    gettext_noop("could not convert row type"));
-
-       InitResultRelInfo(leaf_part_rri,
-                         partrel,
-                         resultRTindex,
-                         rel,
-                         estate->es_instrument);
-
-       /*
-        * Verify result relation is a valid target for INSERT.
-        */
-       CheckValidResultRel(leaf_part_rri, CMD_INSERT);
-
-       /*
-        * Open partition indices (remember we do not support ON CONFLICT in
-        * case of partitioned tables, so we do not need support information
-        * for speculative insertion)
-        */
-       if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex &&
-           leaf_part_rri->ri_IndexRelationDescs == NULL)
-           ExecOpenIndices(leaf_part_rri, false);
-
-       estate->es_leaf_result_relations =
-           lappend(estate->es_leaf_result_relations, leaf_part_rri);
-
-       (*partitions)[i] = leaf_part_rri++;
-       i++;
-   }
-}
-
-/*
- * ExecFindPartition -- Find a leaf partition in the partition tree rooted
- * at parent, for the heap tuple contained in *slot
- *
- * estate must be non-NULL; we'll need it to compute any expressions in the
- * partition key(s)
- *
- * If no leaf partition is found, this routine errors out with the appropriate
- * error message, else it returns the leaf partition sequence number returned
- * by get_partition_for_tuple() unchanged.
- */
-int
-ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
-                 TupleTableSlot *slot, EState *estate)
-{
-   int         result;
-   PartitionDispatchData *failed_at;
-   TupleTableSlot *failed_slot;
-
-   /*
-    * First check the root table's partition constraint, if any.  No point in
-    * routing the tuple if it doesn't belong in the root table itself.
-    */
-   if (resultRelInfo->ri_PartitionCheck)
-       ExecPartitionCheck(resultRelInfo, slot, estate);
-
-   result = get_partition_for_tuple(pd, slot, estate,
-                                    &failed_at, &failed_slot);
-   if (result < 0)
-   {
-       Relation    failed_rel;
-       Datum       key_values[PARTITION_MAX_KEYS];
-       bool        key_isnull[PARTITION_MAX_KEYS];
-       char       *val_desc;
-       ExprContext *ecxt = GetPerTupleExprContext(estate);
-
-       failed_rel = failed_at->reldesc;
-       ecxt->ecxt_scantuple = failed_slot;
-       FormPartitionKeyDatum(failed_at, failed_slot, estate,
-                             key_values, key_isnull);
-       val_desc = ExecBuildSlotPartitionKeyDescription(failed_rel,
-                                                       key_values,
-                                                       key_isnull,
-                                                       64);
-       Assert(OidIsValid(RelationGetRelid(failed_rel)));
-       ereport(ERROR,
-               (errcode(ERRCODE_CHECK_VIOLATION),
-                errmsg("no partition of relation \"%s\" found for row",
-                       RelationGetRelationName(failed_rel)),
-                val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0));
-   }
-
-   return result;
-}
-
-/*
- * BuildSlotPartitionKeyDescription
- *
- * This works very much like BuildIndexValueDescription() and is currently
- * used for building error messages when ExecFindPartition() fails to find
- * partition for a row.
- */
-static char *
-ExecBuildSlotPartitionKeyDescription(Relation rel,
-                                    Datum *values,
-                                    bool *isnull,
-                                    int maxfieldlen)
-{
-   StringInfoData buf;
-   PartitionKey key = RelationGetPartitionKey(rel);
-   int         partnatts = get_partition_natts(key);
-   int         i;
-   Oid         relid = RelationGetRelid(rel);
-   AclResult   aclresult;
-
-   if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
-       return NULL;
-
-   /* If the user has table-level access, just go build the description. */
-   aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
-   if (aclresult != ACLCHECK_OK)
-   {
-       /*
-        * Step through the columns of the partition key and make sure the
-        * user has SELECT rights on all of them.
-        */
-       for (i = 0; i < partnatts; i++)
-       {
-           AttrNumber  attnum = get_partition_col_attnum(key, i);
-
-           /*
-            * If this partition key column is an expression, we return no
-            * detail rather than try to figure out what column(s) the
-            * expression includes and if the user has SELECT rights on them.
-            */
-           if (attnum == InvalidAttrNumber ||
-               pg_attribute_aclcheck(relid, attnum, GetUserId(),
-                                     ACL_SELECT) != ACLCHECK_OK)
-               return NULL;
-       }
-   }
-
-   initStringInfo(&buf);
-   appendStringInfo(&buf, "(%s) = (",
-                    pg_get_partkeydef_columns(relid, true));
-
-   for (i = 0; i < partnatts; i++)
-   {
-       char       *val;
-       int         vallen;
-
-       if (isnull[i])
-           val = "null";
-       else
-       {
-           Oid         foutoid;
-           bool        typisvarlena;
-
-           getTypeOutputInfo(get_partition_col_typid(key, i),
-                             &foutoid, &typisvarlena);
-           val = OidOutputFunctionCall(foutoid, values[i]);
-       }
-
-       if (i > 0)
-           appendStringInfoString(&buf, ", ");
-
-       /* truncate if needed */
-       vallen = strlen(val);
-       if (vallen <= maxfieldlen)
-           appendStringInfoString(&buf, val);
-       else
-       {
-           vallen = pg_mbcliplen(val, vallen, maxfieldlen);
-           appendBinaryStringInfo(&buf, val, vallen);
-           appendStringInfoString(&buf, "...");
-       }
-   }
-
-   appendStringInfoChar(&buf, ')');
-
-   return buf.data;
-}
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
new file mode 100644 (file)
index 0000000..d275cef
--- /dev/null
@@ -0,0 +1,560 @@
+/*-------------------------------------------------------------------------
+ *
+ * execPartition.c
+ *   Support routines for partitioning.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *   src/backend/executor/execPartition.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "catalog/pg_inherits_fn.h"
+#include "executor/execPartition.h"
+#include "executor/executor.h"
+#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/lsyscache.h"
+#include "utils/rls.h"
+#include "utils/ruleutils.h"
+
+static PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel,
+                                int *num_parted, List **leaf_part_oids);
+static void get_partition_dispatch_recurse(Relation rel, Relation parent,
+                              List **pds, List **leaf_part_oids);
+static void FormPartitionKeyDatum(PartitionDispatch pd,
+                     TupleTableSlot *slot,
+                     EState *estate,
+                     Datum *values,
+                     bool *isnull);
+static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
+                                    Datum *values,
+                                    bool *isnull,
+                                    int maxfieldlen);
+
+/*
+ * ExecSetupPartitionTupleRouting - set up information needed during
+ * tuple routing for partitioned tables
+ *
+ * Output arguments:
+ * 'pd' receives an array of PartitionDispatch objects with one entry for
+ *     every partitioned table in the partition tree
+ * 'partitions' receives an array of ResultRelInfo* objects with one entry for
+ *     every leaf partition in the partition tree
+ * 'tup_conv_maps' receives an array of TupleConversionMap objects with one
+ *     entry for every leaf partition (required to convert input tuple based
+ *     on the root table's rowtype to a leaf partition's rowtype after tuple
+ *     routing is done)
+ * 'partition_tuple_slot' receives a standalone TupleTableSlot to be used
+ *     to manipulate any given leaf partition's rowtype after that partition
+ *     is chosen by tuple-routing.
+ * 'num_parted' receives the number of partitioned tables in the partition
+ *     tree (= the number of entries in the 'pd' output array)
+ * 'num_partitions' receives the number of leaf partitions in the partition
+ *     tree (= the number of entries in the 'partitions' and 'tup_conv_maps'
+ *     output arrays
+ *
+ * Note that all the relations in the partition tree are locked using the
+ * RowExclusiveLock mode upon return from this function.
+ */
+void
+ExecSetupPartitionTupleRouting(Relation rel,
+                              Index resultRTindex,
+                              EState *estate,
+                              PartitionDispatch **pd,
+                              ResultRelInfo ***partitions,
+                              TupleConversionMap ***tup_conv_maps,
+                              TupleTableSlot **partition_tuple_slot,
+                              int *num_parted, int *num_partitions)
+{
+   TupleDesc   tupDesc = RelationGetDescr(rel);
+   List       *leaf_parts;
+   ListCell   *cell;
+   int         i;
+   ResultRelInfo *leaf_part_rri;
+
+   /*
+    * Get the information about the partition tree after locking all the
+    * partitions.
+    */
+   (void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL);
+   *pd = RelationGetPartitionDispatchInfo(rel, num_parted, &leaf_parts);
+   *num_partitions = list_length(leaf_parts);
+   *partitions = (ResultRelInfo **) palloc(*num_partitions *
+                                           sizeof(ResultRelInfo *));
+   *tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions *
+                                                    sizeof(TupleConversionMap *));
+
+   /*
+    * Initialize an empty slot that will be used to manipulate tuples of any
+    * given partition's rowtype.  It is attached to the caller-specified node
+    * (such as ModifyTableState) and released when the node finishes
+    * processing.
+    */
+   *partition_tuple_slot = MakeTupleTableSlot();
+
+   leaf_part_rri = (ResultRelInfo *) palloc0(*num_partitions *
+                                             sizeof(ResultRelInfo));
+   i = 0;
+   foreach(cell, leaf_parts)
+   {
+       Relation    partrel;
+       TupleDesc   part_tupdesc;
+
+       /*
+        * We locked all the partitions above including the leaf partitions.
+        * Note that each of the relations in *partitions are eventually
+        * closed by the caller.
+        */
+       partrel = heap_open(lfirst_oid(cell), NoLock);
+       part_tupdesc = RelationGetDescr(partrel);
+
+       /*
+        * Save a tuple conversion map to convert a tuple routed to this
+        * partition from the parent's type to the partition's.
+        */
+       (*tup_conv_maps)[i] = convert_tuples_by_name(tupDesc, part_tupdesc,
+                                                    gettext_noop("could not convert row type"));
+
+       InitResultRelInfo(leaf_part_rri,
+                         partrel,
+                         resultRTindex,
+                         rel,
+                         estate->es_instrument);
+
+       /*
+        * Verify result relation is a valid target for INSERT.
+        */
+       CheckValidResultRel(leaf_part_rri, CMD_INSERT);
+
+       /*
+        * Open partition indices (remember we do not support ON CONFLICT in
+        * case of partitioned tables, so we do not need support information
+        * for speculative insertion)
+        */
+       if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex &&
+           leaf_part_rri->ri_IndexRelationDescs == NULL)
+           ExecOpenIndices(leaf_part_rri, false);
+
+       estate->es_leaf_result_relations =
+           lappend(estate->es_leaf_result_relations, leaf_part_rri);
+
+       (*partitions)[i] = leaf_part_rri++;
+       i++;
+   }
+}
+
+/*
+ * ExecFindPartition -- Find a leaf partition in the partition tree rooted
+ * at parent, for the heap tuple contained in *slot
+ *
+ * estate must be non-NULL; we'll need it to compute any expressions in the
+ * partition key(s)
+ *
+ * If no leaf partition is found, this routine errors out with the appropriate
+ * error message, else it returns the leaf partition sequence number
+ * as an index into the array of (ResultRelInfos of) all leaf partitions in
+ * the partition tree.
+ */
+int
+ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
+                 TupleTableSlot *slot, EState *estate)
+{
+   int         result;
+   Datum       values[PARTITION_MAX_KEYS];
+   bool        isnull[PARTITION_MAX_KEYS];
+   Relation    rel;
+   PartitionDispatch parent;
+   ExprContext *ecxt = GetPerTupleExprContext(estate);
+   TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
+
+   /*
+    * First check the root table's partition constraint, if any.  No point in
+    * routing the tuple if it doesn't belong in the root table itself.
+    */
+   if (resultRelInfo->ri_PartitionCheck)
+       ExecPartitionCheck(resultRelInfo, slot, estate);
+
+   /* start with the root partitioned table */
+   parent = pd[0];
+   while (true)
+   {
+       PartitionDesc   partdesc;
+       TupleTableSlot *myslot = parent->tupslot;
+       TupleConversionMap *map = parent->tupmap;
+       int     cur_index = -1;
+
+       rel = parent->reldesc;
+       partdesc = RelationGetPartitionDesc(rel);
+
+       /*
+        * Convert the tuple to this parent's layout so that we can do certain
+        * things we do below.
+        */
+       if (myslot != NULL && map != NULL)
+       {
+           HeapTuple   tuple = ExecFetchSlotTuple(slot);
+
+           ExecClearTuple(myslot);
+           tuple = do_convert_tuple(tuple, map);
+           ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
+           slot = myslot;
+       }
+
+       /* Quick exit */
+       if (partdesc->nparts == 0)
+       {
+           result = -1;
+           break;
+       }
+
+       /*
+        * Extract partition key from tuple. Expression evaluation machinery
+        * that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
+        * point to the correct tuple slot.  The slot might have changed from
+        * what was used for the parent table if the table of the current
+        * partitioning level has different tuple descriptor from the parent.
+        * So update ecxt_scantuple accordingly.
+        */
+       ecxt->ecxt_scantuple = slot;
+       FormPartitionKeyDatum(parent, slot, estate, values, isnull);
+       cur_index = get_partition_for_tuple(rel, values, isnull);
+
+       /*
+        * cur_index < 0 means we failed to find a partition of this parent.
+        * cur_index >= 0 means we either found the leaf partition, or the
+        * next parent to find a partition of.
+        */
+       if (cur_index < 0)
+       {
+           result = -1;
+           break;
+       }
+       else if (parent->indexes[cur_index] >= 0)
+       {
+           result = parent->indexes[cur_index];
+           break;
+       }
+       else
+           parent = pd[-parent->indexes[cur_index]];
+   }
+
+   /* A partition was not found. */
+   if (result < 0)
+   {
+       char       *val_desc;
+
+       val_desc = ExecBuildSlotPartitionKeyDescription(rel,
+                                                       values, isnull, 64);
+       Assert(OidIsValid(RelationGetRelid(rel)));
+       ereport(ERROR,
+               (errcode(ERRCODE_CHECK_VIOLATION),
+                errmsg("no partition of relation \"%s\" found for row",
+                       RelationGetRelationName(rel)),
+                val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0));
+   }
+
+   ecxt->ecxt_scantuple = ecxt_scantuple_old;
+   return result;
+}
+
+/*
+ * RelationGetPartitionDispatchInfo
+ *     Returns information necessary to route tuples down a partition tree
+ *
+ * The number of elements in the returned array (that is, the number of
+ * PartitionDispatch objects for the partitioned tables in the partition tree)
+ * is returned in *num_parted and a list of the OIDs of all the leaf
+ * partitions of rel is returned in *leaf_part_oids.
+ *
+ * All the relations in the partition tree (including 'rel') must have been
+ * locked (using at least the AccessShareLock) by the caller.
+ */
+static PartitionDispatch *
+RelationGetPartitionDispatchInfo(Relation rel,
+                                int *num_parted, List **leaf_part_oids)
+{
+   List       *pdlist = NIL;
+   PartitionDispatchData **pd;
+   ListCell   *lc;
+   int         i;
+
+   Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+
+   *num_parted = 0;
+   *leaf_part_oids = NIL;
+
+   get_partition_dispatch_recurse(rel, NULL, &pdlist, leaf_part_oids);
+   *num_parted = list_length(pdlist);
+   pd = (PartitionDispatchData **) palloc(*num_parted *
+                                          sizeof(PartitionDispatchData *));
+   i = 0;
+   foreach(lc, pdlist)
+   {
+       pd[i++] = lfirst(lc);
+   }
+
+   return pd;
+}
+
+/*
+ * get_partition_dispatch_recurse
+ *     Recursively expand partition tree rooted at rel
+ *
+ * As the partition tree is expanded in a depth-first manner, we maintain two
+ * global lists: of PartitionDispatch objects corresponding to partitioned
+ * tables in *pds and of the leaf partition OIDs in *leaf_part_oids.
+ *
+ * Note that the order of OIDs of leaf partitions in leaf_part_oids matches
+ * the order in which the planner's expand_partitioned_rtentry() processes
+ * them.  It's not necessarily the case that the offsets match up exactly,
+ * because constraint exclusion might prune away some partitions on the
+ * planner side, whereas we'll always have the complete list; but unpruned
+ * partitions will appear in the same order in the plan as they are returned
+ * here.
+ */
+static void
+get_partition_dispatch_recurse(Relation rel, Relation parent,
+                              List **pds, List **leaf_part_oids)
+{
+   TupleDesc   tupdesc = RelationGetDescr(rel);
+   PartitionDesc partdesc = RelationGetPartitionDesc(rel);
+   PartitionKey partkey = RelationGetPartitionKey(rel);
+   PartitionDispatch pd;
+   int         i;
+
+   check_stack_depth();
+
+   /* Build a PartitionDispatch for this table and add it to *pds. */
+   pd = (PartitionDispatch) palloc(sizeof(PartitionDispatchData));
+   *pds = lappend(*pds, pd);
+   pd->reldesc = rel;
+   pd->key = partkey;
+   pd->keystate = NIL;
+   pd->partdesc = partdesc;
+   if (parent != NULL)
+   {
+       /*
+        * For every partitioned table other than the root, we must store a
+        * tuple table slot initialized with its tuple descriptor and a tuple
+        * conversion map to convert a tuple from its parent's rowtype to its
+        * own. That is to make sure that we are looking at the correct row
+        * using the correct tuple descriptor when computing its partition key
+        * for tuple routing.
+        */
+       pd->tupslot = MakeSingleTupleTableSlot(tupdesc);
+       pd->tupmap = convert_tuples_by_name(RelationGetDescr(parent),
+                                           tupdesc,
+                                           gettext_noop("could not convert row type"));
+   }
+   else
+   {
+       /* Not required for the root partitioned table */
+       pd->tupslot = NULL;
+       pd->tupmap = NULL;
+   }
+
+   /*
+    * Go look at each partition of this table.  If it's a leaf partition,
+    * simply add its OID to *leaf_part_oids.  If it's a partitioned table,
+    * recursively call get_partition_dispatch_recurse(), so that its
+    * partitions are processed as well and a corresponding PartitionDispatch
+    * object gets added to *pds.
+    *
+    * About the values in pd->indexes: for a leaf partition, it contains the
+    * leaf partition's position in the global list *leaf_part_oids minus 1,
+    * whereas for a partitioned table partition, it contains the partition's
+    * position in the global list *pds multiplied by -1.  The latter is
+    * multiplied by -1 to distinguish partitioned tables from leaf partitions
+    * when going through the values in pd->indexes.  So, for example, when
+    * using it during tuple-routing, encountering a value >= 0 means we found
+    * a leaf partition.  It is immediately returned as the index in the array
+    * of ResultRelInfos of all the leaf partitions, using which we insert the
+    * tuple into that leaf partition.  A negative value means we found a
+    * partitioned table.  The value multiplied by -1 is returned as the index
+    * in the array of PartitionDispatch objects of all partitioned tables in
+    * the tree.  This value is used to continue the search in the next level
+    * of the partition tree.
+    */
+   pd->indexes = (int *) palloc(partdesc->nparts * sizeof(int));
+   for (i = 0; i < partdesc->nparts; i++)
+   {
+       Oid         partrelid = partdesc->oids[i];
+
+       if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE)
+       {
+           *leaf_part_oids = lappend_oid(*leaf_part_oids, partrelid);
+           pd->indexes[i] = list_length(*leaf_part_oids) - 1;
+       }
+       else
+       {
+           /*
+            * We assume all tables in the partition tree were already locked
+            * by the caller.
+            */
+           Relation    partrel = heap_open(partrelid, NoLock);
+
+           pd->indexes[i] = -list_length(*pds);
+           get_partition_dispatch_recurse(partrel, rel, pds, leaf_part_oids);
+       }
+   }
+}
+
+/* ----------------
+ *     FormPartitionKeyDatum
+ *         Construct values[] and isnull[] arrays for the partition key
+ *         of a tuple.
+ *
+ * pd              Partition dispatch object of the partitioned table
+ * slot            Heap tuple from which to extract partition key
+ * estate          executor state for evaluating any partition key
+ *                 expressions (must be non-NULL)
+ * values          Array of partition key Datums (output area)
+ * isnull          Array of is-null indicators (output area)
+ *
+ * the ecxt_scantuple slot of estate's per-tuple expr context must point to
+ * the heap tuple passed in.
+ * ----------------
+ */
+static void
+FormPartitionKeyDatum(PartitionDispatch pd,
+                     TupleTableSlot *slot,
+                     EState *estate,
+                     Datum *values,
+                     bool *isnull)
+{
+   ListCell   *partexpr_item;
+   int         i;
+
+   if (pd->key->partexprs != NIL && pd->keystate == NIL)
+   {
+       /* Check caller has set up context correctly */
+       Assert(estate != NULL &&
+              GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
+
+       /* First time through, set up expression evaluation state */
+       pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
+   }
+
+   partexpr_item = list_head(pd->keystate);
+   for (i = 0; i < pd->key->partnatts; i++)
+   {
+       AttrNumber  keycol = pd->key->partattrs[i];
+       Datum       datum;
+       bool        isNull;
+
+       if (keycol != 0)
+       {
+           /* Plain column; get the value directly from the heap tuple */
+           datum = slot_getattr(slot, keycol, &isNull);
+       }
+       else
+       {
+           /* Expression; need to evaluate it */
+           if (partexpr_item == NULL)
+               elog(ERROR, "wrong number of partition key expressions");
+           datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
+                                             GetPerTupleExprContext(estate),
+                                             &isNull);
+           partexpr_item = lnext(partexpr_item);
+       }
+       values[i] = datum;
+       isnull[i] = isNull;
+   }
+
+   if (partexpr_item != NULL)
+       elog(ERROR, "wrong number of partition key expressions");
+}
+
+/*
+ * BuildSlotPartitionKeyDescription
+ *
+ * This works very much like BuildIndexValueDescription() and is currently
+ * used for building error messages when ExecFindPartition() fails to find
+ * partition for a row.
+ */
+static char *
+ExecBuildSlotPartitionKeyDescription(Relation rel,
+                                    Datum *values,
+                                    bool *isnull,
+                                    int maxfieldlen)
+{
+   StringInfoData buf;
+   PartitionKey key = RelationGetPartitionKey(rel);
+   int         partnatts = get_partition_natts(key);
+   int         i;
+   Oid         relid = RelationGetRelid(rel);
+   AclResult   aclresult;
+
+   if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
+       return NULL;
+
+   /* If the user has table-level access, just go build the description. */
+   aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
+   if (aclresult != ACLCHECK_OK)
+   {
+       /*
+        * Step through the columns of the partition key and make sure the
+        * user has SELECT rights on all of them.
+        */
+       for (i = 0; i < partnatts; i++)
+       {
+           AttrNumber  attnum = get_partition_col_attnum(key, i);
+
+           /*
+            * If this partition key column is an expression, we return no
+            * detail rather than try to figure out what column(s) the
+            * expression includes and if the user has SELECT rights on them.
+            */
+           if (attnum == InvalidAttrNumber ||
+               pg_attribute_aclcheck(relid, attnum, GetUserId(),
+                                     ACL_SELECT) != ACLCHECK_OK)
+               return NULL;
+       }
+   }
+
+   initStringInfo(&buf);
+   appendStringInfo(&buf, "(%s) = (",
+                    pg_get_partkeydef_columns(relid, true));
+
+   for (i = 0; i < partnatts; i++)
+   {
+       char       *val;
+       int         vallen;
+
+       if (isnull[i])
+           val = "null";
+       else
+       {
+           Oid         foutoid;
+           bool        typisvarlena;
+
+           getTypeOutputInfo(get_partition_col_typid(key, i),
+                             &foutoid, &typisvarlena);
+           val = OidOutputFunctionCall(foutoid, values[i]);
+       }
+
+       if (i > 0)
+           appendStringInfoString(&buf, ", ");
+
+       /* truncate if needed */
+       vallen = strlen(val);
+       if (vallen <= maxfieldlen)
+           appendStringInfoString(&buf, val);
+       else
+       {
+           vallen = pg_mbcliplen(val, vallen, maxfieldlen);
+           appendBinaryStringInfo(&buf, val, vallen);
+           appendStringInfoString(&buf, "...");
+       }
+   }
+
+   appendStringInfoChar(&buf, ')');
+
+   return buf.data;
+}
index 0027d21253f0521edad0c5ae99e03e288096b521..503b89f6063b7deea455faf53449c5cbbdb08a29 100644 (file)
@@ -40,6 +40,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "commands/trigger.h"
+#include "executor/execPartition.h"
 #include "executor/executor.h"
 #include "executor/nodeModifyTable.h"
 #include "foreign/fdwapi.h"
index 8acc01a876841537254b16a1b20ba13d544e685f..295e9d224ebd22a9f10a09665b18787a83b7b86f 100644 (file)
@@ -42,37 +42,6 @@ typedef struct PartitionDescData
 
 typedef struct PartitionDescData *PartitionDesc;
 
-/*-----------------------
- * PartitionDispatch - information about one partitioned table in a partition
- * hierarchy required to route a tuple to one of its partitions
- *
- * reldesc     Relation descriptor of the table
- * key         Partition key information of the table
- * keystate    Execution state required for expressions in the partition key
- * partdesc    Partition descriptor of the table
- * tupslot     A standalone TupleTableSlot initialized with this table's tuple
- *             descriptor
- * tupmap      TupleConversionMap to convert from the parent's rowtype to
- *             this table's rowtype (when extracting the partition key of a
- *             tuple just before routing it through this table)
- * indexes     Array with partdesc->nparts members (for details on what
- *             individual members represent, see how they are set in
- *             RelationGetPartitionDispatchInfo())
- *-----------------------
- */
-typedef struct PartitionDispatchData
-{
-   Relation    reldesc;
-   PartitionKey key;
-   List       *keystate;       /* list of ExprState */
-   PartitionDesc partdesc;
-   TupleTableSlot *tupslot;
-   TupleConversionMap *tupmap;
-   int        *indexes;
-} PartitionDispatchData;
-
-typedef struct PartitionDispatchData *PartitionDispatch;
-
 extern void RelationBuildPartitionDesc(Relation relation);
 extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
                       bool *parttypbyval, PartitionBoundInfo b1,
@@ -91,19 +60,6 @@ extern List *map_partition_varattnos(List *expr, int target_varno,
 extern List *RelationGetPartitionQual(Relation rel);
 extern Expr *get_partition_qual_relid(Oid relid);
 
-/* For tuple routing */
-extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel,
-                                int *num_parted, List **leaf_part_oids);
-extern void FormPartitionKeyDatum(PartitionDispatch pd,
-                     TupleTableSlot *slot,
-                     EState *estate,
-                     Datum *values,
-                     bool *isnull);
-extern int get_partition_for_tuple(PartitionDispatch *pd,
-                       TupleTableSlot *slot,
-                       EState *estate,
-                       PartitionDispatchData **failed_at,
-                       TupleTableSlot **failed_slot);
 extern Oid get_default_oid_from_partdesc(PartitionDesc partdesc);
 extern Oid get_default_partition_oid(Oid parentId);
 extern void update_default_partition_oid(Oid parentId, Oid defaultPartId);
@@ -111,4 +67,8 @@ extern void check_default_allows_bound(Relation parent, Relation defaultRel,
                           PartitionBoundSpec *new_spec);
 extern List *get_proposed_default_constraint(List *new_part_constaints);
 
+/* For tuple routing */
+extern int get_partition_for_tuple(Relation relation, Datum *values,
+                           bool *isnull);
+
 #endif                         /* PARTITION_H */
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
new file mode 100644 (file)
index 0000000..64e5aab
--- /dev/null
@@ -0,0 +1,65 @@
+/*--------------------------------------------------------------------
+ * execPartition.h
+ *     POSTGRES partitioning executor interface
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *     src/include/executor/execPartition.h
+ *--------------------------------------------------------------------
+ */
+
+#ifndef EXECPARTITION_H
+#define EXECPARTITION_H
+
+#include "catalog/partition.h"
+#include "nodes/execnodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+
+/*-----------------------
+ * PartitionDispatch - information about one partitioned table in a partition
+ * hierarchy required to route a tuple to one of its partitions
+ *
+ * reldesc     Relation descriptor of the table
+ * key         Partition key information of the table
+ * keystate    Execution state required for expressions in the partition key
+ * partdesc    Partition descriptor of the table
+ * tupslot     A standalone TupleTableSlot initialized with this table's tuple
+ *             descriptor
+ * tupmap      TupleConversionMap to convert from the parent's rowtype to
+ *             this table's rowtype (when extracting the partition key of a
+ *             tuple just before routing it through this table)
+ * indexes     Array with partdesc->nparts members (for details on what
+ *             individual members represent, see how they are set in
+ *             get_partition_dispatch_recurse())
+ *-----------------------
+ */
+typedef struct PartitionDispatchData
+{
+   Relation        reldesc;
+   PartitionKey    key;
+   List           *keystate;       /* list of ExprState */
+   PartitionDesc   partdesc;
+   TupleTableSlot *tupslot;
+   TupleConversionMap *tupmap;
+   int            *indexes;
+} PartitionDispatchData;
+
+typedef struct PartitionDispatchData *PartitionDispatch;
+
+extern void ExecSetupPartitionTupleRouting(Relation rel,
+                              Index resultRTindex,
+                              EState *estate,
+                              PartitionDispatch **pd,
+                              ResultRelInfo ***partitions,
+                              TupleConversionMap ***tup_conv_maps,
+                              TupleTableSlot **partition_tuple_slot,
+                              int *num_parted, int *num_partitions);
+extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
+                 PartitionDispatch *pd,
+                 TupleTableSlot *slot,
+                 EState *estate);
+
+#endif                         /* EXECPARTITION_H */
index c4ecf0d50f3e3c2d46afb1867a42cc281ec3bb42..bee4ebf2693426e781ecfc2df1880535295089a8 100644 (file)
@@ -188,6 +188,8 @@ extern void ExecCleanUpTriggerState(EState *estate);
 extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
                TupleTableSlot *slot, EState *estate);
+extern void ExecPartitionCheck(ResultRelInfo *resultRelInfo,
+                  TupleTableSlot *slot, EState *estate);
 extern void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
                     TupleTableSlot *slot, EState *estate);
 extern LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo);
@@ -206,18 +208,6 @@ extern void EvalPlanQualSetPlan(EPQState *epqstate,
 extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
                     HeapTuple tuple);
 extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
-extern void ExecSetupPartitionTupleRouting(Relation rel,
-                              Index resultRTindex,
-                              EState *estate,
-                              PartitionDispatch **pd,
-                              ResultRelInfo ***partitions,
-                              TupleConversionMap ***tup_conv_maps,
-                              TupleTableSlot **partition_tuple_slot,
-                              int *num_parted, int *num_partitions);
-extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
-                 PartitionDispatch *pd,
-                 TupleTableSlot *slot,
-                 EState *estate);
 
 #define EvalPlanQualSetSlot(epqstate, slot)  ((epqstate)->origslot = (slot))
 extern void EvalPlanQualFetchRowMarks(EPQState *epqstate);