mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Rearrange the implementation of index-only scans.
This commit changes index-only scans so that data is read directly from the index tuple without first generating a faux heap tuple. The only immediate benefit is that indexes on system columns (such as OID) can be used in index-only scans, but this is necessary infrastructure if we are ever to support index-only scans on expression indexes. The executor is now ready for that, though the planner still needs substantial work to recognize the possibility. To do this, Vars in index-only plan nodes have to refer to index columns not heap columns. I introduced a new special varno, INDEX_VAR, to mark such Vars to avoid confusion. (In passing, this commit renames the two existing special varnos to OUTER_VAR and INNER_VAR.) This allows ruleutils.c to handle them with logic similar to what we use for subplan reference Vars. Since index-only scans are now fundamentally different from regular indexscans so far as their expression subtrees are concerned, I also chose to change them to have their own plan node type (and hence, their own executor source file).
This commit is contained in:
@ -199,14 +199,15 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
true, NULL, SAOP_FORBID, ST_ANYSCAN);
|
||||
|
||||
/*
|
||||
* Submit all the ones that can form plain IndexScan plans to add_path. (A
|
||||
* plain IndexPath always represents a plain IndexScan plan; however some
|
||||
* of the indexes might support only bitmap scans, and those we mustn't
|
||||
* submit to add_path here.) Also, pick out the ones that might be useful
|
||||
* as bitmap scans. For that, we must discard indexes that don't support
|
||||
* bitmap scans, and we also are only interested in paths that have some
|
||||
* selectivity; we should discard anything that was generated solely for
|
||||
* ordering purposes.
|
||||
* Submit all the ones that can form plain IndexScan plans to add_path.
|
||||
* (A plain IndexPath might represent either a plain IndexScan or an
|
||||
* IndexOnlyScan, but for our purposes here the distinction does not
|
||||
* matter. However, some of the indexes might support only bitmap scans,
|
||||
* and those we mustn't submit to add_path here.) Also, pick out the ones
|
||||
* that might be useful as bitmap scans. For that, we must discard
|
||||
* indexes that don't support bitmap scans, and we also are only
|
||||
* interested in paths that have some selectivity; we should discard
|
||||
* anything that was generated solely for ordering purposes.
|
||||
*/
|
||||
bitindexpaths = NIL;
|
||||
foreach(l, indexpaths)
|
||||
@ -1107,11 +1108,9 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
|
||||
|
||||
/*
|
||||
* For the moment, we just ignore index expressions. It might be nice
|
||||
* to do something with them, later. We also ignore index columns
|
||||
* that are system columns (such as OID), because the virtual-tuple
|
||||
* coding used by IndexStoreHeapTuple() can't deal with them.
|
||||
* to do something with them, later.
|
||||
*/
|
||||
if (attno <= 0)
|
||||
if (attno == 0)
|
||||
continue;
|
||||
|
||||
index_attrs =
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
@ -35,8 +34,6 @@ static PathKey *make_canonical_pathkey(PlannerInfo *root,
|
||||
EquivalenceClass *eclass, Oid opfamily,
|
||||
int strategy, bool nulls_first);
|
||||
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
|
||||
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
|
||||
AttrNumber varattno);
|
||||
static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
|
||||
|
||||
|
||||
@ -504,21 +501,24 @@ build_index_pathkeys(PlannerInfo *root,
|
||||
ScanDirection scandir)
|
||||
{
|
||||
List *retval = NIL;
|
||||
ListCell *indexprs_item;
|
||||
ListCell *lc;
|
||||
int i;
|
||||
|
||||
if (index->sortopfamily == NULL)
|
||||
return NIL; /* non-orderable index */
|
||||
|
||||
indexprs_item = list_head(index->indexprs);
|
||||
for (i = 0; i < index->ncolumns; i++)
|
||||
i = 0;
|
||||
foreach(lc, index->indextlist)
|
||||
{
|
||||
TargetEntry *indextle = (TargetEntry *) lfirst(lc);
|
||||
Expr *indexkey;
|
||||
bool reverse_sort;
|
||||
bool nulls_first;
|
||||
int ikey;
|
||||
Expr *indexkey;
|
||||
PathKey *cpathkey;
|
||||
|
||||
/* We assume we don't need to make a copy of the tlist item */
|
||||
indexkey = indextle->expr;
|
||||
|
||||
if (ScanDirectionIsBackward(scandir))
|
||||
{
|
||||
reverse_sort = !index->reverse_sort[i];
|
||||
@ -530,21 +530,6 @@ build_index_pathkeys(PlannerInfo *root,
|
||||
nulls_first = index->nulls_first[i];
|
||||
}
|
||||
|
||||
ikey = index->indexkeys[i];
|
||||
if (ikey != 0)
|
||||
{
|
||||
/* simple index column */
|
||||
indexkey = (Expr *) find_indexkey_var(root, index->rel, ikey);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* expression --- assume we need not copy it */
|
||||
if (indexprs_item == NULL)
|
||||
elog(ERROR, "wrong number of index expressions");
|
||||
indexkey = (Expr *) lfirst(indexprs_item);
|
||||
indexprs_item = lnext(indexprs_item);
|
||||
}
|
||||
|
||||
/* OK, try to make a canonical pathkey for this sort key */
|
||||
cpathkey = make_pathkey_from_sortinfo(root,
|
||||
indexkey,
|
||||
@ -568,46 +553,13 @@ build_index_pathkeys(PlannerInfo *root,
|
||||
/* Add to list unless redundant */
|
||||
if (!pathkey_is_redundant(cpathkey, retval))
|
||||
retval = lappend(retval, cpathkey);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find or make a Var node for the specified attribute of the rel.
|
||||
*
|
||||
* We first look for the var in the rel's target list, because that's
|
||||
* easy and fast. But the var might not be there (this should normally
|
||||
* only happen for vars that are used in WHERE restriction clauses,
|
||||
* but not in join clauses or in the SELECT target list). In that case,
|
||||
* gin up a Var node the hard way.
|
||||
*/
|
||||
static Var *
|
||||
find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
|
||||
{
|
||||
ListCell *temp;
|
||||
Index relid;
|
||||
Oid reloid,
|
||||
vartypeid,
|
||||
varcollid;
|
||||
int32 type_mod;
|
||||
|
||||
foreach(temp, rel->reltargetlist)
|
||||
{
|
||||
Var *var = (Var *) lfirst(temp);
|
||||
|
||||
if (IsA(var, Var) &&
|
||||
var->varattno == varattno)
|
||||
return var;
|
||||
}
|
||||
|
||||
relid = rel->relid;
|
||||
reloid = getrelid(relid, root->parse->rtable);
|
||||
get_atttypetypmodcoll(reloid, varattno, &vartypeid, &type_mod, &varcollid);
|
||||
|
||||
return makeVar(relid, varattno, vartypeid, type_mod, varcollid, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* convert_subquery_pathkeys
|
||||
* Build a pathkeys list that describes the ordering of a subquery's
|
||||
|
@ -53,8 +53,8 @@ static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path
|
||||
static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
|
||||
static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
|
||||
List *tlist, List *scan_clauses);
|
||||
static IndexScan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
|
||||
List *tlist, List *scan_clauses);
|
||||
static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
|
||||
List *tlist, List *scan_clauses, bool indexonly);
|
||||
static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
|
||||
BitmapHeapPath *best_path,
|
||||
List *tlist, List *scan_clauses);
|
||||
@ -95,7 +95,12 @@ static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
|
||||
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||
Oid indexid, List *indexqual, List *indexqualorig,
|
||||
List *indexorderby, List *indexorderbyorig,
|
||||
ScanDirection indexscandir, bool indexonly);
|
||||
ScanDirection indexscandir);
|
||||
static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual,
|
||||
Index scanrelid, Oid indexid,
|
||||
List *indexqual, List *indexorderby,
|
||||
List *indextlist,
|
||||
ScanDirection indexscandir);
|
||||
static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid,
|
||||
List *indexqual,
|
||||
List *indexqualorig);
|
||||
@ -206,6 +211,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
|
||||
{
|
||||
case T_SeqScan:
|
||||
case T_IndexScan:
|
||||
case T_IndexOnlyScan:
|
||||
case T_BitmapHeapScan:
|
||||
case T_TidScan:
|
||||
case T_SubqueryScan:
|
||||
@ -274,10 +280,18 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
|
||||
*/
|
||||
if (use_physical_tlist(root, rel))
|
||||
{
|
||||
tlist = build_physical_tlist(root, rel);
|
||||
/* if fail because of dropped cols, use regular method */
|
||||
if (tlist == NIL)
|
||||
tlist = build_relation_tlist(rel);
|
||||
if (best_path->pathtype == T_IndexOnlyScan)
|
||||
{
|
||||
/* For index-only scan, the preferred tlist is the index's */
|
||||
tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
|
||||
}
|
||||
else
|
||||
{
|
||||
tlist = build_physical_tlist(root, rel);
|
||||
/* if fail because of dropped cols, use regular method */
|
||||
if (tlist == NIL)
|
||||
tlist = build_relation_tlist(rel);
|
||||
}
|
||||
}
|
||||
else
|
||||
tlist = build_relation_tlist(rel);
|
||||
@ -302,7 +316,16 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
|
||||
plan = (Plan *) create_indexscan_plan(root,
|
||||
(IndexPath *) best_path,
|
||||
tlist,
|
||||
scan_clauses);
|
||||
scan_clauses,
|
||||
false);
|
||||
break;
|
||||
|
||||
case T_IndexOnlyScan:
|
||||
plan = (Plan *) create_indexscan_plan(root,
|
||||
(IndexPath *) best_path,
|
||||
tlist,
|
||||
scan_clauses,
|
||||
true);
|
||||
break;
|
||||
|
||||
case T_BitmapHeapScan:
|
||||
@ -476,6 +499,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
|
||||
{
|
||||
case T_SeqScan:
|
||||
case T_IndexScan:
|
||||
case T_IndexOnlyScan:
|
||||
case T_BitmapHeapScan:
|
||||
case T_TidScan:
|
||||
case T_SubqueryScan:
|
||||
@ -1044,16 +1068,23 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
|
||||
* Returns an indexscan plan for the base relation scanned by 'best_path'
|
||||
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
|
||||
*
|
||||
* We use this for both plain IndexScans and IndexOnlyScans, because the
|
||||
* qual preprocessing work is the same for both. Note that the caller tells
|
||||
* us which to build --- we don't look at best_path->path.pathtype, because
|
||||
* create_bitmap_subplan needs to be able to override the prior decision.
|
||||
*
|
||||
* The indexquals list of the path contains implicitly-ANDed qual conditions.
|
||||
* The list can be empty --- then no index restrictions will be applied during
|
||||
* the scan.
|
||||
*/
|
||||
static IndexScan *
|
||||
static Scan *
|
||||
create_indexscan_plan(PlannerInfo *root,
|
||||
IndexPath *best_path,
|
||||
List *tlist,
|
||||
List *scan_clauses)
|
||||
List *scan_clauses,
|
||||
bool indexonly)
|
||||
{
|
||||
Scan *scan_plan;
|
||||
List *indexquals = best_path->indexquals;
|
||||
List *indexorderbys = best_path->indexorderbys;
|
||||
Index baserelid = best_path->path.parent->relid;
|
||||
@ -1063,7 +1094,6 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
List *fixed_indexquals;
|
||||
List *fixed_indexorderbys;
|
||||
ListCell *l;
|
||||
IndexScan *scan_plan;
|
||||
|
||||
/* it should be a base rel... */
|
||||
Assert(baserelid > 0);
|
||||
@ -1077,7 +1107,7 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
|
||||
/*
|
||||
* The executor needs a copy with the indexkey on the left of each clause
|
||||
* and with index attr numbers substituted for table ones.
|
||||
* and with index Vars substituted for table ones.
|
||||
*/
|
||||
fixed_indexquals = fix_indexqual_references(root, best_path, indexquals);
|
||||
|
||||
@ -1175,20 +1205,29 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
}
|
||||
|
||||
/* Finally ready to build the plan node */
|
||||
scan_plan = make_indexscan(tlist,
|
||||
qpqual,
|
||||
baserelid,
|
||||
indexoid,
|
||||
fixed_indexquals,
|
||||
stripped_indexquals,
|
||||
fixed_indexorderbys,
|
||||
indexorderbys,
|
||||
best_path->indexscandir,
|
||||
best_path->indexonly);
|
||||
if (indexonly)
|
||||
scan_plan = (Scan *) make_indexonlyscan(tlist,
|
||||
qpqual,
|
||||
baserelid,
|
||||
indexoid,
|
||||
fixed_indexquals,
|
||||
fixed_indexorderbys,
|
||||
best_path->indexinfo->indextlist,
|
||||
best_path->indexscandir);
|
||||
else
|
||||
scan_plan = (Scan *) make_indexscan(tlist,
|
||||
qpqual,
|
||||
baserelid,
|
||||
indexoid,
|
||||
fixed_indexquals,
|
||||
stripped_indexquals,
|
||||
fixed_indexorderbys,
|
||||
indexorderbys,
|
||||
best_path->indexscandir);
|
||||
|
||||
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
|
||||
copy_path_costsize(&scan_plan->plan, &best_path->path);
|
||||
/* use the indexscan-specific rows estimate, not the parent rel's */
|
||||
scan_plan->scan.plan.plan_rows = best_path->rows;
|
||||
scan_plan->plan.plan_rows = best_path->rows;
|
||||
|
||||
return scan_plan;
|
||||
}
|
||||
@ -1440,7 +1479,9 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
|
||||
ListCell *l;
|
||||
|
||||
/* Use the regular indexscan plan build machinery... */
|
||||
iscan = create_indexscan_plan(root, ipath, NIL, NIL);
|
||||
iscan = (IndexScan *) create_indexscan_plan(root, ipath,
|
||||
NIL, NIL, false);
|
||||
Assert(IsA(iscan, IndexScan));
|
||||
/* then convert to a bitmap indexscan */
|
||||
plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
|
||||
iscan->indexid,
|
||||
@ -2549,17 +2590,13 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path,
|
||||
/*
|
||||
* fix_indexqual_operand
|
||||
* Convert an indexqual expression to a Var referencing the index column.
|
||||
*
|
||||
* We represent index keys by Var nodes having varno == INDEX_VAR and varattno
|
||||
* equal to the index's attribute number (index column position).
|
||||
*/
|
||||
static Node *
|
||||
fix_indexqual_operand(Node *node, IndexOptInfo *index)
|
||||
{
|
||||
/*
|
||||
* We represent index keys by Var nodes having the varno of the base table
|
||||
* but varattno equal to the index's attribute number (index column
|
||||
* position). This is a bit hokey ... would be cleaner to use a
|
||||
* special-purpose node type that could not be mistaken for a regular Var.
|
||||
* But it will do for now.
|
||||
*/
|
||||
Var *result;
|
||||
int pos;
|
||||
ListCell *indexpr_item;
|
||||
@ -2583,6 +2620,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index)
|
||||
if (index->indexkeys[pos] == varatt)
|
||||
{
|
||||
result = (Var *) copyObject(node);
|
||||
result->varno = INDEX_VAR;
|
||||
result->varattno = pos + 1;
|
||||
return (Node *) result;
|
||||
}
|
||||
@ -2606,7 +2644,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index)
|
||||
if (equal(node, indexkey))
|
||||
{
|
||||
/* Found a match */
|
||||
result = makeVar(index->rel->relid, pos + 1,
|
||||
result = makeVar(INDEX_VAR, pos + 1,
|
||||
exprType(lfirst(indexpr_item)), -1,
|
||||
exprCollation(lfirst(indexpr_item)),
|
||||
0);
|
||||
@ -2842,8 +2880,7 @@ make_indexscan(List *qptlist,
|
||||
List *indexqualorig,
|
||||
List *indexorderby,
|
||||
List *indexorderbyorig,
|
||||
ScanDirection indexscandir,
|
||||
bool indexonly)
|
||||
ScanDirection indexscandir)
|
||||
{
|
||||
IndexScan *node = makeNode(IndexScan);
|
||||
Plan *plan = &node->scan.plan;
|
||||
@ -2860,7 +2897,34 @@ make_indexscan(List *qptlist,
|
||||
node->indexorderby = indexorderby;
|
||||
node->indexorderbyorig = indexorderbyorig;
|
||||
node->indexorderdir = indexscandir;
|
||||
node->indexonly = indexonly;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static IndexOnlyScan *
|
||||
make_indexonlyscan(List *qptlist,
|
||||
List *qpqual,
|
||||
Index scanrelid,
|
||||
Oid indexid,
|
||||
List *indexqual,
|
||||
List *indexorderby,
|
||||
List *indextlist,
|
||||
ScanDirection indexscandir)
|
||||
{
|
||||
IndexOnlyScan *node = makeNode(IndexOnlyScan);
|
||||
Plan *plan = &node->scan.plan;
|
||||
|
||||
/* cost should be inserted by caller */
|
||||
plan->targetlist = qptlist;
|
||||
plan->qual = qpqual;
|
||||
plan->lefttree = NULL;
|
||||
plan->righttree = NULL;
|
||||
node->scan.scanrelid = scanrelid;
|
||||
node->indexid = indexid;
|
||||
node->indexqual = indexqual;
|
||||
node->indexorderby = indexorderby;
|
||||
node->indextlist = indextlist;
|
||||
node->indexorderdir = indexscandir;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ typedef struct
|
||||
{
|
||||
PlannerInfo *root;
|
||||
indexed_tlist *subplan_itlist;
|
||||
Index newvarno;
|
||||
int rtoffset;
|
||||
} fix_upper_expr_context;
|
||||
|
||||
@ -81,6 +82,9 @@ typedef struct
|
||||
((List *) fix_scan_expr(root, (Node *) (lst), rtoffset))
|
||||
|
||||
static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
|
||||
static Plan *set_indexonlyscan_references(PlannerInfo *root,
|
||||
IndexOnlyScan *plan,
|
||||
int rtoffset);
|
||||
static Plan *set_subqueryscan_references(PlannerInfo *root,
|
||||
SubqueryScan *plan,
|
||||
int rtoffset);
|
||||
@ -113,6 +117,7 @@ static Node *fix_join_expr_mutator(Node *node,
|
||||
static Node *fix_upper_expr(PlannerInfo *root,
|
||||
Node *node,
|
||||
indexed_tlist *subplan_itlist,
|
||||
Index newvarno,
|
||||
int rtoffset);
|
||||
static Node *fix_upper_expr_mutator(Node *node,
|
||||
fix_upper_expr_context *context);
|
||||
@ -234,6 +239,16 @@ set_plan_references(PlannerInfo *root, Plan *plan)
|
||||
newrte->relid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for RT index overflow; it's very unlikely, but if it did happen,
|
||||
* the executor would get confused by varnos that match the special varno
|
||||
* values.
|
||||
*/
|
||||
if (IS_SPECIAL_VARNO(list_length(glob->finalrtable)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||
errmsg("too many range table entries")));
|
||||
|
||||
/*
|
||||
* Adjust RT indexes of PlanRowMarks and add to final rowmarks list
|
||||
*/
|
||||
@ -305,6 +320,13 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
fix_scan_list(root, splan->indexorderbyorig, rtoffset);
|
||||
}
|
||||
break;
|
||||
case T_IndexOnlyScan:
|
||||
{
|
||||
IndexOnlyScan *splan = (IndexOnlyScan *) plan;
|
||||
|
||||
return set_indexonlyscan_references(root, splan, rtoffset);
|
||||
}
|
||||
break;
|
||||
case T_BitmapIndexScan:
|
||||
{
|
||||
BitmapIndexScan *splan = (BitmapIndexScan *) plan;
|
||||
@ -652,6 +674,49 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
return plan;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_indexonlyscan_references
|
||||
* Do set_plan_references processing on an IndexOnlyScan
|
||||
*
|
||||
* This is unlike the handling of a plain IndexScan because we have to
|
||||
* convert Vars referencing the heap into Vars referencing the index.
|
||||
* We can use the fix_upper_expr machinery for that, by working from a
|
||||
* targetlist describing the index columns.
|
||||
*/
|
||||
static Plan *
|
||||
set_indexonlyscan_references(PlannerInfo *root,
|
||||
IndexOnlyScan *plan,
|
||||
int rtoffset)
|
||||
{
|
||||
indexed_tlist *index_itlist;
|
||||
|
||||
index_itlist = build_tlist_index(plan->indextlist);
|
||||
|
||||
plan->scan.scanrelid += rtoffset;
|
||||
plan->scan.plan.targetlist = (List *)
|
||||
fix_upper_expr(root,
|
||||
(Node *) plan->scan.plan.targetlist,
|
||||
index_itlist,
|
||||
INDEX_VAR,
|
||||
rtoffset);
|
||||
plan->scan.plan.qual = (List *)
|
||||
fix_upper_expr(root,
|
||||
(Node *) plan->scan.plan.qual,
|
||||
index_itlist,
|
||||
INDEX_VAR,
|
||||
rtoffset);
|
||||
/* indexqual is already transformed to reference index columns */
|
||||
plan->indexqual = fix_scan_list(root, plan->indexqual, rtoffset);
|
||||
/* indexorderby is already transformed to reference index columns */
|
||||
plan->indexorderby = fix_scan_list(root, plan->indexorderby, rtoffset);
|
||||
/* indextlist must NOT be transformed to reference index columns */
|
||||
plan->indextlist = fix_scan_list(root, plan->indextlist, rtoffset);
|
||||
|
||||
pfree(index_itlist);
|
||||
|
||||
return (Plan *) plan;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_subqueryscan_references
|
||||
* Do set_plan_references processing on a SubqueryScan
|
||||
@ -919,11 +984,13 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
|
||||
Assert(var->varlevelsup == 0);
|
||||
|
||||
/*
|
||||
* We should not see any Vars marked INNER or OUTER.
|
||||
* We should not see any Vars marked INNER_VAR or OUTER_VAR. But an
|
||||
* indexqual expression could contain INDEX_VAR Vars.
|
||||
*/
|
||||
Assert(var->varno != INNER);
|
||||
Assert(var->varno != OUTER);
|
||||
var->varno += context->rtoffset;
|
||||
Assert(var->varno != INNER_VAR);
|
||||
Assert(var->varno != OUTER_VAR);
|
||||
if (!IS_SPECIAL_VARNO(var->varno))
|
||||
var->varno += context->rtoffset;
|
||||
if (var->varnoold > 0)
|
||||
var->varnoold += context->rtoffset;
|
||||
return (Node *) var;
|
||||
@ -932,9 +999,10 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
|
||||
{
|
||||
CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
|
||||
|
||||
Assert(cexpr->cvarno != INNER);
|
||||
Assert(cexpr->cvarno != OUTER);
|
||||
cexpr->cvarno += context->rtoffset;
|
||||
Assert(cexpr->cvarno != INNER_VAR);
|
||||
Assert(cexpr->cvarno != OUTER_VAR);
|
||||
if (!IS_SPECIAL_VARNO(cexpr->cvarno))
|
||||
cexpr->cvarno += context->rtoffset;
|
||||
return (Node *) cexpr;
|
||||
}
|
||||
if (IsA(node, PlaceHolderVar))
|
||||
@ -963,9 +1031,9 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
|
||||
/*
|
||||
* set_join_references
|
||||
* Modify the target list and quals of a join node to reference its
|
||||
* subplans, by setting the varnos to OUTER or INNER and setting attno
|
||||
* values to the result domain number of either the corresponding outer
|
||||
* or inner join tuple item. Also perform opcode lookup for these
|
||||
* subplans, by setting the varnos to OUTER_VAR or INNER_VAR and setting
|
||||
* attno values to the result domain number of either the corresponding
|
||||
* outer or inner join tuple item. Also perform opcode lookup for these
|
||||
* expressions. and add regclass OIDs to root->glob->relationOids.
|
||||
*/
|
||||
static void
|
||||
@ -1012,6 +1080,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
|
||||
nlp->paramval = (Var *) fix_upper_expr(root,
|
||||
(Node *) nlp->paramval,
|
||||
outer_itlist,
|
||||
OUTER_VAR,
|
||||
rtoffset);
|
||||
}
|
||||
}
|
||||
@ -1083,17 +1152,19 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
|
||||
tle->ressortgroupref,
|
||||
subplan_itlist,
|
||||
OUTER);
|
||||
OUTER_VAR);
|
||||
if (!newexpr)
|
||||
newexpr = fix_upper_expr(root,
|
||||
(Node *) tle->expr,
|
||||
subplan_itlist,
|
||||
OUTER_VAR,
|
||||
rtoffset);
|
||||
}
|
||||
else
|
||||
newexpr = fix_upper_expr(root,
|
||||
(Node *) tle->expr,
|
||||
subplan_itlist,
|
||||
OUTER_VAR,
|
||||
rtoffset);
|
||||
tle = flatCopyTargetEntry(tle);
|
||||
tle->expr = (Expr *) newexpr;
|
||||
@ -1105,6 +1176,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
fix_upper_expr(root,
|
||||
(Node *) plan->qual,
|
||||
subplan_itlist,
|
||||
OUTER_VAR,
|
||||
rtoffset);
|
||||
|
||||
pfree(subplan_itlist);
|
||||
@ -1113,7 +1185,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
/*
|
||||
* set_dummy_tlist_references
|
||||
* Replace the targetlist of an upper-level plan node with a simple
|
||||
* list of OUTER references to its child.
|
||||
* list of OUTER_VAR references to its child.
|
||||
*
|
||||
* This is used for plan types like Sort and Append that don't evaluate
|
||||
* their targetlists. Although the executor doesn't care at all what's in
|
||||
@ -1136,7 +1208,7 @@ set_dummy_tlist_references(Plan *plan, int rtoffset)
|
||||
Var *oldvar = (Var *) tle->expr;
|
||||
Var *newvar;
|
||||
|
||||
newvar = makeVar(OUTER,
|
||||
newvar = makeVar(OUTER_VAR,
|
||||
tle->resno,
|
||||
exprType((Node *) oldvar),
|
||||
exprTypmod((Node *) oldvar),
|
||||
@ -1382,11 +1454,12 @@ search_indexed_tlist_for_sortgroupref(Node *node,
|
||||
* relation target lists. Also perform opcode lookup and add
|
||||
* regclass OIDs to root->glob->relationOids.
|
||||
*
|
||||
* This is used in two different scenarios: a normal join clause, where
|
||||
* all the Vars in the clause *must* be replaced by OUTER or INNER references;
|
||||
* and a RETURNING clause, which may contain both Vars of the target relation
|
||||
* and Vars of other relations. In the latter case we want to replace the
|
||||
* other-relation Vars by OUTER references, while leaving target Vars alone.
|
||||
* This is used in two different scenarios: a normal join clause, where all
|
||||
* the Vars in the clause *must* be replaced by OUTER_VAR or INNER_VAR
|
||||
* references; and a RETURNING clause, which may contain both Vars of the
|
||||
* target relation and Vars of other relations. In the latter case we want
|
||||
* to replace the other-relation Vars by OUTER_VAR references, while leaving
|
||||
* target Vars alone.
|
||||
*
|
||||
* For a normal join, acceptable_rel should be zero so that any failure to
|
||||
* match a Var will be reported as an error. For the RETURNING case, pass
|
||||
@ -1435,7 +1508,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
/* First look for the var in the input tlists */
|
||||
newvar = search_indexed_tlist_for_var(var,
|
||||
context->outer_itlist,
|
||||
OUTER,
|
||||
OUTER_VAR,
|
||||
context->rtoffset);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
@ -1443,7 +1516,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_var(var,
|
||||
context->inner_itlist,
|
||||
INNER,
|
||||
INNER_VAR,
|
||||
context->rtoffset);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
@ -1470,7 +1543,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var((Node *) phv,
|
||||
context->outer_itlist,
|
||||
OUTER);
|
||||
OUTER_VAR);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
}
|
||||
@ -1478,7 +1551,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var((Node *) phv,
|
||||
context->inner_itlist,
|
||||
INNER);
|
||||
INNER_VAR);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
}
|
||||
@ -1491,7 +1564,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var(node,
|
||||
context->outer_itlist,
|
||||
OUTER);
|
||||
OUTER_VAR);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
}
|
||||
@ -1499,7 +1572,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var(node,
|
||||
context->inner_itlist,
|
||||
INNER);
|
||||
INNER_VAR);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
}
|
||||
@ -1516,7 +1589,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
* root->glob->relationOids.
|
||||
*
|
||||
* This is used to fix up target and qual expressions of non-join upper-level
|
||||
* plan nodes.
|
||||
* plan nodes, as well as index-only scan nodes.
|
||||
*
|
||||
* An error is raised if no matching var can be found in the subplan tlist
|
||||
* --- so this routine should only be applied to nodes whose subplans'
|
||||
@ -1529,23 +1602,26 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
* subplan tlist is just a flattened list of Vars.)
|
||||
*
|
||||
* 'node': the tree to be fixed (a target item or qual)
|
||||
* 'subplan_itlist': indexed target list for subplan
|
||||
* 'subplan_itlist': indexed target list for subplan (or index)
|
||||
* 'newvarno': varno to use for Vars referencing tlist elements
|
||||
* 'rtoffset': how much to increment varnoold by
|
||||
*
|
||||
* The resulting tree is a copy of the original in which all Var nodes have
|
||||
* varno = OUTER, varattno = resno of corresponding subplan target.
|
||||
* varno = newvarno, varattno = resno of corresponding targetlist element.
|
||||
* The original tree is not modified.
|
||||
*/
|
||||
static Node *
|
||||
fix_upper_expr(PlannerInfo *root,
|
||||
Node *node,
|
||||
indexed_tlist *subplan_itlist,
|
||||
Index newvarno,
|
||||
int rtoffset)
|
||||
{
|
||||
fix_upper_expr_context context;
|
||||
|
||||
context.root = root;
|
||||
context.subplan_itlist = subplan_itlist;
|
||||
context.newvarno = newvarno;
|
||||
context.rtoffset = rtoffset;
|
||||
return fix_upper_expr_mutator(node, &context);
|
||||
}
|
||||
@ -1563,7 +1639,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
|
||||
|
||||
newvar = search_indexed_tlist_for_var(var,
|
||||
context->subplan_itlist,
|
||||
OUTER,
|
||||
context->newvarno,
|
||||
context->rtoffset);
|
||||
if (!newvar)
|
||||
elog(ERROR, "variable not found in subplan target list");
|
||||
@ -1578,7 +1654,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var((Node *) phv,
|
||||
context->subplan_itlist,
|
||||
OUTER);
|
||||
context->newvarno);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
}
|
||||
@ -1590,7 +1666,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var(node,
|
||||
context->subplan_itlist,
|
||||
OUTER);
|
||||
context->newvarno);
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
}
|
||||
@ -1610,7 +1686,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
|
||||
* table should be left alone, however (the executor will evaluate them
|
||||
* using the actual heap tuple, after firing triggers if any). In the
|
||||
* adjusted RETURNING list, result-table Vars will still have their
|
||||
* original varno, but Vars for other rels will have varno OUTER.
|
||||
* original varno, but Vars for other rels will have varno OUTER_VAR.
|
||||
*
|
||||
* We also must perform opcode lookup and add regclass OIDs to
|
||||
* root->glob->relationOids.
|
||||
|
@ -1974,6 +1974,18 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
|
||||
context.paramids = bms_add_members(context.paramids, scan_params);
|
||||
break;
|
||||
|
||||
case T_IndexOnlyScan:
|
||||
finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexqual,
|
||||
&context);
|
||||
finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexorderby,
|
||||
&context);
|
||||
|
||||
/*
|
||||
* we need not look at indextlist, since it cannot contain Params.
|
||||
*/
|
||||
context.paramids = bms_add_members(context.paramids, scan_params);
|
||||
break;
|
||||
|
||||
case T_BitmapIndexScan:
|
||||
finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual,
|
||||
&context);
|
||||
|
@ -452,7 +452,7 @@ create_index_path(PlannerInfo *root,
|
||||
indexscandir = NoMovementScanDirection;
|
||||
}
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
|
||||
@ -470,7 +470,6 @@ create_index_path(PlannerInfo *root,
|
||||
|
||||
pathnode->isjoininner = (outer_rel != NULL);
|
||||
pathnode->indexscandir = indexscandir;
|
||||
pathnode->indexonly = indexonly;
|
||||
|
||||
if (outer_rel != NULL)
|
||||
{
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "access/sysattr.h"
|
||||
#include "access/transam.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
@ -49,6 +50,8 @@ static int32 get_rel_data_width(Relation rel, int32 *attr_widths);
|
||||
static List *get_relation_constraints(PlannerInfo *root,
|
||||
Oid relationObjectId, RelOptInfo *rel,
|
||||
bool include_notnull);
|
||||
static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
|
||||
Relation heapRelation);
|
||||
|
||||
|
||||
/*
|
||||
@ -314,6 +317,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
|
||||
ChangeVarNodes((Node *) info->indexprs, 1, varno, 0);
|
||||
if (info->indpred && varno != 1)
|
||||
ChangeVarNodes((Node *) info->indpred, 1, varno, 0);
|
||||
|
||||
/* Build targetlist using the completed indexprs data */
|
||||
info->indextlist = build_index_tlist(root, info, relation);
|
||||
|
||||
info->predOK = false; /* set later in indxpath.c */
|
||||
info->unique = index->indisunique;
|
||||
info->hypothetical = false;
|
||||
@ -900,6 +907,70 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
|
||||
return tlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* build_index_tlist
|
||||
*
|
||||
* Build a targetlist representing the columns of the specified index.
|
||||
* Each column is represented by a Var for the corresponding base-relation
|
||||
* column, or an expression in base-relation Vars, as appropriate.
|
||||
*
|
||||
* There are never any dropped columns in indexes, so unlike
|
||||
* build_physical_tlist, we need no failure case.
|
||||
*/
|
||||
static List *
|
||||
build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
|
||||
Relation heapRelation)
|
||||
{
|
||||
List *tlist = NIL;
|
||||
Index varno = index->rel->relid;
|
||||
ListCell *indexpr_item;
|
||||
int i;
|
||||
|
||||
indexpr_item = list_head(index->indexprs);
|
||||
for (i = 0; i < index->ncolumns; i++)
|
||||
{
|
||||
int indexkey = index->indexkeys[i];
|
||||
Expr *indexvar;
|
||||
|
||||
if (indexkey != 0)
|
||||
{
|
||||
/* simple column */
|
||||
Form_pg_attribute att_tup;
|
||||
|
||||
if (indexkey < 0)
|
||||
att_tup = SystemAttributeDefinition(indexkey,
|
||||
heapRelation->rd_rel->relhasoids);
|
||||
else
|
||||
att_tup = heapRelation->rd_att->attrs[indexkey - 1];
|
||||
|
||||
indexvar = (Expr *) makeVar(varno,
|
||||
indexkey,
|
||||
att_tup->atttypid,
|
||||
att_tup->atttypmod,
|
||||
att_tup->attcollation,
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* expression column */
|
||||
if (indexpr_item == NULL)
|
||||
elog(ERROR, "wrong number of index expressions");
|
||||
indexvar = (Expr *) lfirst(indexpr_item);
|
||||
indexpr_item = lnext(indexpr_item);
|
||||
}
|
||||
|
||||
tlist = lappend(tlist,
|
||||
makeTargetEntry(indexvar,
|
||||
i + 1,
|
||||
NULL,
|
||||
false));
|
||||
}
|
||||
if (indexpr_item != NULL)
|
||||
elog(ERROR, "wrong number of index expressions");
|
||||
|
||||
return tlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* restriction_selectivity
|
||||
*
|
||||
|
Reference in New Issue
Block a user