mirror of
https://github.com/postgres/postgres.git
synced 2025-08-27 07:42:10 +03:00
Add TID Range Scans to support efficient scanning ranges of TIDs
This adds a new executor node named TID Range Scan. The query planner will generate paths for TID Range scans when quals are discovered on base relations which search for ranges on the table's ctid column. These ranges may be open at either end. For example, WHERE ctid >= '(10,0)'; will return all tuples on page 10 and over. To support this, two new optional callback functions have been added to table AM. scan_set_tidrange is used to set the scan range to just the given range of TIDs. scan_getnextslot_tidrange fetches the next tuple in the given range. For AMs were scanning ranges of TIDs would not make sense, these functions can be set to NULL in the TableAmRoutine. The query planner won't generate TID Range Scan Paths in that case. Author: Edmund Horner, David Rowley Reviewed-by: David Rowley, Tomas Vondra, Tom Lane, Andres Freund, Zhihong Yu Discussion: https://postgr.es/m/CAMyN-kB-nFTkF=VA_JPwFNo08S0d-Yk0F741S2B7LDmYAi8eyA@mail.gmail.com
This commit is contained in:
@@ -374,6 +374,7 @@ RelOptInfo - a relation or joined relations
|
||||
IndexPath - index scan
|
||||
BitmapHeapPath - top of a bitmapped index scan
|
||||
TidPath - scan by CTID
|
||||
TidRangePath - scan a contiguous range of CTIDs
|
||||
SubqueryScanPath - scan a subquery-in-FROM
|
||||
ForeignPath - scan a foreign table, foreign join or foreign upper-relation
|
||||
CustomPath - for custom scan providers
|
||||
|
@@ -1283,6 +1283,101 @@ cost_tidscan(Path *path, PlannerInfo *root,
|
||||
path->total_cost = startup_cost + run_cost;
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_tidrangescan
|
||||
* Determines and sets the costs of scanning a relation using a range of
|
||||
* TIDs for 'path'
|
||||
*
|
||||
* 'baserel' is the relation to be scanned
|
||||
* 'tidrangequals' is the list of TID-checkable range quals
|
||||
* 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
|
||||
*/
|
||||
void
|
||||
cost_tidrangescan(Path *path, PlannerInfo *root,
|
||||
RelOptInfo *baserel, List *tidrangequals,
|
||||
ParamPathInfo *param_info)
|
||||
{
|
||||
Selectivity selectivity;
|
||||
double pages;
|
||||
Cost startup_cost = 0;
|
||||
Cost run_cost = 0;
|
||||
QualCost qpqual_cost;
|
||||
Cost cpu_per_tuple;
|
||||
QualCost tid_qual_cost;
|
||||
double ntuples;
|
||||
double nseqpages;
|
||||
double spc_random_page_cost;
|
||||
double spc_seq_page_cost;
|
||||
|
||||
/* Should only be applied to base relations */
|
||||
Assert(baserel->relid > 0);
|
||||
Assert(baserel->rtekind == RTE_RELATION);
|
||||
|
||||
/* Mark the path with the correct row estimate */
|
||||
if (param_info)
|
||||
path->rows = param_info->ppi_rows;
|
||||
else
|
||||
path->rows = baserel->rows;
|
||||
|
||||
/* Count how many tuples and pages we expect to scan */
|
||||
selectivity = clauselist_selectivity(root, tidrangequals, baserel->relid,
|
||||
JOIN_INNER, NULL);
|
||||
pages = ceil(selectivity * baserel->pages);
|
||||
|
||||
if (pages <= 0.0)
|
||||
pages = 1.0;
|
||||
|
||||
/*
|
||||
* The first page in a range requires a random seek, but each subsequent
|
||||
* page is just a normal sequential page read. NOTE: it's desirable for
|
||||
* TID Range Scans to cost more than the equivalent Sequential Scans,
|
||||
* because Seq Scans have some performance advantages such as scan
|
||||
* synchronization and parallelizability, and we'd prefer one of them to
|
||||
* be picked unless a TID Range Scan really is better.
|
||||
*/
|
||||
ntuples = selectivity * baserel->tuples;
|
||||
nseqpages = pages - 1.0;
|
||||
|
||||
if (!enable_tidscan)
|
||||
startup_cost += disable_cost;
|
||||
|
||||
/*
|
||||
* The TID qual expressions will be computed once, any other baserestrict
|
||||
* quals once per retrieved tuple.
|
||||
*/
|
||||
cost_qual_eval(&tid_qual_cost, tidrangequals, root);
|
||||
|
||||
/* fetch estimated page cost for tablespace containing table */
|
||||
get_tablespace_page_costs(baserel->reltablespace,
|
||||
&spc_random_page_cost,
|
||||
&spc_seq_page_cost);
|
||||
|
||||
/* disk costs; 1 random page and the remainder as seq pages */
|
||||
run_cost += spc_random_page_cost + spc_seq_page_cost * nseqpages;
|
||||
|
||||
/* Add scanning CPU costs */
|
||||
get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
|
||||
|
||||
/*
|
||||
* XXX currently we assume TID quals are a subset of qpquals at this
|
||||
* point; they will be removed (if possible) when we create the plan, so
|
||||
* we subtract their cost from the total qpqual cost. (If the TID quals
|
||||
* can't be removed, this is a mistake and we're going to underestimate
|
||||
* the CPU cost a bit.)
|
||||
*/
|
||||
startup_cost += qpqual_cost.startup + tid_qual_cost.per_tuple;
|
||||
cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple -
|
||||
tid_qual_cost.per_tuple;
|
||||
run_cost += cpu_per_tuple * ntuples;
|
||||
|
||||
/* tlist eval costs are paid per output row, not per tuple scanned */
|
||||
startup_cost += path->pathtarget->cost.startup;
|
||||
run_cost += path->pathtarget->cost.per_tuple * path->rows;
|
||||
|
||||
path->startup_cost = startup_cost;
|
||||
path->total_cost = startup_cost + run_cost;
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_subqueryscan
|
||||
* Determines and returns the cost of scanning a subquery RTE.
|
||||
|
@@ -2,9 +2,9 @@
|
||||
*
|
||||
* tidpath.c
|
||||
* Routines to determine which TID conditions are usable for scanning
|
||||
* a given relation, and create TidPaths accordingly.
|
||||
* a given relation, and create TidPaths and TidRangePaths accordingly.
|
||||
*
|
||||
* What we are looking for here is WHERE conditions of the form
|
||||
* For TidPaths, we look for WHERE conditions of the form
|
||||
* "CTID = pseudoconstant", which can be implemented by just fetching
|
||||
* the tuple directly via heap_fetch(). We can also handle OR'd conditions
|
||||
* such as (CTID = const1) OR (CTID = const2), as well as ScalarArrayOpExpr
|
||||
@@ -23,6 +23,9 @@
|
||||
* a function, but in practice it works better to keep the special node
|
||||
* representation all the way through to execution.
|
||||
*
|
||||
* Additionally, TidRangePaths may be created for conditions of the form
|
||||
* "CTID relop pseudoconstant", where relop is one of >,>=,<,<=, and
|
||||
* AND-clauses composed of such conditions.
|
||||
*
|
||||
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
@@ -63,14 +66,14 @@ IsCTIDVar(Var *var, RelOptInfo *rel)
|
||||
|
||||
/*
|
||||
* Check to see if a RestrictInfo is of the form
|
||||
* CTID = pseudoconstant
|
||||
* CTID OP pseudoconstant
|
||||
* or
|
||||
* pseudoconstant = CTID
|
||||
* where the CTID Var belongs to relation "rel", and nothing on the
|
||||
* other side of the clause does.
|
||||
* pseudoconstant OP CTID
|
||||
* where OP is a binary operation, the CTID Var belongs to relation "rel",
|
||||
* and nothing on the other side of the clause does.
|
||||
*/
|
||||
static bool
|
||||
IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
|
||||
IsBinaryTidClause(RestrictInfo *rinfo, RelOptInfo *rel)
|
||||
{
|
||||
OpExpr *node;
|
||||
Node *arg1,
|
||||
@@ -83,10 +86,9 @@ IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
|
||||
return false;
|
||||
node = (OpExpr *) rinfo->clause;
|
||||
|
||||
/* Operator must be tideq */
|
||||
if (node->opno != TIDEqualOperator)
|
||||
/* OpExpr must have two arguments */
|
||||
if (list_length(node->args) != 2)
|
||||
return false;
|
||||
Assert(list_length(node->args) == 2);
|
||||
arg1 = linitial(node->args);
|
||||
arg2 = lsecond(node->args);
|
||||
|
||||
@@ -116,6 +118,50 @@ IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
|
||||
return true; /* success */
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if a RestrictInfo is of the form
|
||||
* CTID = pseudoconstant
|
||||
* or
|
||||
* pseudoconstant = CTID
|
||||
* where the CTID Var belongs to relation "rel", and nothing on the
|
||||
* other side of the clause does.
|
||||
*/
|
||||
static bool
|
||||
IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
|
||||
{
|
||||
if (!IsBinaryTidClause(rinfo, rel))
|
||||
return false;
|
||||
|
||||
if (((OpExpr *) rinfo->clause)->opno == TIDEqualOperator)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if a RestrictInfo is of the form
|
||||
* CTID OP pseudoconstant
|
||||
* or
|
||||
* pseudoconstant OP CTID
|
||||
* where OP is a range operator such as <, <=, >, or >=, the CTID Var belongs
|
||||
* to relation "rel", and nothing on the other side of the clause does.
|
||||
*/
|
||||
static bool
|
||||
IsTidRangeClause(RestrictInfo *rinfo, RelOptInfo *rel)
|
||||
{
|
||||
Oid opno;
|
||||
|
||||
if (!IsBinaryTidClause(rinfo, rel))
|
||||
return false;
|
||||
opno = ((OpExpr *) rinfo->clause)->opno;
|
||||
|
||||
if (opno == TIDLessOperator || opno == TIDLessEqOperator ||
|
||||
opno == TIDGreaterOperator || opno == TIDGreaterEqOperator)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if a RestrictInfo is of the form
|
||||
* CTID = ANY (pseudoconstant_array)
|
||||
@@ -222,7 +268,7 @@ TidQualFromRestrictInfo(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
|
||||
*
|
||||
* Returns a List of CTID qual RestrictInfos for the specified rel (with
|
||||
* implicit OR semantics across the list), or NIL if there are no usable
|
||||
* conditions.
|
||||
* equality conditions.
|
||||
*
|
||||
* This function is just concerned with handling AND/OR recursion.
|
||||
*/
|
||||
@@ -301,6 +347,34 @@ TidQualFromRestrictInfoList(PlannerInfo *root, List *rlist, RelOptInfo *rel)
|
||||
return rlst;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a set of CTID range conditions from implicit-AND List of RestrictInfos
|
||||
*
|
||||
* Returns a List of CTID range qual RestrictInfos for the specified rel
|
||||
* (with implicit AND semantics across the list), or NIL if there are no
|
||||
* usable range conditions or if the rel's table AM does not support TID range
|
||||
* scans.
|
||||
*/
|
||||
static List *
|
||||
TidRangeQualFromRestrictInfoList(List *rlist, RelOptInfo *rel)
|
||||
{
|
||||
List *rlst = NIL;
|
||||
ListCell *l;
|
||||
|
||||
if ((rel->amflags & AMFLAG_HAS_TID_RANGE) == 0)
|
||||
return NIL;
|
||||
|
||||
foreach(l, rlist)
|
||||
{
|
||||
RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
|
||||
|
||||
if (IsTidRangeClause(rinfo, rel))
|
||||
rlst = lappend(rlst, rinfo);
|
||||
}
|
||||
|
||||
return rlst;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a list of join clauses involving our rel, create a parameterized
|
||||
* TidPath for each one that is a suitable TidEqual clause.
|
||||
@@ -385,6 +459,7 @@ void
|
||||
create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
{
|
||||
List *tidquals;
|
||||
List *tidrangequals;
|
||||
|
||||
/*
|
||||
* If any suitable quals exist in the rel's baserestrict list, generate a
|
||||
@@ -392,7 +467,7 @@ create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
*/
|
||||
tidquals = TidQualFromRestrictInfoList(root, rel->baserestrictinfo, rel);
|
||||
|
||||
if (tidquals)
|
||||
if (tidquals != NIL)
|
||||
{
|
||||
/*
|
||||
* This path uses no join clauses, but it could still have required
|
||||
@@ -404,6 +479,26 @@ create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
required_outer));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are range quals in the baserestrict list, generate a
|
||||
* TidRangePath.
|
||||
*/
|
||||
tidrangequals = TidRangeQualFromRestrictInfoList(rel->baserestrictinfo,
|
||||
rel);
|
||||
|
||||
if (tidrangequals != NIL)
|
||||
{
|
||||
/*
|
||||
* This path uses no join clauses, but it could still have required
|
||||
* parameterization due to LATERAL refs in its tlist.
|
||||
*/
|
||||
Relids required_outer = rel->lateral_relids;
|
||||
|
||||
add_path(rel, (Path *) create_tidrangescan_path(root, rel,
|
||||
tidrangequals,
|
||||
required_outer));
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to generate parameterized TidPaths using equality clauses extracted
|
||||
* from EquivalenceClasses. (This is important since simple "t1.ctid =
|
||||
|
@@ -129,6 +129,10 @@ static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
|
||||
static void bitmap_subplan_mark_shared(Plan *plan);
|
||||
static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
|
||||
List *tlist, List *scan_clauses);
|
||||
static TidRangeScan *create_tidrangescan_plan(PlannerInfo *root,
|
||||
TidRangePath *best_path,
|
||||
List *tlist,
|
||||
List *scan_clauses);
|
||||
static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root,
|
||||
SubqueryScanPath *best_path,
|
||||
List *tlist, List *scan_clauses);
|
||||
@@ -193,6 +197,8 @@ static BitmapHeapScan *make_bitmap_heapscan(List *qptlist,
|
||||
Index scanrelid);
|
||||
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||
List *tidquals);
|
||||
static TidRangeScan *make_tidrangescan(List *qptlist, List *qpqual,
|
||||
Index scanrelid, List *tidrangequals);
|
||||
static SubqueryScan *make_subqueryscan(List *qptlist,
|
||||
List *qpqual,
|
||||
Index scanrelid,
|
||||
@@ -384,6 +390,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
|
||||
case T_IndexOnlyScan:
|
||||
case T_BitmapHeapScan:
|
||||
case T_TidScan:
|
||||
case T_TidRangeScan:
|
||||
case T_SubqueryScan:
|
||||
case T_FunctionScan:
|
||||
case T_TableFuncScan:
|
||||
@@ -679,6 +686,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
|
||||
scan_clauses);
|
||||
break;
|
||||
|
||||
case T_TidRangeScan:
|
||||
plan = (Plan *) create_tidrangescan_plan(root,
|
||||
(TidRangePath *) best_path,
|
||||
tlist,
|
||||
scan_clauses);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
plan = (Plan *) create_subqueryscan_plan(root,
|
||||
(SubqueryScanPath *) best_path,
|
||||
@@ -3436,6 +3450,71 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
|
||||
return scan_plan;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_tidrangescan_plan
|
||||
* Returns a tidrangescan plan for the base relation scanned by 'best_path'
|
||||
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
|
||||
*/
|
||||
static TidRangeScan *
|
||||
create_tidrangescan_plan(PlannerInfo *root, TidRangePath *best_path,
|
||||
List *tlist, List *scan_clauses)
|
||||
{
|
||||
TidRangeScan *scan_plan;
|
||||
Index scan_relid = best_path->path.parent->relid;
|
||||
List *tidrangequals = best_path->tidrangequals;
|
||||
|
||||
/* it should be a base rel... */
|
||||
Assert(scan_relid > 0);
|
||||
Assert(best_path->path.parent->rtekind == RTE_RELATION);
|
||||
|
||||
/*
|
||||
* The qpqual list must contain all restrictions not enforced by the
|
||||
* tidrangequals list. tidrangequals has AND semantics, so we can simply
|
||||
* remove any qual that appears in it.
|
||||
*/
|
||||
{
|
||||
List *qpqual = NIL;
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, scan_clauses)
|
||||
{
|
||||
RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
|
||||
|
||||
if (rinfo->pseudoconstant)
|
||||
continue; /* we may drop pseudoconstants here */
|
||||
if (list_member_ptr(tidrangequals, rinfo))
|
||||
continue; /* simple duplicate */
|
||||
qpqual = lappend(qpqual, rinfo);
|
||||
}
|
||||
scan_clauses = qpqual;
|
||||
}
|
||||
|
||||
/* Sort clauses into best execution order */
|
||||
scan_clauses = order_qual_clauses(root, scan_clauses);
|
||||
|
||||
/* Reduce RestrictInfo lists to bare expressions; ignore pseudoconstants */
|
||||
tidrangequals = extract_actual_clauses(tidrangequals, false);
|
||||
scan_clauses = extract_actual_clauses(scan_clauses, false);
|
||||
|
||||
/* Replace any outer-relation variables with nestloop params */
|
||||
if (best_path->path.param_info)
|
||||
{
|
||||
tidrangequals = (List *)
|
||||
replace_nestloop_params(root, (Node *) tidrangequals);
|
||||
scan_clauses = (List *)
|
||||
replace_nestloop_params(root, (Node *) scan_clauses);
|
||||
}
|
||||
|
||||
scan_plan = make_tidrangescan(tlist,
|
||||
scan_clauses,
|
||||
scan_relid,
|
||||
tidrangequals);
|
||||
|
||||
copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
|
||||
|
||||
return scan_plan;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_subqueryscan_plan
|
||||
* Returns a subqueryscan plan for the base relation scanned by 'best_path'
|
||||
@@ -5369,6 +5448,25 @@ make_tidscan(List *qptlist,
|
||||
return node;
|
||||
}
|
||||
|
||||
static TidRangeScan *
|
||||
make_tidrangescan(List *qptlist,
|
||||
List *qpqual,
|
||||
Index scanrelid,
|
||||
List *tidrangequals)
|
||||
{
|
||||
TidRangeScan *node = makeNode(TidRangeScan);
|
||||
Plan *plan = &node->scan.plan;
|
||||
|
||||
plan->targetlist = qptlist;
|
||||
plan->qual = qpqual;
|
||||
plan->lefttree = NULL;
|
||||
plan->righttree = NULL;
|
||||
node->scan.scanrelid = scanrelid;
|
||||
node->tidrangequals = tidrangequals;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static SubqueryScan *
|
||||
make_subqueryscan(List *qptlist,
|
||||
List *qpqual,
|
||||
|
@@ -619,6 +619,22 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
rtoffset, 1);
|
||||
}
|
||||
break;
|
||||
case T_TidRangeScan:
|
||||
{
|
||||
TidRangeScan *splan = (TidRangeScan *) plan;
|
||||
|
||||
splan->scan.scanrelid += rtoffset;
|
||||
splan->scan.plan.targetlist =
|
||||
fix_scan_list(root, splan->scan.plan.targetlist,
|
||||
rtoffset, NUM_EXEC_TLIST(plan));
|
||||
splan->scan.plan.qual =
|
||||
fix_scan_list(root, splan->scan.plan.qual,
|
||||
rtoffset, NUM_EXEC_QUAL(plan));
|
||||
splan->tidrangequals =
|
||||
fix_scan_list(root, splan->tidrangequals,
|
||||
rtoffset, 1);
|
||||
}
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
/* Needs special treatment, see comments below */
|
||||
return set_subqueryscan_references(root,
|
||||
|
@@ -2367,6 +2367,12 @@ finalize_plan(PlannerInfo *root, Plan *plan,
|
||||
context.paramids = bms_add_members(context.paramids, scan_params);
|
||||
break;
|
||||
|
||||
case T_TidRangeScan:
|
||||
finalize_primnode((Node *) ((TidRangeScan *) plan)->tidrangequals,
|
||||
&context);
|
||||
context.paramids = bms_add_members(context.paramids, scan_params);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
{
|
||||
SubqueryScan *sscan = (SubqueryScan *) plan;
|
||||
|
@@ -1203,6 +1203,35 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_tidrangescan_path
|
||||
* Creates a path corresponding to a scan by a range of TIDs, returning
|
||||
* the pathnode.
|
||||
*/
|
||||
TidRangePath *
|
||||
create_tidrangescan_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
List *tidrangequals, Relids required_outer)
|
||||
{
|
||||
TidRangePath *pathnode = makeNode(TidRangePath);
|
||||
|
||||
pathnode->path.pathtype = T_TidRangeScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.pathtarget = rel->reltarget;
|
||||
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
|
||||
required_outer);
|
||||
pathnode->path.parallel_aware = false;
|
||||
pathnode->path.parallel_safe = rel->consider_parallel;
|
||||
pathnode->path.parallel_workers = 0;
|
||||
pathnode->path.pathkeys = NIL; /* always unordered */
|
||||
|
||||
pathnode->tidrangequals = tidrangequals;
|
||||
|
||||
cost_tidrangescan(&pathnode->path, root, rel, tidrangequals,
|
||||
pathnode->path.param_info);
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_append_path
|
||||
* Creates a path corresponding to an Append plan, returning the
|
||||
|
@@ -467,6 +467,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
|
||||
/* Collect info about relation's foreign keys, if relevant */
|
||||
get_relation_foreign_keys(root, rel, relation, inhparent);
|
||||
|
||||
/* Collect info about functions implemented by the rel's table AM. */
|
||||
if (relation->rd_tableam &&
|
||||
relation->rd_tableam->scan_set_tidrange != NULL &&
|
||||
relation->rd_tableam->scan_getnextslot_tidrange != NULL)
|
||||
rel->amflags |= AMFLAG_HAS_TID_RANGE;
|
||||
|
||||
/*
|
||||
* Collect info about relation's partitioning scheme, if any. Only
|
||||
* inheritance parents may be partitioned.
|
||||
|
@@ -234,6 +234,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
|
||||
rel->subroot = NULL;
|
||||
rel->subplan_params = NIL;
|
||||
rel->rel_parallel_workers = -1; /* set up in get_relation_info */
|
||||
rel->amflags = 0;
|
||||
rel->serverid = InvalidOid;
|
||||
rel->userid = rte->checkAsUser;
|
||||
rel->useridiscurrent = false;
|
||||
@@ -646,6 +647,7 @@ build_join_rel(PlannerInfo *root,
|
||||
joinrel->subroot = NULL;
|
||||
joinrel->subplan_params = NIL;
|
||||
joinrel->rel_parallel_workers = -1;
|
||||
joinrel->amflags = 0;
|
||||
joinrel->serverid = InvalidOid;
|
||||
joinrel->userid = InvalidOid;
|
||||
joinrel->useridiscurrent = false;
|
||||
@@ -826,6 +828,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
|
||||
joinrel->eclass_indexes = NULL;
|
||||
joinrel->subroot = NULL;
|
||||
joinrel->subplan_params = NIL;
|
||||
joinrel->amflags = 0;
|
||||
joinrel->serverid = InvalidOid;
|
||||
joinrel->userid = InvalidOid;
|
||||
joinrel->useridiscurrent = false;
|
||||
|
Reference in New Issue
Block a user