]> granicus.if.org Git - postgresql/commitdiff
Fix RETURNING to work correctly with partition tuple routing.
authorRobert Haas <rhaas@postgresql.org>
Thu, 19 Jan 2017 18:20:11 +0000 (13:20 -0500)
committerRobert Haas <rhaas@postgresql.org>
Thu, 19 Jan 2017 18:20:11 +0000 (13:20 -0500)
In ExecInsert(), do not switch back to the root partitioned table
ResultRelInfo until after we finish ExecProcessReturning(), so that
RETURNING projection is done using the partition's descriptor.  For
the projection to work correctly, we must initialize the same for each
leaf partition during ModifyTableState initialization.

Amit Langote

src/backend/catalog/partition.c
src/backend/commands/tablecmds.c
src/backend/executor/execMain.c
src/backend/executor/nodeModifyTable.c
src/include/catalog/partition.h
src/test/regress/expected/insert.out
src/test/regress/sql/insert.sql

index 874e69d8d62e8e93164093e7352756ebfd0f69bc..3ee2b2a5aafb421106fb7cacca6056c12f989e35 100644 (file)
@@ -883,7 +883,8 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound)
  * different from the parent's.
  */
 List *
-map_partition_varattnos(List *expr, Relation partrel, Relation parent)
+map_partition_varattnos(List *expr, int target_varno,
+                                               Relation partrel, Relation parent)
 {
        TupleDesc       tupdesc = RelationGetDescr(parent);
        AttrNumber      attno;
@@ -908,7 +909,7 @@ map_partition_varattnos(List *expr, Relation partrel, Relation parent)
        }
 
        expr = (List *) map_variable_attnos((Node *) expr,
-                                                                               1, 0,
+                                                                               target_varno, 0,
                                                                                part_attnos,
                                                                                tupdesc->natts,
                                                                                &found_whole_row);
@@ -1540,8 +1541,9 @@ generate_partition_qual(Relation rel)
         * Change Vars to have partition's attnos instead of the parent's.
         * We do this after we concatenate the parent's quals, because
         * we want every Var in it to bear this relation's attnos.
+        * It's safe to assume varno = 1 here.
         */
-       result = map_partition_varattnos(result, rel, parent);
+       result = map_partition_varattnos(result, 1, rel, parent);
 
        /* Save a copy in the relcache */
        oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
index 06e43cbb3a273efb9127d65d253adedd91dee59d..18cac9ad2d97aaed65a26c2902c27d46cf2bf36a 100644 (file)
@@ -13454,6 +13454,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
                        constr = linitial(partConstraint);
                        my_constr = make_ands_implicit((Expr *) constr);
                        tab->partition_constraint = map_partition_varattnos(my_constr,
+                                                                                                                               1,
                                                                                                                                part_rel,
                                                                                                                                rel);
                        /* keep our lock until commit */
index 5457f8fbde5d9455514cf7d117737edb516b4790..e6edcc06c2393e58d6eb8c36339974e43c5940fb 100644 (file)
@@ -1279,10 +1279,10 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
                /*
                 * This is not our own partition constraint, but rather an ancestor's.
                 * So any Vars in it bear the ancestor's attribute numbers.  We must
-                * switch them to our own.
+                * switch them to our own. (dummy varno = 1)
                 */
                if (partition_check != NIL)
-                       partition_check = map_partition_varattnos(partition_check,
+                       partition_check = map_partition_varattnos(partition_check, 1,
                                                                                                          resultRelationDesc,
                                                                                                          partition_root);
        }
index 4692427e600319793da9afa5291180c28787e998..982f15d490e1d8225243a4b4630711ff3dbc23db 100644 (file)
@@ -262,7 +262,8 @@ ExecInsert(ModifyTableState *mtstate,
        Relation        resultRelationDesc;
        Oid                     newId;
        List       *recheckIndexes = NIL;
-       TupleTableSlot *oldslot = slot;
+       TupleTableSlot *oldslot = slot,
+                                  *result = NULL;
 
        /*
         * get the heap tuple out of the tuple table slot, making sure we have a
@@ -574,12 +575,6 @@ ExecInsert(ModifyTableState *mtstate,
 
        list_free(recheckIndexes);
 
-       if (saved_resultRelInfo)
-       {
-               resultRelInfo = saved_resultRelInfo;
-               estate->es_result_relation_info = resultRelInfo;
-       }
-
        /*
         * Check any WITH CHECK OPTION constraints from parent views.  We are
         * required to do this after testing all constraints and uniqueness
@@ -597,9 +592,12 @@ ExecInsert(ModifyTableState *mtstate,
 
        /* Process RETURNING if present */
        if (resultRelInfo->ri_projectReturning)
-               return ExecProcessReturning(resultRelInfo, slot, planSlot);
+               result = ExecProcessReturning(resultRelInfo, slot, planSlot);
 
-       return NULL;
+       if (saved_resultRelInfo)
+               estate->es_result_relation_info = saved_resultRelInfo;
+
+       return result;
 }
 
 /* ----------------------------------------------------------------
@@ -1786,6 +1784,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        {
                TupleTableSlot *slot;
                ExprContext *econtext;
+               List            *returningList;
 
                /*
                 * Initialize result tuple slot and assign its rowtype using the first
@@ -1818,6 +1817,32 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
                                                                         resultRelInfo->ri_RelationDesc->rd_att);
                        resultRelInfo++;
                }
+
+               /*
+                * Build a projection for each leaf partition rel.  Note that we
+                * didn't build the returningList for each partition within the
+                * planner, but simple translation of the varattnos for each
+                * partition will suffice.  This only occurs for the INSERT case;
+                * UPDATE/DELETE are handled above.
+                */
+               resultRelInfo = mtstate->mt_partitions;
+               returningList = linitial(node->returningLists);
+               for (i = 0; i < mtstate->mt_num_partitions; i++)
+               {
+                       Relation        partrel = resultRelInfo->ri_RelationDesc;
+                       List       *rlist,
+                                          *rliststate;
+
+                       /* varno = node->nominalRelation */
+                       rlist = map_partition_varattnos(returningList,
+                                                                                       node->nominalRelation,
+                                                                                       partrel, rel);
+                       rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
+                       resultRelInfo->ri_projectReturning =
+                               ExecBuildProjectionInfo(rliststate, econtext, slot,
+                                                                        resultRelInfo->ri_RelationDesc->rd_att);
+                       resultRelInfo++;
+               }
        }
        else
        {
index 537f0aad67545a907440401833b3bf0812995c21..df7dcce3312f370f4bb739f7570b329e51c3f908 100644 (file)
@@ -77,7 +77,8 @@ extern bool partition_bounds_equal(PartitionKey key,
 extern void check_new_partition_bound(char *relname, Relation parent, Node *bound);
 extern Oid get_partition_parent(Oid relid);
 extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound);
-extern List *map_partition_varattnos(List *expr, Relation partrel, Relation parent);
+extern List *map_partition_varattnos(List *expr, int target_varno,
+                                               Relation partrel, Relation parent);
 extern List *RelationGetPartitionQual(Relation rel);
 
 /* For tuple routing */
index 729d9ebbbc6286e3687c0899cc41cdaf43e956b6..538fe5b1819e9b89a7bc6b0d5edb1dce14cb711d 100644 (file)
@@ -364,5 +364,27 @@ DETAIL:  Failing row contains (1, 2).
 insert into p1 (a, b) values (2, 3);
 ERROR:  new row for relation "p11" violates partition constraint
 DETAIL:  Failing row contains (3, 2).
+-- check that RETURNING works correctly with tuple-routing
+alter table p drop constraint check_b;
+create table p12 partition of p1 for values from (5) to (10);
+create table p2 (b int not null, a int not null);
+alter table p attach partition p2 for values from (1, 10) to (1, 20);
+create table p3 partition of p for values from (1, 20) to (1, 30);
+create table p4 (like p);
+alter table p4 drop a;
+alter table p4 add a int not null;
+alter table p attach partition p4 for values from (1, 30) to (1, 40);
+with ins (a, b, c) as
+  (insert into p (b, a) select s.a, 1 from generate_series(2, 39) s(a) returning tableoid::regclass, *)
+  select a, b, min(c), max(c) from ins group by a, b order by 1;
+  a  | b | min | max 
+-----+---+-----+-----
+ p11 | 1 |   2 |   4
+ p12 | 1 |   5 |   9
+ p2  | 1 |  10 |  19
+ p3  | 1 |  20 |  29
+ p4  | 1 |  30 |  39
+(5 rows)
+
 -- cleanup
-drop table p, p1, p11;
+drop table p, p1, p11, p12, p2, p3, p4;
index 5509555fc578b4f49592d428153c66fd562dff72..3d5138a8e0704ab98d180d78a171e8908fb248f5 100644 (file)
@@ -226,5 +226,19 @@ insert into p values (1, 2);
 -- selected by tuple-routing
 insert into p1 (a, b) values (2, 3);
 
+-- check that RETURNING works correctly with tuple-routing
+alter table p drop constraint check_b;
+create table p12 partition of p1 for values from (5) to (10);
+create table p2 (b int not null, a int not null);
+alter table p attach partition p2 for values from (1, 10) to (1, 20);
+create table p3 partition of p for values from (1, 20) to (1, 30);
+create table p4 (like p);
+alter table p4 drop a;
+alter table p4 add a int not null;
+alter table p attach partition p4 for values from (1, 30) to (1, 40);
+with ins (a, b, c) as
+  (insert into p (b, a) select s.a, 1 from generate_series(2, 39) s(a) returning tableoid::regclass, *)
+  select a, b, min(c), max(c) from ins group by a, b order by 1;
+
 -- cleanup
-drop table p, p1, p11;
+drop table p, p1, p11, p12, p2, p3, p4;