estate->es_num_result_relations = numResultRelations;
/* es_result_relation_info is NULL except when within ModifyTable */
estate->es_result_relation_info = NULL;
+
+ /*
+ * In the partitioned result relation case, lock the non-leaf result
+ * relations too. We don't however need ResultRelInfos for them.
+ */
+ if (plannedstmt->nonleafResultRelations)
+ {
+ foreach(l, plannedstmt->nonleafResultRelations)
+ {
+ Index resultRelationIndex = lfirst_int(l);
+ Oid resultRelationOid;
+
+ resultRelationOid = getrelid(resultRelationIndex, rangeTable);
+ LockRelationOid(resultRelationOid, RowExclusiveLock);
+ }
+ }
}
else
{
/*
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
* before we initialize the plan tree, else we'd be risking lock upgrades.
- * While we are at it, build the ExecRowMark list.
+ * While we are at it, build the ExecRowMark list. Any partitioned child
+ * tables are ignored here (because isParent=true) and will be locked by
+ * the first Append or MergeAppend node that references them. (Note that
+ * the RowMarks corresponding to partitioned child tables are present in
+ * the same list as the rest, i.e., plannedstmt->rowMarks.)
*/
estate->es_rowMarks = NIL;
foreach(l, plannedstmt->rowMarks)
pstmt->planTree = plan;
pstmt->rtable = estate->es_range_table;
pstmt->resultRelations = NIL;
+ pstmt->nonleafResultRelations = NIL;
pstmt->subplans = estate->es_plannedstmt->subplans;
pstmt->rewindPlanIDs = NULL;
pstmt->rowMarks = NIL;
#include "executor/executor.h"
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
+#include "storage/lmgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
MemoryContextSwitchTo(oldcontext);
}
+
+/*
+ * ExecLockNonLeafAppendTables
+ *
+ * Locks, if necessary, the tables indicated by the RT indexes contained in
+ * the partitioned_rels list. These are the non-leaf tables in the partition
+ * tree controlled by a given Append or MergeAppend node.
+ */
+void
+ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
+{
+ PlannedStmt *stmt = estate->es_plannedstmt;
+ ListCell *lc;
+
+ foreach(lc, partitioned_rels)
+ {
+ ListCell *l;
+ Index rti = lfirst_int(lc);
+ bool is_result_rel = false;
+ Oid relid = getrelid(rti, estate->es_range_table);
+
+ /* If this is a result relation, already locked in InitPlan */
+ foreach(l, stmt->nonleafResultRelations)
+ {
+ if (rti == lfirst_int(l))
+ {
+ is_result_rel = true;
+ break;
+ }
+ }
+
+ /*
+ * Not a result relation; check if there is a RowMark that requires
+ * taking a RowShareLock on this rel.
+ */
+ if (!is_result_rel)
+ {
+ PlanRowMark *rc = NULL;
+
+ foreach(l, stmt->rowMarks)
+ {
+ if (((PlanRowMark *) lfirst(l))->rti == rti)
+ {
+ rc = lfirst(l);
+ break;
+ }
+ }
+
+ if (rc && RowMarkRequiresRowShareLock(rc->markType))
+ LockRelationOid(relid, RowShareLock);
+ else
+ LockRelationOid(relid, AccessShareLock);
+ }
+ }
+}
/* check for unsupported flags */
Assert(!(eflags & EXEC_FLAG_MARK));
+ /*
+ * Lock the non-leaf tables in the partition tree controlled by this
+ * node. It's a no-op for non-partitioned parent tables.
+ */
+ ExecLockNonLeafAppendTables(node->partitioned_rels, estate);
+
/*
* Set up empty vector of subplan states
*/
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
+ /*
+ * Lock the non-leaf tables in the partition tree controlled by this
+ * node. It's a no-op for non-partitioned parent tables.
+ */
+ ExecLockNonLeafAppendTables(node->partitioned_rels, estate);
+
/*
* Set up empty vector of subplan states
*/
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "parser/parsetree.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
estate->es_result_relation_info = saved_resultRelInfo;
+ /* The root table RT index is at the head of the partitioned_rels list */
+ if (node->partitioned_rels)
+ {
+ Index root_rti;
+ Oid root_oid;
+
+ root_rti = linitial_int(node->partitioned_rels);
+ root_oid = getrelid(root_rti, estate->es_range_table);
+ rel = heap_open(root_oid, NoLock); /* locked by InitPlan */
+ }
+ else
+ rel = mtstate->resultRelInfo->ri_RelationDesc;
+
/* Build state for INSERT tuple routing */
- rel = mtstate->resultRelInfo->ri_RelationDesc;
if (operation == CMD_INSERT &&
rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
mtstate->ps.ps_ExprContext = NULL;
}
+ /* Close the root partitioned rel if we opened it above. */
+ if (rel != mtstate->resultRelInfo->ri_RelationDesc)
+ heap_close(rel, NoLock);
+
/*
* If needed, Initialize target list, projection and qual for ON CONFLICT
* DO UPDATE.
COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(resultRelations);
+ COPY_NODE_FIELD(nonleafResultRelations);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
COPY_SCALAR_FIELD(operation);
COPY_SCALAR_FIELD(canSetTag);
COPY_SCALAR_FIELD(nominalRelation);
+ COPY_NODE_FIELD(partitioned_rels);
COPY_NODE_FIELD(resultRelations);
COPY_SCALAR_FIELD(resultRelIndex);
COPY_NODE_FIELD(plans);
/*
* copy remainder of node
*/
+ COPY_NODE_FIELD(partitioned_rels);
COPY_NODE_FIELD(appendplans);
return newnode;
/*
* copy remainder of node
*/
+ COPY_NODE_FIELD(partitioned_rels);
COPY_NODE_FIELD(mergeplans);
COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
return newnode;
}
+/*
+ * _copyPartitionedChildRelInfo
+ */
+static PartitionedChildRelInfo *
+_copyPartitionedChildRelInfo(const PartitionedChildRelInfo *from)
+{
+ PartitionedChildRelInfo *newnode = makeNode(PartitionedChildRelInfo);
+
+ COPY_SCALAR_FIELD(parent_relid);
+ COPY_NODE_FIELD(child_rels);
+
+ return newnode;
+}
+
/*
* _copyPlaceHolderInfo
*/
case T_AppendRelInfo:
retval = _copyAppendRelInfo(from);
break;
+ case T_PartitionedChildRelInfo:
+ retval = _copyPartitionedChildRelInfo(from);
+ break;
case T_PlaceHolderInfo:
retval = _copyPlaceHolderInfo(from);
break;
return true;
}
+static bool
+_equalPartitionedChildRelInfo(const PartitionedChildRelInfo *a, const PartitionedChildRelInfo *b)
+{
+ COMPARE_SCALAR_FIELD(parent_relid);
+ COMPARE_NODE_FIELD(child_rels);
+
+ return true;
+}
+
static bool
_equalPlaceHolderInfo(const PlaceHolderInfo *a, const PlaceHolderInfo *b)
{
case T_AppendRelInfo:
retval = _equalAppendRelInfo(a, b);
break;
+ case T_PartitionedChildRelInfo:
+ retval = _equalPartitionedChildRelInfo(a, b);
+ break;
case T_PlaceHolderInfo:
retval = _equalPlaceHolderInfo(a, b);
break;
WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(resultRelations);
+ WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
WRITE_ENUM_FIELD(operation, CmdType);
WRITE_BOOL_FIELD(canSetTag);
WRITE_UINT_FIELD(nominalRelation);
+ WRITE_NODE_FIELD(partitioned_rels);
WRITE_NODE_FIELD(resultRelations);
WRITE_INT_FIELD(resultRelIndex);
WRITE_NODE_FIELD(plans);
_outPlanInfo(str, (const Plan *) node);
+ WRITE_NODE_FIELD(partitioned_rels);
WRITE_NODE_FIELD(appendplans);
}
_outPlanInfo(str, (const Plan *) node);
+ WRITE_NODE_FIELD(partitioned_rels);
WRITE_NODE_FIELD(mergeplans);
WRITE_INT_FIELD(numCols);
_outPathInfo(str, (const Path *) node);
+ WRITE_NODE_FIELD(partitioned_rels);
WRITE_NODE_FIELD(subpaths);
}
_outPathInfo(str, (const Path *) node);
+ WRITE_NODE_FIELD(partitioned_rels);
WRITE_NODE_FIELD(subpaths);
WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
}
WRITE_ENUM_FIELD(operation, CmdType);
WRITE_BOOL_FIELD(canSetTag);
WRITE_UINT_FIELD(nominalRelation);
+ WRITE_NODE_FIELD(partitioned_rels);
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(subpaths);
WRITE_NODE_FIELD(subroots);
WRITE_NODE_FIELD(finalrtable);
WRITE_NODE_FIELD(finalrowmarks);
WRITE_NODE_FIELD(resultRelations);
+ WRITE_NODE_FIELD(nonleafResultRelations);
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
WRITE_INT_FIELD(nParamExec);
WRITE_NODE_FIELD(full_join_clauses);
WRITE_NODE_FIELD(join_info_list);
WRITE_NODE_FIELD(append_rel_list);
+ WRITE_NODE_FIELD(pcinfo_list);
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(placeholder_list);
WRITE_NODE_FIELD(fkey_list);
WRITE_OID_FIELD(parent_reloid);
}
+static void
+_outPartitionedChildRelInfo(StringInfo str, const PartitionedChildRelInfo *node)
+{
+ WRITE_NODE_TYPE("PARTITIONEDCHILDRELINFO");
+
+ WRITE_UINT_FIELD(parent_relid);
+ WRITE_NODE_FIELD(child_rels);
+}
+
static void
_outPlaceHolderInfo(StringInfo str, const PlaceHolderInfo *node)
{
case T_AppendRelInfo:
_outAppendRelInfo(str, obj);
break;
+ case T_PartitionedChildRelInfo:
+ _outPartitionedChildRelInfo(str, obj);
+ break;
case T_PlaceHolderInfo:
_outPlaceHolderInfo(str, obj);
break;
READ_NODE_FIELD(planTree);
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(resultRelations);
+ READ_NODE_FIELD(nonleafResultRelations);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
READ_ENUM_FIELD(operation, CmdType);
READ_BOOL_FIELD(canSetTag);
READ_UINT_FIELD(nominalRelation);
+ READ_NODE_FIELD(partitioned_rels);
READ_NODE_FIELD(resultRelations);
READ_INT_FIELD(resultRelIndex);
READ_NODE_FIELD(plans);
ReadCommonPlan(&local_node->plan);
+ READ_NODE_FIELD(partitioned_rels);
READ_NODE_FIELD(appendplans);
READ_DONE();
ReadCommonPlan(&local_node->plan);
+ READ_NODE_FIELD(partitioned_rels);
READ_NODE_FIELD(mergeplans);
READ_INT_FIELD(numCols);
READ_ATTRNUMBER_ARRAY(sortColIdx, local_node->numCols);
Index rti, RangeTblEntry *rte);
static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
List *live_childrels,
- List *all_child_pathkeys);
+ List *all_child_pathkeys,
+ List *partitioned_rels);
static Path *get_cheapest_parameterized_child_path(PlannerInfo *root,
RelOptInfo *rel,
Relids required_outer);
/* Foreign table */
set_foreign_size(root, rel, rte);
}
+ else if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ /*
+ * A partitioned table without leaf partitions is marked
+ * as a dummy rel.
+ */
+ set_dummy_rel_pathlist(rel);
+ }
else if (rte->tablesample != NULL)
{
/* Sampled relation */
List *all_child_pathkeys = NIL;
List *all_child_outers = NIL;
ListCell *l;
+ List *partitioned_rels = NIL;
+ RangeTblEntry *rte;
+
+ rte = planner_rt_fetch(rel->relid, root);
+ if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ partitioned_rels = get_partitioned_child_rels(root, rel->relid);
+ /* The root partitioned table is included as a child rel */
+ Assert(list_length(partitioned_rels) >= 1);
+ }
/*
* For every non-dummy child, remember the cheapest path. Also, identify
* if we have zero or one live subpath due to constraint exclusion.)
*/
if (subpaths_valid)
- add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0));
+ add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0,
+ partitioned_rels));
/*
* Consider an append of partial unordered, unparameterized partial paths.
/* Generate a partial append path. */
appendpath = create_append_path(rel, partial_subpaths, NULL,
- parallel_workers);
+ parallel_workers, partitioned_rels);
add_partial_path(rel, (Path *) appendpath);
}
*/
if (subpaths_valid)
generate_mergeappend_paths(root, rel, live_childrels,
- all_child_pathkeys);
+ all_child_pathkeys,
+ partitioned_rels);
/*
* Build Append paths for each parameterization seen among the child rels.
if (subpaths_valid)
add_path(rel, (Path *)
- create_append_path(rel, subpaths, required_outer, 0));
+ create_append_path(rel, subpaths, required_outer, 0,
+ partitioned_rels));
}
}
static void
generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
List *live_childrels,
- List *all_child_pathkeys)
+ List *all_child_pathkeys,
+ List *partitioned_rels)
{
ListCell *lcp;
rel,
startup_subpaths,
pathkeys,
- NULL));
+ NULL,
+ partitioned_rels));
if (startup_neq_total)
add_path(rel, (Path *) create_merge_append_path(root,
rel,
total_subpaths,
pathkeys,
- NULL));
+ NULL,
+ partitioned_rels));
}
}
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0));
+ add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL));
/*
* We set the cheapest path immediately, to ensure that IS_DUMMY_REL()
rel->partial_pathlist = NIL;
/* Set up the dummy path */
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0));
+ add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL));
/* Set or update cheapest_total_path and related fields */
set_cheapest(rel);
Index scanrelid, int ctePlanId, int cteParam);
static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
Index scanrelid, int wtParam);
-static Append *make_append(List *appendplans, List *tlist);
+static Append *make_append(List *appendplans, List *tlist, List *partitioned_rels);
static RecursiveUnion *make_recursive_union(List *tlist,
Plan *lefttree,
Plan *righttree,
static ProjectSet *make_project_set(List *tlist, Plan *subplan);
static ModifyTable *make_modifytable(PlannerInfo *root,
CmdType operation, bool canSetTag,
- Index nominalRelation,
+ Index nominalRelation, List *partitioned_rels,
List *resultRelations, List *subplans,
List *withCheckOptionLists, List *returningLists,
List *rowMarks, OnConflictExpr *onconflict, int epqParam);
* parent-rel Vars it'll be asked to emit.
*/
- plan = make_append(subplans, tlist);
+ plan = make_append(subplans, tlist, best_path->partitioned_rels);
copy_generic_path_info(&plan->plan, (Path *) best_path);
subplans = lappend(subplans, subplan);
}
+ node->partitioned_rels = best_path->partitioned_rels;
node->mergeplans = subplans;
return (Plan *) node;
best_path->operation,
best_path->canSetTag,
best_path->nominalRelation,
+ best_path->partitioned_rels,
best_path->resultRelations,
subplans,
best_path->withCheckOptionLists,
}
static Append *
-make_append(List *appendplans, List *tlist)
+make_append(List *appendplans, List *tlist, List *partitioned_rels)
{
Append *node = makeNode(Append);
Plan *plan = &node->plan;
plan->qual = NIL;
plan->lefttree = NULL;
plan->righttree = NULL;
+ node->partitioned_rels = partitioned_rels;
node->appendplans = appendplans;
return node;
static ModifyTable *
make_modifytable(PlannerInfo *root,
CmdType operation, bool canSetTag,
- Index nominalRelation,
+ Index nominalRelation, List *partitioned_rels,
List *resultRelations, List *subplans,
List *withCheckOptionLists, List *returningLists,
List *rowMarks, OnConflictExpr *onconflict, int epqParam)
node->operation = operation;
node->canSetTag = canSetTag;
node->nominalRelation = nominalRelation;
+ node->partitioned_rels = partitioned_rels;
node->resultRelations = resultRelations;
node->resultRelIndex = -1; /* will be set correctly in setrefs.c */
node->plans = subplans;
glob->finalrtable = NIL;
glob->finalrowmarks = NIL;
glob->resultRelations = NIL;
+ glob->nonleafResultRelations = NIL;
glob->relationOids = NIL;
glob->invalItems = NIL;
glob->nParamExec = 0;
Assert(glob->finalrtable == NIL);
Assert(glob->finalrowmarks == NIL);
Assert(glob->resultRelations == NIL);
+ Assert(glob->nonleafResultRelations == NIL);
top_plan = set_plan_references(root, top_plan);
/* ... and the subplans (both regular subplans and initplans) */
Assert(list_length(glob->subplans) == list_length(glob->subroots));
result->planTree = top_plan;
result->rtable = glob->finalrtable;
result->resultRelations = glob->resultRelations;
+ result->nonleafResultRelations = glob->nonleafResultRelations;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
root->multiexpr_params = NIL;
root->eq_classes = NIL;
root->append_rel_list = NIL;
+ root->pcinfo_list = NIL;
root->rowMarks = NIL;
memset(root->upper_rels, 0, sizeof(root->upper_rels));
memset(root->upper_targets, 0, sizeof(root->upper_targets));
RelOptInfo *final_rel;
ListCell *lc;
Index rti;
+ RangeTblEntry *parent_rte;
+ List *partitioned_rels = NIL;
Assert(parse->commandType != CMD_INSERT);
}
}
+ /*
+ * If the parent RTE is a partitioned table, we should use that as the
+ * nominal relation, because the RTEs added for partitioned tables
+ * (including the root parent) as child members of the inheritance set
+ * do not appear anywhere else in the plan. The situation is exactly
+ * the opposite in the case of non-partitioned inheritance parent as
+ * described below.
+ */
+ parent_rte = rt_fetch(parentRTindex, root->parse->rtable);
+ if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
+ nominalRelation = parentRTindex;
+
/*
* And now we can get on with generating a plan for each child table.
*/
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
PlannerInfo *subroot;
- RangeTblEntry *parent_rte;
RangeTblEntry *child_rte;
RelOptInfo *sub_final_rel;
Path *subpath;
grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );
/*
- * We'll use the first child relation (even if it's excluded) as the
- * nominal target relation of the ModifyTable node. Because of the
- * way expand_inherited_rtentry works, this should always be the RTE
- * representing the parent table in its role as a simple member of the
- * inheritance set. (It would be logically cleaner to use the
- * inheritance parent RTE as the nominal target; but since that RTE
- * will not be otherwise referenced in the plan, doing so would give
- * rise to confusing use of multiple aliases in EXPLAIN output for
- * what the user will think is the "same" table.)
+ * Set the nomimal target relation of the ModifyTable node if not
+ * already done. We use the inheritance parent RTE as the nominal
+ * target relation if it's a partitioned table (see just above this
+ * loop). In the non-partitioned parent case, we'll use the first
+ * child relation (even if it's excluded) as the nominal target
+ * relation. Because of the way expand_inherited_rtentry works, the
+ * latter should be the RTE representing the parent table in its role
+ * as a simple member of the inheritance set.
+ *
+ * It would be logically cleaner to *always* use the inheritance
+ * parent RTE as the nominal relation; but that RTE is not otherwise
+ * referenced in the plan in the non-partitioned inheritance case.
+ * Instead the duplicate child RTE created by expand_inherited_rtentry
+ * is used elsewhere in the plan, so using the original parent RTE
+ * would give rise to confusing use of multiple aliases in EXPLAIN
+ * output for what the user will think is the "same" table. OTOH,
+ * it's not a problem in the partitioned inheritance case, because
+ * the duplicate child RTE added for the parent does not appear
+ * anywhere else in the plan tree.
*/
if (nominalRelation < 0)
nominalRelation = appinfo->child_relid;
Assert(!parse->onConflict);
}
+ if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ partitioned_rels = get_partitioned_child_rels(root, parentRTindex);
+ /* The root partitioned table is included as a child rel */
+ Assert(list_length(partitioned_rels) >= 1);
+ }
+
/* Result path must go into outer query's FINAL upperrel */
final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
parse->commandType,
parse->canSetTag,
nominalRelation,
+ partitioned_rels,
resultRelations,
subpaths,
subroots,
parse->commandType,
parse->canSetTag,
parse->resultRelation,
+ NIL,
list_make1_int(parse->resultRelation),
list_make1(path),
list_make1(root),
create_append_path(grouped_rel,
paths,
NULL,
- 0);
+ 0,
+ NIL);
path->pathtarget = target;
}
else
return (seqScanAndSortPath.total_cost < indexScanPath->path.total_cost);
}
+
+/*
+ * get_partitioned_child_rels
+ * Returns a list of the RT indexes of the partitioned child relations
+ * with rti as the root parent RT index.
+ *
+ * Note: Only call this function on RTEs known to be partitioned tables.
+ */
+List *
+get_partitioned_child_rels(PlannerInfo *root, Index rti)
+{
+ List *result = NIL;
+ ListCell *l;
+
+ foreach(l, root->pcinfo_list)
+ {
+ PartitionedChildRelInfo *pc = lfirst(l);
+
+ if (pc->parent_relid == rti)
+ {
+ result = pc->child_rels;
+ break;
+ }
+ }
+
+ /* The root partitioned table is included as a child rel */
+ Assert(list_length(result) >= 1);
+
+ return result;
+}
splan->nominalRelation += rtoffset;
splan->exclRelRTI += rtoffset;
+ foreach(l, splan->partitioned_rels)
+ {
+ lfirst_int(l) += rtoffset;
+ }
foreach(l, splan->resultRelations)
{
lfirst_int(l) += rtoffset;
root->glob->resultRelations =
list_concat(root->glob->resultRelations,
list_copy(splan->resultRelations));
+
+ /*
+ * If the main target relation is a partitioned table, the
+ * following list contains the RT indexes of partitioned child
+ * relations, which are not included in the above list.
+ */
+ root->glob->nonleafResultRelations =
+ list_concat(root->glob->nonleafResultRelations,
+ list_copy(splan->partitioned_rels));
}
break;
case T_Append:
*/
set_dummy_tlist_references(plan, rtoffset);
Assert(splan->plan.qual == NIL);
+ foreach(l, splan->partitioned_rels)
+ {
+ lfirst_int(l) += rtoffset;
+ }
foreach(l, splan->appendplans)
{
lfirst(l) = set_plan_refs(root,
*/
set_dummy_tlist_references(plan, rtoffset);
Assert(splan->plan.qual == NIL);
+ foreach(l, splan->partitioned_rels)
+ {
+ lfirst_int(l) += rtoffset;
+ }
foreach(l, splan->mergeplans)
{
lfirst(l) = set_plan_refs(root,
/*
* Append the child results together.
*/
- path = (Path *) create_append_path(result_rel, pathlist, NULL, 0);
+ path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL);
/* We have to manually jam the right tlist into the path; ick */
path->pathtarget = create_pathtarget(root, tlist);
/*
* Append the child results together.
*/
- path = (Path *) create_append_path(result_rel, pathlist, NULL, 0);
+ path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL);
/* We have to manually jam the right tlist into the path; ick */
path->pathtarget = create_pathtarget(root, tlist);
List *inhOIDs;
List *appinfos;
ListCell *l;
+ bool need_append;
+ PartitionedChildRelInfo *pcinfo;
+ List *partitioned_child_rels = NIL;
/* Does RT entry allow inheritance? */
if (!rte->inh)
/* Scan the inheritance set and expand it */
appinfos = NIL;
+ need_append = false;
foreach(l, inhOIDs)
{
Oid childOID = lfirst_oid(l);
childRTindex = list_length(parse->rtable);
/*
- * Build an AppendRelInfo for this parent and child.
+ * Build an AppendRelInfo for this parent and child, unless the child
+ * is a partitioned table.
*/
- appinfo = makeNode(AppendRelInfo);
- appinfo->parent_relid = rti;
- appinfo->child_relid = childRTindex;
- appinfo->parent_reltype = oldrelation->rd_rel->reltype;
- appinfo->child_reltype = newrelation->rd_rel->reltype;
- make_inh_translation_list(oldrelation, newrelation, childRTindex,
- &appinfo->translated_vars);
- appinfo->parent_reloid = parentOID;
- appinfos = lappend(appinfos, appinfo);
-
- /*
- * Translate the column permissions bitmaps to the child's attnums (we
- * have to build the translated_vars list before we can do this). But
- * if this is the parent table, leave copyObject's result alone.
- *
- * Note: we need to do this even though the executor won't run any
- * permissions checks on the child RTE. The insertedCols/updatedCols
- * bitmaps may be examined for trigger-firing purposes.
- */
- if (childOID != parentOID)
+ if (childrte->relkind != RELKIND_PARTITIONED_TABLE)
{
- childrte->selectedCols = translate_col_privs(rte->selectedCols,
+ need_append = true;
+ appinfo = makeNode(AppendRelInfo);
+ appinfo->parent_relid = rti;
+ appinfo->child_relid = childRTindex;
+ appinfo->parent_reltype = oldrelation->rd_rel->reltype;
+ appinfo->child_reltype = newrelation->rd_rel->reltype;
+ make_inh_translation_list(oldrelation, newrelation, childRTindex,
+ &appinfo->translated_vars);
+ appinfo->parent_reloid = parentOID;
+ appinfos = lappend(appinfos, appinfo);
+
+ /*
+ * Translate the column permissions bitmaps to the child's attnums
+ * (we have to build the translated_vars list before we can do
+ * this). But if this is the parent table, leave copyObject's
+ * result alone.
+ *
+ * Note: we need to do this even though the executor won't run any
+ * permissions checks on the child RTE. The
+ * insertedCols/updatedCols bitmaps may be examined for
+ * trigger-firing purposes.
+ */
+ if (childOID != parentOID)
+ {
+ childrte->selectedCols = translate_col_privs(rte->selectedCols,
appinfo->translated_vars);
- childrte->insertedCols = translate_col_privs(rte->insertedCols,
+ childrte->insertedCols = translate_col_privs(rte->insertedCols,
appinfo->translated_vars);
- childrte->updatedCols = translate_col_privs(rte->updatedCols,
+ childrte->updatedCols = translate_col_privs(rte->updatedCols,
appinfo->translated_vars);
+ }
}
+ else
+ partitioned_child_rels = lappend_int(partitioned_child_rels,
+ childRTindex);
/*
* Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
newrc->allMarkTypes = (1 << newrc->markType);
newrc->strength = oldrc->strength;
newrc->waitPolicy = oldrc->waitPolicy;
- newrc->isParent = false;
+
+ /*
+ * We mark RowMarks for partitioned child tables as parent RowMarks
+ * so that the executor ignores them (except their existence means
+ * that the child tables be locked using appropriate mode).
+ */
+ newrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
/* Include child's rowmark type in parent's allMarkTypes */
oldrc->allMarkTypes |= newrc->allMarkTypes;
heap_close(oldrelation, NoLock);
/*
- * If all the children were temp tables, pretend it's a non-inheritance
- * situation. The duplicate RTE we added for the parent table is
- * harmless, so we don't bother to get rid of it; ditto for the useless
- * PlanRowMark node.
+ * If all the children were temp tables or a partitioned parent did not
+ * have any leaf partitions, pretend it's a non-inheritance situation; we
+ * don't need Append node in that case. The duplicate RTE we added for
+ * the parent table is harmless, so we don't bother to get rid of it;
+ * ditto for the useless PlanRowMark node.
*/
- if (list_length(appinfos) < 2)
+ if (!need_append)
{
/* Clear flag before returning */
rte->inh = false;
return;
}
+ /*
+ * We keep a list of objects in root, each of which maps a partitioned
+ * parent RT index to the list of RT indexes of its partitioned child
+ * tables. When creating an Append or a ModifyTable path for the parent,
+ * we copy the child RT index list verbatim to the path so that it could
+ * be carried over to the executor so that the latter could identify
+ * the partitioned child tables.
+ */
+ if (partitioned_child_rels != NIL)
+ {
+ pcinfo = makeNode(PartitionedChildRelInfo);
+
+ Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
+ pcinfo->parent_relid = rti;
+ pcinfo->child_rels = partitioned_child_rels;
+ root->pcinfo_list = lappend(root->pcinfo_list, pcinfo);
+ }
+
/* Otherwise, OK to add to root->append_rel_list */
root->append_rel_list = list_concat(root->append_rel_list, appinfos);
}
*/
AppendPath *
create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer,
- int parallel_workers)
+ int parallel_workers, List *partitioned_rels)
{
AppendPath *pathnode = makeNode(AppendPath);
ListCell *l;
pathnode->path.parallel_workers = parallel_workers;
pathnode->path.pathkeys = NIL; /* result is always considered
* unsorted */
+ pathnode->partitioned_rels = partitioned_rels;
pathnode->subpaths = subpaths;
/*
RelOptInfo *rel,
List *subpaths,
List *pathkeys,
- Relids required_outer)
+ Relids required_outer,
+ List *partitioned_rels)
{
MergeAppendPath *pathnode = makeNode(MergeAppendPath);
Cost input_startup_cost;
pathnode->path.parallel_safe = rel->consider_parallel;
pathnode->path.parallel_workers = 0;
pathnode->path.pathkeys = pathkeys;
+ pathnode->partitioned_rels = partitioned_rels;
pathnode->subpaths = subpaths;
/*
ModifyTablePath *
create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
CmdType operation, bool canSetTag,
- Index nominalRelation,
+ Index nominalRelation, List *partitioned_rels,
List *resultRelations, List *subpaths,
List *subroots,
List *withCheckOptionLists, List *returningLists,
pathnode->operation = operation;
pathnode->canSetTag = canSetTag;
pathnode->nominalRelation = nominalRelation;
+ pathnode->partitioned_rels = partitioned_rels;
pathnode->resultRelations = resultRelations;
pathnode->subpaths = subpaths;
pathnode->subroots = subroots;
* fail if it's been dropped entirely --- we'll just transiently
* acquire a non-conflicting lock.
*/
- if (list_member_int(plannedstmt->resultRelations, rt_index))
+ if (list_member_int(plannedstmt->resultRelations, rt_index) ||
+ list_member_int(plannedstmt->nonleafResultRelations, rt_index))
lockmode = RowExclusiveLock;
else if ((rc = get_plan_rowmark(plannedstmt->rowMarks, rt_index)) != NULL &&
RowMarkRequiresRowShareLock(rc->markType))
extern void UnregisterExprContextCallback(ExprContext *econtext,
ExprContextCallbackFunction function,
Datum arg);
+extern void ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate);
/*
* prototypes from functions in execIndexing.c
T_PlaceHolderVar,
T_SpecialJoinInfo,
T_AppendRelInfo,
+ T_PartitionedChildRelInfo,
T_PlaceHolderInfo,
T_MinMaxAggInfo,
T_PlannerParamItem,
/* rtable indexes of target relations for INSERT/UPDATE/DELETE */
List *resultRelations; /* integer list of RT indexes, or NIL */
+ /* rtable indexes of non-leaf target relations for INSERT/UPDATE/DELETE */
+ List *nonleafResultRelations;
+
List *subplans; /* Plan trees for SubPlan expressions */
Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */
CmdType operation; /* INSERT, UPDATE, or DELETE */
bool canSetTag; /* do we set the command tag/es_processed? */
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
+ /* RT indexes of non-leaf tables in a partition tree */
+ List *partitioned_rels;
List *resultRelations; /* integer list of RT indexes */
int resultRelIndex; /* index of first resultRel in plan's list */
List *plans; /* plan(s) producing source data */
typedef struct Append
{
Plan plan;
+ /* RT indexes of non-leaf tables in a partition tree */
+ List *partitioned_rels;
List *appendplans;
} Append;
typedef struct MergeAppend
{
Plan plan;
+ /* RT indexes of non-leaf tables in a partition tree */
+ List *partitioned_rels;
List *mergeplans;
/* remaining fields are just like the sort-key info in struct Sort */
int numCols; /* number of sort-key columns */
* When the planner discovers that a relation is the root of an inheritance
* tree, it sets isParent true, and adds an additional PlanRowMark to the
* list for each child relation (including the target rel itself in its role
- * as a child). The child entries have rti == child rel's RT index and
- * prti == parent's RT index, and can therefore be recognized as children by
- * the fact that prti != rti. The parent's allMarkTypes field gets the OR
- * of (1<<markType) across all its children (this definition allows children
- * to use different markTypes).
+ * as a child). isParent is also set to true for the partitioned child
+ * relations, which are not scanned just like the root parent. The child
+ * entries have rti == child rel's RT index and prti == parent's RT index,
+ * and can therefore be recognized as children by the fact that prti != rti.
+ * The parent's allMarkTypes field gets the OR of (1<<markType) across all
+ * its children (this definition allows children to use different markTypes).
*
* The planner also adds resjunk output columns to the plan that carry
* information sufficient to identify the locked or fetched rows. When
List *resultRelations; /* "flat" list of integer RT indexes */
+ List *nonleafResultRelations; /* "flat" list of integer RT indexes */
+
List *relationOids; /* OIDs of relations the plan depends on */
List *invalItems; /* other dependencies, as PlanInvalItems */
List *append_rel_list; /* list of AppendRelInfos */
+ List *pcinfo_list; /* list of PartitionedChildRelInfos */
+
List *rowMarks; /* list of PlanRowMarks */
List *placeholder_list; /* list of PlaceHolderInfos */
typedef struct AppendPath
{
Path path;
+ /* RT indexes of non-leaf tables in a partition tree */
+ List *partitioned_rels;
List *subpaths; /* list of component Paths */
} AppendPath;
typedef struct MergeAppendPath
{
Path path;
+ /* RT indexes of non-leaf tables in a partition tree */
+ List *partitioned_rels;
List *subpaths; /* list of component Paths */
double limit_tuples; /* hard limit on output tuples, or -1 */
} MergeAppendPath;
CmdType operation; /* INSERT, UPDATE, or DELETE */
bool canSetTag; /* do we set the command tag/es_processed? */
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
+ /* RT indexes of non-leaf tables in a partition tree */
+ List *partitioned_rels;
List *resultRelations; /* integer list of RT indexes */
List *subpaths; /* Path(s) producing source data */
List *subroots; /* per-target-table PlannerInfos */
*
* When we expand an inheritable table or a UNION-ALL subselect into an
* "append relation" (essentially, a list of child RTEs), we build an
- * AppendRelInfo for each child RTE. The list of AppendRelInfos indicates
- * which child RTEs must be included when expanding the parent, and each
- * node carries information needed to translate Vars referencing the parent
- * into Vars referencing that child.
+ * AppendRelInfo for each non-partitioned child RTE. The list of
+ * AppendRelInfos indicates which child RTEs must be included when expanding
+ * the parent, and each node carries information needed to translate Vars
+ * referencing the parent into Vars referencing that child.
*
* These structs are kept in the PlannerInfo node's append_rel_list.
* Note that we just throw all the structs into one list, and scan the
Oid parent_reloid; /* OID of parent relation */
} AppendRelInfo;
+/*
+ * For a partitioned table, this maps its RT index to the list of RT indexes
+ * of the partitioned child tables in the partition tree. We need to
+ * separately store this information, because we do not create AppendRelInfos
+ * for the partitioned child tables of a parent table, since AppendRelInfos
+ * contain information that is unnecessary for the partitioned child tables.
+ * The child_rels list must contain at least one element, because the parent
+ * partitioned table is itself counted as a child.
+ *
+ * These structs are kept in the PlannerInfo node's pcinfo_list.
+ */
+typedef struct PartitionedChildRelInfo
+{
+ NodeTag type;
+
+ Index parent_relid;
+ List *child_rels;
+} PartitionedChildRelInfo;
+
/*
* For each distinct placeholder expression generated during planning, we
* store a PlaceHolderInfo node in the PlannerInfo node's placeholder_list.
extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
List *tidquals, Relids required_outer);
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths,
- Relids required_outer, int parallel_workers);
+ Relids required_outer, int parallel_workers,
+ List *partitioned_rels);
extern MergeAppendPath *create_merge_append_path(PlannerInfo *root,
RelOptInfo *rel,
List *subpaths,
List *pathkeys,
- Relids required_outer);
+ Relids required_outer,
+ List *partitioned_rels);
extern ResultPath *create_result_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target, List *resconstantqual);
extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
extern ModifyTablePath *create_modifytable_path(PlannerInfo *root,
RelOptInfo *rel,
CmdType operation, bool canSetTag,
- Index nominalRelation,
+ Index nominalRelation, List *partitioned_rels,
List *resultRelations, List *subpaths,
List *subroots,
List *withCheckOptionLists, List *returningLists,
extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
+extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
+
#endif /* PLANNER_H */
bar2 | 4 | 104
(8 rows)
+-- Check UPDATE with *partitioned* inherited target and an appendrel subquery
+create table some_tab (a int);
+insert into some_tab values (0);
+create table some_tab_child () inherits (some_tab);
+insert into some_tab_child values (1);
+create table parted_tab (a int, b char) partition by list (a);
+create table parted_tab_part1 partition of parted_tab for values in (1);
+create table parted_tab_part2 partition of parted_tab for values in (2);
+create table parted_tab_part3 partition of parted_tab for values in (3);
+insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a');
+update parted_tab set b = 'b'
+from
+ (select a from some_tab union all select a+1 from some_tab) ss (a)
+where parted_tab.a = ss.a;
+select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2;
+ relname | a | b
+------------------+---+---
+ parted_tab_part1 | 1 | b
+ parted_tab_part2 | 2 | b
+ parted_tab_part3 | 3 | a
+(3 rows)
+
+truncate parted_tab;
+insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a');
+update parted_tab set b = 'b'
+from
+ (select 0 from parted_tab union all select 1 from parted_tab) ss (a)
+where parted_tab.a = ss.a;
+select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2;
+ relname | a | b
+------------------+---+---
+ parted_tab_part1 | 1 | b
+ parted_tab_part2 | 2 | a
+ parted_tab_part3 | 3 | a
+(3 rows)
+
+drop table parted_tab;
+drop table some_tab cascade;
+NOTICE: drop cascades to table some_tab_child
/* Test multiple inheritance of column defaults */
CREATE TABLE firstparent (tomorrow date default now()::date + 1);
CREATE TABLE secondparent (tomorrow date default now() :: date + 1);
QUERY PLAN
--------------------------------
Append
- -> Seq Scan on list_parted
-> Seq Scan on part_ab_cd
-> Seq Scan on part_ef_gh
-> Seq Scan on part_null_xy
-(5 rows)
+(4 rows)
explain (costs off) select * from list_parted where a is null;
QUERY PLAN
--------------------------------
Append
- -> Seq Scan on list_parted
- Filter: (a IS NULL)
-> Seq Scan on part_null_xy
Filter: (a IS NULL)
-(5 rows)
+(3 rows)
explain (costs off) select * from list_parted where a is not null;
QUERY PLAN
---------------------------------
Append
- -> Seq Scan on list_parted
- Filter: (a IS NOT NULL)
-> Seq Scan on part_ab_cd
Filter: (a IS NOT NULL)
-> Seq Scan on part_ef_gh
Filter: (a IS NOT NULL)
-> Seq Scan on part_null_xy
Filter: (a IS NOT NULL)
-(9 rows)
+(7 rows)
explain (costs off) select * from list_parted where a in ('ab', 'cd', 'ef');
QUERY PLAN
----------------------------------------------------------
Append
- -> Seq Scan on list_parted
- Filter: ((a)::text = ANY ('{ab,cd,ef}'::text[]))
-> Seq Scan on part_ab_cd
Filter: ((a)::text = ANY ('{ab,cd,ef}'::text[]))
-> Seq Scan on part_ef_gh
Filter: ((a)::text = ANY ('{ab,cd,ef}'::text[]))
-(7 rows)
+(5 rows)
explain (costs off) select * from list_parted where a = 'ab' or a in (null, 'cd');
QUERY PLAN
---------------------------------------------------------------------------------------
Append
- -> Seq Scan on list_parted
- Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
-> Seq Scan on part_ab_cd
Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
-> Seq Scan on part_ef_gh
Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
-> Seq Scan on part_null_xy
Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
-(9 rows)
+(7 rows)
explain (costs off) select * from list_parted where a = 'ab';
QUERY PLAN
------------------------------------------
Append
- -> Seq Scan on list_parted
- Filter: ((a)::text = 'ab'::text)
-> Seq Scan on part_ab_cd
Filter: ((a)::text = 'ab'::text)
-(5 rows)
+(3 rows)
create table range_list_parted (
a int,
create table part_40_inf_cd partition of part_40_inf for values in ('cd');
create table part_40_inf_null partition of part_40_inf for values in (null);
explain (costs off) select * from range_list_parted;
- QUERY PLAN
--------------------------------------
+ QUERY PLAN
+------------------------------------
Append
- -> Seq Scan on range_list_parted
- -> Seq Scan on part_1_10
- -> Seq Scan on part_10_20
- -> Seq Scan on part_21_30
- -> Seq Scan on part_40_inf
-> Seq Scan on part_1_10_ab
-> Seq Scan on part_1_10_cd
-> Seq Scan on part_10_20_ab
-> Seq Scan on part_40_inf_ab
-> Seq Scan on part_40_inf_cd
-> Seq Scan on part_40_inf_null
-(15 rows)
+(10 rows)
explain (costs off) select * from range_list_parted where a = 5;
- QUERY PLAN
--------------------------------------
+ QUERY PLAN
+--------------------------------
Append
- -> Seq Scan on range_list_parted
- Filter: (a = 5)
- -> Seq Scan on part_1_10
- Filter: (a = 5)
-> Seq Scan on part_1_10_ab
Filter: (a = 5)
-> Seq Scan on part_1_10_cd
Filter: (a = 5)
-(9 rows)
+(5 rows)
explain (costs off) select * from range_list_parted where b = 'ab';
- QUERY PLAN
--------------------------------------
+ QUERY PLAN
+------------------------------------
Append
- -> Seq Scan on range_list_parted
- Filter: (b = 'ab'::bpchar)
- -> Seq Scan on part_1_10
- Filter: (b = 'ab'::bpchar)
- -> Seq Scan on part_10_20
- Filter: (b = 'ab'::bpchar)
- -> Seq Scan on part_21_30
- Filter: (b = 'ab'::bpchar)
- -> Seq Scan on part_40_inf
- Filter: (b = 'ab'::bpchar)
-> Seq Scan on part_1_10_ab
Filter: (b = 'ab'::bpchar)
-> Seq Scan on part_10_20_ab
Filter: (b = 'ab'::bpchar)
-> Seq Scan on part_40_inf_ab
Filter: (b = 'ab'::bpchar)
-(19 rows)
+(9 rows)
explain (costs off) select * from range_list_parted where a between 3 and 23 and b in ('ab');
QUERY PLAN
-----------------------------------------------------------------
Append
- -> Seq Scan on range_list_parted
- Filter: ((a >= 3) AND (a <= 23) AND (b = 'ab'::bpchar))
- -> Seq Scan on part_1_10
- Filter: ((a >= 3) AND (a <= 23) AND (b = 'ab'::bpchar))
- -> Seq Scan on part_10_20
- Filter: ((a >= 3) AND (a <= 23) AND (b = 'ab'::bpchar))
- -> Seq Scan on part_21_30
- Filter: ((a >= 3) AND (a <= 23) AND (b = 'ab'::bpchar))
-> Seq Scan on part_1_10_ab
Filter: ((a >= 3) AND (a <= 23) AND (b = 'ab'::bpchar))
-> Seq Scan on part_10_20_ab
Filter: ((a >= 3) AND (a <= 23) AND (b = 'ab'::bpchar))
-> Seq Scan on part_21_30_ab
Filter: ((a >= 3) AND (a <= 23) AND (b = 'ab'::bpchar))
-(15 rows)
+(7 rows)
/* Should select no rows because range partition key cannot be null */
explain (costs off) select * from range_list_parted where a is null;
/* Should only select rows from the null-accepting partition */
explain (costs off) select * from range_list_parted where b is null;
- QUERY PLAN
--------------------------------------
+ QUERY PLAN
+------------------------------------
Append
- -> Seq Scan on range_list_parted
- Filter: (b IS NULL)
- -> Seq Scan on part_1_10
- Filter: (b IS NULL)
- -> Seq Scan on part_10_20
- Filter: (b IS NULL)
- -> Seq Scan on part_21_30
- Filter: (b IS NULL)
- -> Seq Scan on part_40_inf
- Filter: (b IS NULL)
-> Seq Scan on part_40_inf_null
Filter: (b IS NULL)
-(13 rows)
+(3 rows)
explain (costs off) select * from range_list_parted where a is not null and a < 67;
QUERY PLAN
------------------------------------------------
Append
- -> Seq Scan on range_list_parted
- Filter: ((a IS NOT NULL) AND (a < 67))
- -> Seq Scan on part_1_10
- Filter: ((a IS NOT NULL) AND (a < 67))
- -> Seq Scan on part_10_20
- Filter: ((a IS NOT NULL) AND (a < 67))
- -> Seq Scan on part_21_30
- Filter: ((a IS NOT NULL) AND (a < 67))
- -> Seq Scan on part_40_inf
- Filter: ((a IS NOT NULL) AND (a < 67))
-> Seq Scan on part_1_10_ab
Filter: ((a IS NOT NULL) AND (a < 67))
-> Seq Scan on part_1_10_cd
Filter: ((a IS NOT NULL) AND (a < 67))
-> Seq Scan on part_40_inf_null
Filter: ((a IS NOT NULL) AND (a < 67))
-(29 rows)
+(19 rows)
explain (costs off) select * from range_list_parted where a >= 30;
- QUERY PLAN
--------------------------------------
+ QUERY PLAN
+------------------------------------
Append
- -> Seq Scan on range_list_parted
- Filter: (a >= 30)
- -> Seq Scan on part_40_inf
- Filter: (a >= 30)
-> Seq Scan on part_40_inf_ab
Filter: (a >= 30)
-> Seq Scan on part_40_inf_cd
Filter: (a >= 30)
-> Seq Scan on part_40_inf_null
Filter: (a >= 30)
-(11 rows)
+(7 rows)
drop table list_parted;
drop table range_list_parted;
QUERY PLAN
-------------------------------------------
Append
- -> Sample Scan on parted_sample
- Sampling: bernoulli ('100'::real)
-> Sample Scan on parted_sample_1
Sampling: bernoulli ('100'::real)
-> Sample Scan on parted_sample_2
Sampling: bernoulli ('100'::real)
-(7 rows)
+(5 rows)
drop table parted_sample, parted_sample_1, parted_sample_2;
select tableoid::regclass::text as relname, bar.* from bar order by 1,2;
+-- Check UPDATE with *partitioned* inherited target and an appendrel subquery
+create table some_tab (a int);
+insert into some_tab values (0);
+create table some_tab_child () inherits (some_tab);
+insert into some_tab_child values (1);
+create table parted_tab (a int, b char) partition by list (a);
+create table parted_tab_part1 partition of parted_tab for values in (1);
+create table parted_tab_part2 partition of parted_tab for values in (2);
+create table parted_tab_part3 partition of parted_tab for values in (3);
+insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a');
+
+update parted_tab set b = 'b'
+from
+ (select a from some_tab union all select a+1 from some_tab) ss (a)
+where parted_tab.a = ss.a;
+select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2;
+
+truncate parted_tab;
+insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a');
+update parted_tab set b = 'b'
+from
+ (select 0 from parted_tab union all select 1 from parted_tab) ss (a)
+where parted_tab.a = ss.a;
+select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2;
+
+drop table parted_tab;
+drop table some_tab cascade;
+
/* Test multiple inheritance of column defaults */
CREATE TABLE firstparent (tomorrow date default now()::date + 1);