Commit fa559a86 authored by Tom Lane's avatar Tom Lane

Adjust indexscan planning logic to keep RestrictInfo nodes associated

with index qual clauses in the Path representation.  This saves a little
work during createplan and (probably more importantly) allows reuse of
cached selectivity estimates during indexscan planning.  Also fix latent
bug: wrong plan would have been generated for a 'special operator' used
in a nestloop-inner-indexscan join qual, because the special operator
would not have gotten into the list of quals to recheck.  This bug is
only latent because at present the special-operator code could never
trigger on a join qual, but sooner or later someone will want to do it.
parent 5d472f64
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.227 2004/01/05 18:04:38 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.228 2004/01/05 23:39:53 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -968,8 +968,9 @@ _outIndexPath(StringInfo str, IndexPath *node) ...@@ -968,8 +968,9 @@ _outIndexPath(StringInfo str, IndexPath *node)
_outPathInfo(str, (Path *) node); _outPathInfo(str, (Path *) node);
WRITE_NODE_FIELD(indexinfo); WRITE_NODE_FIELD(indexinfo);
WRITE_NODE_FIELD(indexqual); WRITE_NODE_FIELD(indexclauses);
WRITE_NODE_FIELD(indexjoinclauses); WRITE_NODE_FIELD(indexquals);
WRITE_BOOL_FIELD(isjoininner);
WRITE_ENUM_FIELD(indexscandir, ScanDirection); WRITE_ENUM_FIELD(indexscandir, ScanDirection);
WRITE_FLOAT_FIELD(rows, "%.0f"); WRITE_FLOAT_FIELD(rows, "%.0f");
} }
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.120 2004/01/05 05:07:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.121 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -238,6 +238,9 @@ cost_nonsequential_access(double relpages) ...@@ -238,6 +238,9 @@ cost_nonsequential_access(double relpages)
* Any additional quals evaluated as qpquals may reduce the number of returned * Any additional quals evaluated as qpquals may reduce the number of returned
* tuples, but they won't reduce the number of tuples we have to fetch from * tuples, but they won't reduce the number of tuples we have to fetch from
* the table, so they don't reduce the scan cost. * the table, so they don't reduce the scan cost.
*
* NOTE: as of 7.5, indexQuals is a list of RestrictInfo nodes, where formerly
* it was a list of bare clause expressions.
*/ */
void void
cost_index(Path *path, Query *root, cost_index(Path *path, Query *root,
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.154 2004/01/05 05:07:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.155 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -76,7 +76,7 @@ static bool match_index_to_operand(Node *operand, int indexcol, ...@@ -76,7 +76,7 @@ static bool match_index_to_operand(Node *operand, int indexcol,
RelOptInfo *rel, IndexOptInfo *index); RelOptInfo *rel, IndexOptInfo *index);
static bool match_special_index_operator(Expr *clause, Oid opclass, static bool match_special_index_operator(Expr *clause, Oid opclass,
bool indexkey_on_left); bool indexkey_on_left);
static List *expand_indexqual_condition(Expr *clause, Oid opclass); static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass);
static List *prefix_quals(Node *leftop, Oid opclass, static List *prefix_quals(Node *leftop, Oid opclass,
Const *prefix, Pattern_Prefix_Status pstatus); Const *prefix, Pattern_Prefix_Status pstatus);
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,
...@@ -1418,8 +1418,7 @@ make_innerjoin_index_path(Query *root, ...@@ -1418,8 +1418,7 @@ make_innerjoin_index_path(Query *root,
{ {
IndexPath *pathnode = makeNode(IndexPath); IndexPath *pathnode = makeNode(IndexPath);
List *indexquals, List *indexquals,
*allclauses, *allclauses;
*l;
/* XXX perhaps this code should be merged with create_index_path? */ /* XXX perhaps this code should be merged with create_index_path? */
...@@ -1433,28 +1432,21 @@ make_innerjoin_index_path(Query *root, ...@@ -1433,28 +1432,21 @@ make_innerjoin_index_path(Query *root,
*/ */
pathnode->path.pathkeys = NIL; pathnode->path.pathkeys = NIL;
/* Convert RestrictInfo nodes to indexquals the executor can handle */ /* Convert clauses to indexquals the executor can handle */
indexquals = expand_indexqual_conditions(index, clausegroups); indexquals = expand_indexqual_conditions(index, clausegroups);
/* /* Flatten the clausegroups list to produce indexclauses list */
* Also make a flattened list of the RestrictInfo nodes; createplan.c allclauses = flatten_clausegroups_list(clausegroups);
* will need this later. We assume here that we can destructively
* modify the passed-in clausegroups list structure.
*/
allclauses = NIL;
foreach(l, clausegroups)
{
/* nconc okay here since same clause couldn't be in two sublists */
allclauses = nconc(allclauses, (List *) lfirst(l));
}
/* /*
* Note that we are making a pathnode for a single-scan indexscan; * Note that we are making a pathnode for a single-scan indexscan;
* therefore, indexinfo and indexqual should be single-element lists. * therefore, indexinfo etc should be single-element lists.
*/ */
pathnode->indexinfo = makeList1(index); pathnode->indexinfo = makeList1(index);
pathnode->indexqual = makeList1(indexquals); pathnode->indexclauses = makeList1(allclauses);
pathnode->indexjoinclauses = makeList1(allclauses); pathnode->indexquals = makeList1(indexquals);
pathnode->isjoininner = true;
/* We don't actually care what order the index scans in ... */ /* We don't actually care what order the index scans in ... */
pathnode->indexscandir = NoMovementScanDirection; pathnode->indexscandir = NoMovementScanDirection;
...@@ -1489,6 +1481,61 @@ make_innerjoin_index_path(Query *root, ...@@ -1489,6 +1481,61 @@ make_innerjoin_index_path(Query *root,
return (Path *) pathnode; return (Path *) pathnode;
} }
/*
* flatten_clausegroups_list
* Given a list of lists of RestrictInfos, flatten it to a list
* of RestrictInfos.
*
* This is used to flatten out the result of group_clauses_by_indexkey()
* or one of its sibling routines, to produce an indexclauses list.
*/
List *
flatten_clausegroups_list(List *clausegroups)
{
List *allclauses = NIL;
List *l;
foreach(l, clausegroups)
{
allclauses = nconc(allclauses, listCopy((List *) lfirst(l)));
}
return allclauses;
}
/*
* make_expr_from_indexclauses()
* Given an indexclauses structure, produce an ordinary boolean expression.
*
* This consists of stripping out the RestrictInfo nodes and inserting
* explicit AND and OR nodes as needed. There's not much to it, but
* the functionality is needed in a few places, so centralize the logic.
*/
Expr *
make_expr_from_indexclauses(List *indexclauses)
{
List *orclauses = NIL;
List *orlist;
/* There's no such thing as an indexpath with zero scans */
Assert(indexclauses != NIL);
foreach(orlist, indexclauses)
{
List *andlist = (List *) lfirst(orlist);
/* Strip RestrictInfos */
andlist = get_actual_clauses(andlist);
/* Insert AND node if needed, and add to orclauses list */
orclauses = lappend(orclauses, make_ands_explicit(andlist));
}
if (length(orclauses) > 1)
return make_orclause(orclauses);
else
return (Expr *) lfirst(orclauses);
}
/**************************************************************************** /****************************************************************************
* ---- ROUTINES TO CHECK OPERANDS ---- * ---- ROUTINES TO CHECK OPERANDS ----
****************************************************************************/ ****************************************************************************/
...@@ -1799,8 +1846,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) ...@@ -1799,8 +1846,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i); RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
FastConc(&resultquals, FastConc(&resultquals,
expand_indexqual_condition(rinfo->clause, expand_indexqual_condition(rinfo, curClass));
curClass));
} }
clausegroups = lnext(clausegroups); clausegroups = lnext(clausegroups);
...@@ -1816,10 +1862,13 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) ...@@ -1816,10 +1862,13 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
/* /*
* expand_indexqual_condition --- expand a single indexqual condition * expand_indexqual_condition --- expand a single indexqual condition
*
* The input is a single RestrictInfo, the output a list of RestrictInfos
*/ */
static List * static List *
expand_indexqual_condition(Expr *clause, Oid opclass) expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
{ {
Expr *clause = rinfo->clause;
/* we know these will succeed */ /* we know these will succeed */
Node *leftop = get_leftop(clause); Node *leftop = get_leftop(clause);
Node *rightop = get_rightop(clause); Node *rightop = get_rightop(clause);
...@@ -1883,7 +1932,7 @@ expand_indexqual_condition(Expr *clause, Oid opclass) ...@@ -1883,7 +1932,7 @@ expand_indexqual_condition(Expr *clause, Oid opclass)
break; break;
default: default:
result = makeList1(clause); result = makeList1(rinfo);
break; break;
} }
...@@ -1978,7 +2027,7 @@ prefix_quals(Node *leftop, Oid opclass, ...@@ -1978,7 +2027,7 @@ prefix_quals(Node *leftop, Oid opclass,
elog(ERROR, "no = operator for opclass %u", opclass); elog(ERROR, "no = operator for opclass %u", opclass);
expr = make_opclause(oproid, BOOLOID, false, expr = make_opclause(oproid, BOOLOID, false,
(Expr *) leftop, (Expr *) prefix_const); (Expr *) leftop, (Expr *) prefix_const);
result = makeList1(expr); result = makeList1(make_restrictinfo(expr, true, true));
return result; return result;
} }
...@@ -1993,7 +2042,7 @@ prefix_quals(Node *leftop, Oid opclass, ...@@ -1993,7 +2042,7 @@ prefix_quals(Node *leftop, Oid opclass,
elog(ERROR, "no >= operator for opclass %u", opclass); elog(ERROR, "no >= operator for opclass %u", opclass);
expr = make_opclause(oproid, BOOLOID, false, expr = make_opclause(oproid, BOOLOID, false,
(Expr *) leftop, (Expr *) prefix_const); (Expr *) leftop, (Expr *) prefix_const);
result = makeList1(expr); result = makeList1(make_restrictinfo(expr, true, true));
/*------- /*-------
* If we can create a string larger than the prefix, we can say * If we can create a string larger than the prefix, we can say
...@@ -2009,7 +2058,7 @@ prefix_quals(Node *leftop, Oid opclass, ...@@ -2009,7 +2058,7 @@ prefix_quals(Node *leftop, Oid opclass,
elog(ERROR, "no < operator for opclass %u", opclass); elog(ERROR, "no < operator for opclass %u", opclass);
expr = make_opclause(oproid, BOOLOID, false, expr = make_opclause(oproid, BOOLOID, false,
(Expr *) leftop, (Expr *) greaterstr); (Expr *) leftop, (Expr *) greaterstr);
result = lappend(result, expr); result = lappend(result, make_restrictinfo(expr, true, true));
} }
return result; return result;
...@@ -2080,7 +2129,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop) ...@@ -2080,7 +2129,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
(Expr *) leftop, (Expr *) leftop,
(Expr *) makeConst(datatype, -1, opr1right, (Expr *) makeConst(datatype, -1, opr1right,
false, false)); false, false));
result = makeList1(expr); result = makeList1(make_restrictinfo(expr, true, true));
/* create clause "key <= network_scan_last( rightop )" */ /* create clause "key <= network_scan_last( rightop )" */
...@@ -2095,7 +2144,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop) ...@@ -2095,7 +2144,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
(Expr *) leftop, (Expr *) leftop,
(Expr *) makeConst(datatype, -1, opr2right, (Expr *) makeConst(datatype, -1, opr2right,
false, false)); false, false));
result = lappend(result, expr); result = lappend(result, make_restrictinfo(expr, true, true));
return result; return result;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.56 2004/01/05 05:07:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.57 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -28,7 +28,8 @@ static bool best_or_subclause_index(Query *root, ...@@ -28,7 +28,8 @@ static bool best_or_subclause_index(Query *root,
RelOptInfo *rel, RelOptInfo *rel,
Expr *subclause, Expr *subclause,
IndexOptInfo **retIndexInfo, IndexOptInfo **retIndexInfo,
List **retIndexQual, List **retIndexClauses,
List **retIndexQuals,
Cost *retStartupCost, Cost *retStartupCost,
Cost *retTotalCost); Cost *retTotalCost);
...@@ -95,9 +96,7 @@ create_or_index_quals(Query *root, RelOptInfo *rel) ...@@ -95,9 +96,7 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
{ {
IndexPath *bestpath = NULL; IndexPath *bestpath = NULL;
RestrictInfo *bestrinfo = NULL; RestrictInfo *bestrinfo = NULL;
FastList orclauses; List *newrinfos;
List *orclause;
Expr *indxqual_or_expr;
RestrictInfo *or_rinfo; RestrictInfo *or_rinfo;
Selectivity or_selec, Selectivity or_selec,
orig_selec; orig_selec;
...@@ -145,20 +144,14 @@ create_or_index_quals(Query *root, RelOptInfo *rel) ...@@ -145,20 +144,14 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
return false; return false;
/* /*
* Build an expression representation of the indexqual, expanding * Convert the indexclauses structure to a RestrictInfo tree,
* the implicit OR and AND semantics of the first- and * and add it to the rel's restriction list.
* second-level lists.
*/ */
FastListInit(&orclauses); newrinfos = make_restrictinfo_from_indexclauses(bestpath->indexclauses,
foreach(orclause, bestpath->indexqual) true, true);
FastAppend(&orclauses, make_ands_explicit(lfirst(orclause))); Assert(length(newrinfos) == 1);
indxqual_or_expr = make_orclause(FastListValue(&orclauses)); or_rinfo = (RestrictInfo *) lfirst(newrinfos);
rel->baserestrictinfo = nconc(rel->baserestrictinfo, newrinfos);
/*
* And add it to the rel's restriction list.
*/
or_rinfo = make_restrictinfo(indxqual_or_expr, true, true);
rel->baserestrictinfo = lappend(rel->baserestrictinfo, or_rinfo);
/* /*
* Adjust the original OR clause's cached selectivity to compensate * Adjust the original OR clause's cached selectivity to compensate
...@@ -251,6 +244,7 @@ best_or_subclause_indexes(Query *root, ...@@ -251,6 +244,7 @@ best_or_subclause_indexes(Query *root,
List *subclauses) List *subclauses)
{ {
FastList infos; FastList infos;
FastList clauses;
FastList quals; FastList quals;
Cost path_startup_cost; Cost path_startup_cost;
Cost path_total_cost; Cost path_total_cost;
...@@ -258,6 +252,7 @@ best_or_subclause_indexes(Query *root, ...@@ -258,6 +252,7 @@ best_or_subclause_indexes(Query *root,
IndexPath *pathnode; IndexPath *pathnode;
FastListInit(&infos); FastListInit(&infos);
FastListInit(&clauses);
FastListInit(&quals); FastListInit(&quals);
path_startup_cost = 0; path_startup_cost = 0;
path_total_cost = 0; path_total_cost = 0;
...@@ -267,17 +262,20 @@ best_or_subclause_indexes(Query *root, ...@@ -267,17 +262,20 @@ best_or_subclause_indexes(Query *root,
{ {
Expr *subclause = lfirst(slist); Expr *subclause = lfirst(slist);
IndexOptInfo *best_indexinfo; IndexOptInfo *best_indexinfo;
List *best_indexqual; List *best_indexclauses;
List *best_indexquals;
Cost best_startup_cost; Cost best_startup_cost;
Cost best_total_cost; Cost best_total_cost;
if (!best_or_subclause_index(root, rel, subclause, if (!best_or_subclause_index(root, rel, subclause,
&best_indexinfo, &best_indexqual, &best_indexinfo,
&best_indexclauses, &best_indexquals,
&best_startup_cost, &best_total_cost)) &best_startup_cost, &best_total_cost))
return NULL; /* failed to match this subclause */ return NULL; /* failed to match this subclause */
FastAppend(&infos, best_indexinfo); FastAppend(&infos, best_indexinfo);
FastAppend(&quals, best_indexqual); FastAppend(&clauses, best_indexclauses);
FastAppend(&quals, best_indexquals);
/* /*
* Path startup_cost is the startup cost for the first index scan only; * Path startup_cost is the startup cost for the first index scan only;
* startup costs for later scans will be paid later on, so they just * startup costs for later scans will be paid later on, so they just
...@@ -306,10 +304,11 @@ best_or_subclause_indexes(Query *root, ...@@ -306,10 +304,11 @@ best_or_subclause_indexes(Query *root,
pathnode->path.pathkeys = NIL; pathnode->path.pathkeys = NIL;
pathnode->indexinfo = FastListValue(&infos); pathnode->indexinfo = FastListValue(&infos);
pathnode->indexqual = FastListValue(&quals); pathnode->indexclauses = FastListValue(&clauses);
pathnode->indexquals = FastListValue(&quals);
/* It's not an innerjoin path. */ /* It's not an innerjoin path. */
pathnode->indexjoinclauses = NIL; pathnode->isjoininner = false;
/* We don't actually care what order the index scans in. */ /* We don't actually care what order the index scans in. */
pathnode->indexscandir = NoMovementScanDirection; pathnode->indexscandir = NoMovementScanDirection;
...@@ -336,7 +335,8 @@ best_or_subclause_indexes(Query *root, ...@@ -336,7 +335,8 @@ best_or_subclause_indexes(Query *root,
* 'subclause' is the OR subclause being considered * 'subclause' is the OR subclause being considered
* *
* '*retIndexInfo' gets the IndexOptInfo of the best index * '*retIndexInfo' gets the IndexOptInfo of the best index
* '*retIndexQual' gets a list of the indexqual conditions for the best index * '*retIndexClauses' gets a list of the index clauses for the best index
* '*retIndexQuals' gets a list of the expanded indexquals for the best index
* '*retStartupCost' gets the startup cost of a scan with that index * '*retStartupCost' gets the startup cost of a scan with that index
* '*retTotalCost' gets the total cost of a scan with that index * '*retTotalCost' gets the total cost of a scan with that index
*/ */
...@@ -345,7 +345,8 @@ best_or_subclause_index(Query *root, ...@@ -345,7 +345,8 @@ best_or_subclause_index(Query *root,
RelOptInfo *rel, RelOptInfo *rel,
Expr *subclause, Expr *subclause,
IndexOptInfo **retIndexInfo, /* return value */ IndexOptInfo **retIndexInfo, /* return value */
List **retIndexQual, /* return value */ List **retIndexClauses, /* return value */
List **retIndexQuals, /* return value */
Cost *retStartupCost, /* return value */ Cost *retStartupCost, /* return value */
Cost *retTotalCost) /* return value */ Cost *retTotalCost) /* return value */
{ {
...@@ -355,7 +356,7 @@ best_or_subclause_index(Query *root, ...@@ -355,7 +356,7 @@ best_or_subclause_index(Query *root,
foreach(ilist, rel->indexlist) foreach(ilist, rel->indexlist)
{ {
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
List *qualrinfos; List *indexclauses;
List *indexquals; List *indexquals;
Path subclause_path; Path subclause_path;
...@@ -364,21 +365,22 @@ best_or_subclause_index(Query *root, ...@@ -364,21 +365,22 @@ best_or_subclause_index(Query *root,
continue; continue;
/* Collect index clauses usable with this index */ /* Collect index clauses usable with this index */
qualrinfos = group_clauses_by_indexkey_for_or(rel, index, subclause); indexclauses = group_clauses_by_indexkey_for_or(rel, index, subclause);
/* Ignore index if it doesn't match the subclause at all */ /* Ignore index if it doesn't match the subclause at all */
if (qualrinfos == NIL) if (indexclauses == NIL)
continue; continue;
/* Convert RestrictInfo nodes to indexquals the executor can handle */ /* Convert clauses to indexquals the executor can handle */
indexquals = expand_indexqual_conditions(index, qualrinfos); indexquals = expand_indexqual_conditions(index, indexclauses);
cost_index(&subclause_path, root, rel, index, indexquals, false); cost_index(&subclause_path, root, rel, index, indexquals, false);
if (!found || subclause_path.total_cost < *retTotalCost) if (!found || subclause_path.total_cost < *retTotalCost)
{ {
*retIndexInfo = index; *retIndexInfo = index;
*retIndexQual = indexquals; *retIndexClauses = flatten_clausegroups_list(indexclauses);
*retIndexQuals = indexquals;
*retStartupCost = subclause_path.startup_cost; *retStartupCost = subclause_path.startup_cost;
*retTotalCost = subclause_path.total_cost; *retTotalCost = subclause_path.total_cost;
found = true; found = true;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.163 2004/01/05 18:04:38 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.164 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -44,15 +44,15 @@ static Append *create_append_plan(Query *root, AppendPath *best_path); ...@@ -44,15 +44,15 @@ static Append *create_append_plan(Query *root, AppendPath *best_path);
static Result *create_result_plan(Query *root, ResultPath *best_path); static Result *create_result_plan(Query *root, ResultPath *best_path);
static Material *create_material_plan(Query *root, MaterialPath *best_path); static Material *create_material_plan(Query *root, MaterialPath *best_path);
static Plan *create_unique_plan(Query *root, UniquePath *best_path); static Plan *create_unique_plan(Query *root, UniquePath *best_path);
static SeqScan *create_seqscan_plan(Path *best_path, List *tlist, static SeqScan *create_seqscan_plan(Query *root, Path *best_path,
List *scan_clauses); List *tlist, List *scan_clauses);
static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path, static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist, static TidScan *create_tidscan_plan(Query *root, TidPath *best_path,
List *scan_clauses); List *tlist, List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(Path *best_path, static SubqueryScan *create_subqueryscan_plan(Query *root, Path *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static FunctionScan *create_functionscan_plan(Path *best_path, static FunctionScan *create_functionscan_plan(Query *root, Path *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(Query *root, NestPath *best_path, static NestLoop *create_nestloop_plan(Query *root, NestPath *best_path,
Plan *outer_plan, Plan *inner_plan); Plan *outer_plan, Plan *inner_plan);
...@@ -219,15 +219,13 @@ create_scan_plan(Query *root, Path *best_path) ...@@ -219,15 +219,13 @@ create_scan_plan(Query *root, Path *best_path)
* Extract the relevant restriction clauses from the parent relation; * Extract the relevant restriction clauses from the parent relation;
* the executor must apply all these restrictions during the scan. * the executor must apply all these restrictions during the scan.
*/ */
scan_clauses = get_actual_clauses(rel->baserestrictinfo); scan_clauses = rel->baserestrictinfo;
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
switch (best_path->pathtype) switch (best_path->pathtype)
{ {
case T_SeqScan: case T_SeqScan:
plan = (Scan *) create_seqscan_plan(best_path, plan = (Scan *) create_seqscan_plan(root,
best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
...@@ -240,19 +238,22 @@ create_scan_plan(Query *root, Path *best_path) ...@@ -240,19 +238,22 @@ create_scan_plan(Query *root, Path *best_path)
break; break;
case T_TidScan: case T_TidScan:
plan = (Scan *) create_tidscan_plan((TidPath *) best_path, plan = (Scan *) create_tidscan_plan(root,
(TidPath *) best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
case T_SubqueryScan: case T_SubqueryScan:
plan = (Scan *) create_subqueryscan_plan(best_path, plan = (Scan *) create_subqueryscan_plan(root,
best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
case T_FunctionScan: case T_FunctionScan:
plan = (Scan *) create_functionscan_plan(best_path, plan = (Scan *) create_functionscan_plan(root,
best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
...@@ -667,7 +668,8 @@ create_unique_plan(Query *root, UniquePath *best_path) ...@@ -667,7 +668,8 @@ create_unique_plan(Query *root, UniquePath *best_path)
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static SeqScan * static SeqScan *
create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses) create_seqscan_plan(Query *root, Path *best_path,
List *tlist, List *scan_clauses)
{ {
SeqScan *scan_plan; SeqScan *scan_plan;
Index scan_relid = best_path->parent->relid; Index scan_relid = best_path->parent->relid;
...@@ -676,6 +678,12 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses) ...@@ -676,6 +678,12 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
Assert(scan_relid > 0); Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_RELATION); Assert(best_path->parent->rtekind == RTE_RELATION);
/* Reduce RestrictInfo list to bare expressions */
scan_clauses = get_actual_clauses(scan_clauses);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
scan_plan = make_seqscan(tlist, scan_plan = make_seqscan(tlist,
scan_clauses, scan_clauses,
scan_relid); scan_relid);
...@@ -690,9 +698,9 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses) ...@@ -690,9 +698,9 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
* Returns a indexscan plan for the base relation scanned by 'best_path' * Returns a indexscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
* *
* The indexqual of the path contains a sublist of implicitly-ANDed qual * The indexquals list of the path contains a sublist of implicitly-ANDed
* conditions for each scan of the index(es); if there is more than one * qual conditions for each scan of the index(es); if there is more than one
* scan then the retrieved tuple sets are ORed together. The indexqual * scan then the retrieved tuple sets are ORed together. The indexquals
* and indexinfo lists must have the same length, ie, the number of scans * and indexinfo lists must have the same length, ie, the number of scans
* that will occur. Note it is possible for a qual condition sublist * that will occur. Note it is possible for a qual condition sublist
* to be empty --- then no index restrictions will be applied during that * to be empty --- then no index restrictions will be applied during that
...@@ -704,16 +712,17 @@ create_indexscan_plan(Query *root, ...@@ -704,16 +712,17 @@ create_indexscan_plan(Query *root,
List *tlist, List *tlist,
List *scan_clauses) List *scan_clauses)
{ {
List *indxqual = best_path->indexqual; List *indxquals = best_path->indexquals;
Index baserelid = best_path->path.parent->relid; Index baserelid = best_path->path.parent->relid;
List *qpqual; List *qpqual;
Expr *indxqual_or_expr = NULL; Expr *indxqual_or_expr = NULL;
List *fixed_indxqual; List *stripped_indxquals;
List *recheck_indxqual; List *fixed_indxquals;
List *recheck_indxquals;
List *indxstrategy; List *indxstrategy;
List *indxsubtype; List *indxsubtype;
FastList indexids; FastList indexids;
List *ixinfo; List *i;
IndexScan *scan_plan; IndexScan *scan_plan;
/* it should be a base rel... */ /* it should be a base rel... */
...@@ -721,64 +730,94 @@ create_indexscan_plan(Query *root, ...@@ -721,64 +730,94 @@ create_indexscan_plan(Query *root,
Assert(best_path->path.parent->rtekind == RTE_RELATION); Assert(best_path->path.parent->rtekind == RTE_RELATION);
/* /*
* Build list of index OIDs. * If this is a innerjoin scan, the indexclauses will contain join
* clauses that are not present in scan_clauses (since the passed-in
* value is just the rel's baserestrictinfo list). We must add these
* clauses to scan_clauses to ensure they get checked. In most cases
* we will remove the join clauses again below, but if a join clause
* contains a lossy or special operator, we need to make sure it gets
* into scan_clauses.
*/ */
if (best_path->isjoininner)
{
/*
* We don't currently support OR indexscans in joins, so we only
* need to worry about the plain AND case. Also, pointer comparison
* should be enough to determine RestrictInfo matches.
*/
Assert(length(best_path->indexclauses) == 1);
scan_clauses = set_ptrUnion(scan_clauses,
(List *) lfirst(best_path->indexclauses));
}
/* Reduce RestrictInfo list to bare expressions */
scan_clauses = get_actual_clauses(scan_clauses);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Build list of index OIDs */
FastListInit(&indexids); FastListInit(&indexids);
foreach(ixinfo, best_path->indexinfo) foreach(i, best_path->indexinfo)
{ {
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo); IndexOptInfo *index = (IndexOptInfo *) lfirst(i);
FastAppendo(&indexids, index->indexoid); FastAppendo(&indexids, index->indexoid);
} }
/*
* Build "stripped" indexquals structure (no RestrictInfos) to pass to
* executor as indxqualorig
*/
stripped_indxquals = NIL;
foreach(i, indxquals)
{
List *andlist = (List *) lfirst(i);
stripped_indxquals = lappend(stripped_indxquals,
get_actual_clauses(andlist));
}
/* /*
* The qpqual list must contain all restrictions not automatically * The qpqual list must contain all restrictions not automatically
* handled by the index. Normally the predicates in the indxqual are * handled by the index. Normally the predicates in the indexquals are
* checked fully by the index, but if the index is "lossy" for a * checked fully by the index, but if the index is "lossy" for a
* particular operator (as signaled by the amopreqcheck flag in * particular operator (as signaled by the amopreqcheck flag in
* pg_amop), then we need to double-check that predicate in qpqual, * pg_amop), then we need to double-check that predicate in qpqual,
* because the index may return more tuples than match the predicate. * because the index may return more tuples than match the predicate.
* *
* Since the indexquals were generated from the restriction clauses given * Since the indexquals were generated from the restriction clauses given
* by scan_clauses, there will normally be some duplications between * by scan_clauses, there will normally be duplications between the lists.
* the lists. We get rid of the duplicates, then add back if lossy. * We get rid of the duplicates, then add back if lossy.
*/ */
if (length(indxqual) > 1) if (length(indxquals) > 1)
{ {
/* /*
* Build an expression representation of the indexqual, expanding * Build an expression representation of the indexqual, expanding
* the implicit OR and AND semantics of the first- and * the implicit OR and AND semantics of the first- and
* second-level lists. * second-level lists.
*/ */
FastList orclauses; indxqual_or_expr = make_expr_from_indexclauses(indxquals);
List *orclause;
FastListInit(&orclauses);
foreach(orclause, indxqual)
FastAppend(&orclauses, make_ands_explicit(lfirst(orclause)));
indxqual_or_expr = make_orclause(FastListValue(&orclauses));
qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr)); qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr));
} }
else if (indxqual != NIL) else
{ {
/* /*
* Here, we can simply treat the first sublist as an independent * Here, we can simply treat the first sublist as an independent
* set of qual expressions, since there is no top-level OR * set of qual expressions, since there is no top-level OR
* behavior. * behavior.
*/ */
qpqual = set_difference(scan_clauses, lfirst(indxqual)); Assert(stripped_indxquals != NIL);
qpqual = set_difference(scan_clauses, lfirst(stripped_indxquals));
} }
else
qpqual = scan_clauses;
/* /*
* The executor needs a copy with the indexkey on the left of each * The executor needs a copy with the indexkey on the left of each
* clause and with index attr numbers substituted for table ones. This * clause and with index attr numbers substituted for table ones. This
* pass also looks for "lossy" operators. * pass also looks for "lossy" operators.
*/ */
fix_indxqual_references(indxqual, best_path, fix_indxqual_references(indxquals, best_path,
&fixed_indxqual, &recheck_indxqual, &fixed_indxquals, &recheck_indxquals,
&indxstrategy, &indxsubtype); &indxstrategy, &indxsubtype);
/* /*
...@@ -786,10 +825,10 @@ create_indexscan_plan(Query *root, ...@@ -786,10 +825,10 @@ create_indexscan_plan(Query *root,
* appropriate qual clauses to the qpqual. When there is just one * appropriate qual clauses to the qpqual. When there is just one
* indexscan being performed (ie, we have simple AND semantics), we * indexscan being performed (ie, we have simple AND semantics), we
* can just add the lossy clauses themselves to qpqual. If we have * can just add the lossy clauses themselves to qpqual. If we have
* OR-of-ANDs, we'd better add the entire original indexqual to make * OR-of-ANDs, we'd better add the entire original indexquals to make
* sure that the semantics are correct. * sure that the semantics are correct.
*/ */
if (recheck_indxqual != NIL) if (recheck_indxquals != NIL)
{ {
if (indxqual_or_expr) if (indxqual_or_expr)
{ {
...@@ -799,8 +838,8 @@ create_indexscan_plan(Query *root, ...@@ -799,8 +838,8 @@ create_indexscan_plan(Query *root,
else else
{ {
/* Subroutine already copied quals, so just append to list */ /* Subroutine already copied quals, so just append to list */
Assert(length(recheck_indxqual) == 1); Assert(length(recheck_indxquals) == 1);
qpqual = nconc(qpqual, (List *) lfirst(recheck_indxqual)); qpqual = nconc(qpqual, (List *) lfirst(recheck_indxquals));
} }
} }
...@@ -809,8 +848,8 @@ create_indexscan_plan(Query *root, ...@@ -809,8 +848,8 @@ create_indexscan_plan(Query *root,
qpqual, qpqual,
baserelid, baserelid,
FastListValue(&indexids), FastListValue(&indexids),
fixed_indxqual, fixed_indxquals,
indxqual, stripped_indxquals,
indxstrategy, indxstrategy,
indxsubtype, indxsubtype,
best_path->indexscandir); best_path->indexscandir);
...@@ -828,7 +867,8 @@ create_indexscan_plan(Query *root, ...@@ -828,7 +867,8 @@ create_indexscan_plan(Query *root,
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static TidScan * static TidScan *
create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses) create_tidscan_plan(Query *root, TidPath *best_path,
List *tlist, List *scan_clauses)
{ {
TidScan *scan_plan; TidScan *scan_plan;
Index scan_relid = best_path->path.parent->relid; Index scan_relid = best_path->path.parent->relid;
...@@ -837,6 +877,12 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses) ...@@ -837,6 +877,12 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
Assert(scan_relid > 0); Assert(scan_relid > 0);
Assert(best_path->path.parent->rtekind == RTE_RELATION); Assert(best_path->path.parent->rtekind == RTE_RELATION);
/* Reduce RestrictInfo list to bare expressions */
scan_clauses = get_actual_clauses(scan_clauses);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
scan_plan = make_tidscan(tlist, scan_plan = make_tidscan(tlist,
scan_clauses, scan_clauses,
scan_relid, scan_relid,
...@@ -853,7 +899,8 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses) ...@@ -853,7 +899,8 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static SubqueryScan * static SubqueryScan *
create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses) create_subqueryscan_plan(Query *root, Path *best_path,
List *tlist, List *scan_clauses)
{ {
SubqueryScan *scan_plan; SubqueryScan *scan_plan;
Index scan_relid = best_path->parent->relid; Index scan_relid = best_path->parent->relid;
...@@ -862,6 +909,12 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses) ...@@ -862,6 +909,12 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
Assert(scan_relid > 0); Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_SUBQUERY); Assert(best_path->parent->rtekind == RTE_SUBQUERY);
/* Reduce RestrictInfo list to bare expressions */
scan_clauses = get_actual_clauses(scan_clauses);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
scan_plan = make_subqueryscan(tlist, scan_plan = make_subqueryscan(tlist,
scan_clauses, scan_clauses,
scan_relid, scan_relid,
...@@ -878,7 +931,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses) ...@@ -878,7 +931,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static FunctionScan * static FunctionScan *
create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses) create_functionscan_plan(Query *root, Path *best_path,
List *tlist, List *scan_clauses)
{ {
FunctionScan *scan_plan; FunctionScan *scan_plan;
Index scan_relid = best_path->parent->relid; Index scan_relid = best_path->parent->relid;
...@@ -887,6 +941,12 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses) ...@@ -887,6 +941,12 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
Assert(scan_relid > 0); Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_FUNCTION); Assert(best_path->parent->rtekind == RTE_FUNCTION);
/* Reduce RestrictInfo list to bare expressions */
scan_clauses = get_actual_clauses(scan_clauses);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid); scan_plan = make_functionscan(tlist, scan_clauses, scan_relid);
copy_path_costsize(&scan_plan->scan.plan, best_path); copy_path_costsize(&scan_plan->scan.plan, best_path);
...@@ -928,20 +988,19 @@ create_nestloop_plan(Query *root, ...@@ -928,20 +988,19 @@ create_nestloop_plan(Query *root,
* have caught this case because the join clauses would never have * have caught this case because the join clauses would never have
* been put in the same joininfo list. * been put in the same joininfo list.
* *
* This would be a waste of time if the indexpath was an ordinary * We can skip this if the index path is an ordinary indexpath and
* indexpath and not a special innerjoin path. We will skip it in * not a special innerjoin path.
* that case since indexjoinclauses is NIL in an ordinary
* indexpath.
*/ */
IndexPath *innerpath = (IndexPath *) best_path->innerjoinpath; IndexPath *innerpath = (IndexPath *) best_path->innerjoinpath;
List *indexjoinclauses = innerpath->indexjoinclauses; List *indexclauses = innerpath->indexclauses;
if (length(indexjoinclauses) == 1) /* single indexscan? */ if (innerpath->isjoininner &&
length(indexclauses) == 1) /* single indexscan? */
{ {
joinrestrictclauses = joinrestrictclauses =
select_nonredundant_join_clauses(root, select_nonredundant_join_clauses(root,
joinrestrictclauses, joinrestrictclauses,
lfirst(indexjoinclauses), lfirst(indexclauses),
best_path->jointype); best_path->jointype);
} }
} }
...@@ -1138,7 +1197,8 @@ create_hashjoin_plan(Query *root, ...@@ -1138,7 +1197,8 @@ create_hashjoin_plan(Query *root,
* Adjust indexqual clauses to the form the executor's indexqual * Adjust indexqual clauses to the form the executor's indexqual
* machinery needs, and check for recheckable (lossy) index conditions. * machinery needs, and check for recheckable (lossy) index conditions.
* *
* We have four tasks here: * We have five tasks here:
* * Remove RestrictInfo nodes from the input clauses.
* * Index keys must be represented by Var nodes with varattno set to the * * Index keys must be represented by Var nodes with varattno set to the
* index's attribute number, not the attribute number in the original rel. * index's attribute number, not the attribute number in the original rel.
* * If the index key is on the right, commute the clause to put it on the * * If the index key is on the right, commute the clause to put it on the
...@@ -1154,14 +1214,15 @@ create_hashjoin_plan(Query *root, ...@@ -1154,14 +1214,15 @@ create_hashjoin_plan(Query *root,
* *
* Both the input list and the output lists have the form of lists of sublists * Both the input list and the output lists have the form of lists of sublists
* of qual clauses --- the top-level list has one entry for each indexscan * of qual clauses --- the top-level list has one entry for each indexscan
* to be performed. The semantics are OR-of-ANDs. * to be performed. The semantics are OR-of-ANDs. Note however that the
* input list contains RestrictInfos, while the output lists do not.
* *
* fixed_indexquals receives a modified copy of the indexqual list --- the * fixed_indexquals receives a modified copy of the indexqual list --- the
* original is not changed. Note also that the copy shares no substructure * original is not changed. Note also that the copy shares no substructure
* with the original; this is needed in case there is a subplan in it (we need * with the original; this is needed in case there is a subplan in it (we need
* two separate copies of the subplan tree, or things will go awry). * two separate copies of the subplan tree, or things will go awry).
* *
* recheck_indexquals similarly receives a full copy of whichever clauses * recheck_indexquals similarly receives a copy of whichever clauses
* need rechecking. * need rechecking.
* *
* indxstrategy receives a list of integer sublists of strategy numbers. * indxstrategy receives a list of integer sublists of strategy numbers.
...@@ -1243,14 +1304,16 @@ fix_indxqual_sublist(List *indexqual, ...@@ -1243,14 +1304,16 @@ fix_indxqual_sublist(List *indexqual,
*subtype = NIL; *subtype = NIL;
foreach(i, indexqual) foreach(i, indexqual)
{ {
OpExpr *clause = (OpExpr *) lfirst(i); RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
OpExpr *clause;
OpExpr *newclause; OpExpr *newclause;
Relids leftvarnos;
Oid opclass; Oid opclass;
int stratno; int stratno;
Oid stratsubtype; Oid stratsubtype;
bool recheck; bool recheck;
Assert(IsA(rinfo, RestrictInfo));
clause = (OpExpr *) rinfo->clause;
if (!IsA(clause, OpExpr) || if (!IsA(clause, OpExpr) ||
length(clause->args) != 2) length(clause->args) != 2)
elog(ERROR, "indexqual clause is not binary opclause"); elog(ERROR, "indexqual clause is not binary opclause");
...@@ -1269,10 +1332,8 @@ fix_indxqual_sublist(List *indexqual, ...@@ -1269,10 +1332,8 @@ fix_indxqual_sublist(List *indexqual,
* the clause. The indexkey should be the side that refers to * the clause. The indexkey should be the side that refers to
* (only) the base relation. * (only) the base relation.
*/ */
leftvarnos = pull_varnos((Node *) lfirst(newclause->args)); if (!bms_equal(rinfo->left_relids, baserelids))
if (!bms_equal(leftvarnos, baserelids))
CommuteClause(newclause); CommuteClause(newclause);
bms_free(leftvarnos);
/* /*
* Now, determine which index attribute this is, change the * Now, determine which index attribute this is, change the
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.98 2004/01/05 18:04:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.99 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -354,18 +354,22 @@ create_index_path(Query *root, ...@@ -354,18 +354,22 @@ create_index_path(Query *root,
pathnode->path.parent = rel; pathnode->path.parent = rel;
pathnode->path.pathkeys = pathkeys; pathnode->path.pathkeys = pathkeys;
/* Convert RestrictInfo nodes to indexquals the executor can handle */ /* Convert clauses to indexquals the executor can handle */
indexquals = expand_indexqual_conditions(index, restriction_clauses); indexquals = expand_indexqual_conditions(index, restriction_clauses);
/* Flatten the clause-groups list to produce indexclauses list */
restriction_clauses = flatten_clausegroups_list(restriction_clauses);
/* /*
* We are making a pathnode for a single-scan indexscan; therefore, * We are making a pathnode for a single-scan indexscan; therefore,
* both indexinfo and indexqual should be single-element lists. * indexinfo etc should be single-element lists.
*/ */
pathnode->indexinfo = makeList1(index); pathnode->indexinfo = makeList1(index);
pathnode->indexqual = makeList1(indexquals); pathnode->indexclauses = makeList1(restriction_clauses);
pathnode->indexquals = makeList1(indexquals);
/* It's not an innerjoin path. */ /* It's not an innerjoin path. */
pathnode->indexjoinclauses = NIL; pathnode->isjoininner = false;
pathnode->indexscandir = indexscandir; pathnode->indexscandir = indexscandir;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.24 2004/01/05 05:07:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.25 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,7 +20,12 @@ ...@@ -20,7 +20,12 @@
#include "optimizer/var.h" #include "optimizer/var.h"
static Expr *make_sub_restrictinfos(Expr *clause, bool is_pushed_down, static RestrictInfo *make_restrictinfo_internal(Expr *clause,
Expr *orclause,
bool is_pushed_down,
bool valid_everywhere);
static Expr *make_sub_restrictinfos(Expr *clause,
bool is_pushed_down,
bool valid_everywhere); bool valid_everywhere);
static bool join_clause_is_redundant(Query *root, static bool join_clause_is_redundant(Query *root,
RestrictInfo *rinfo, RestrictInfo *rinfo,
...@@ -42,10 +47,89 @@ static bool join_clause_is_redundant(Query *root, ...@@ -42,10 +47,89 @@ static bool join_clause_is_redundant(Query *root,
*/ */
RestrictInfo * RestrictInfo *
make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere) make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere)
{
Expr *orclause;
/*
* If it's an OR clause, build a modified copy with RestrictInfos
* inserted above each subclause of the top-level AND/OR structure.
*/
if (or_clause((Node *) clause))
{
orclause = make_sub_restrictinfos(clause,
is_pushed_down,
valid_everywhere);
}
else
{
/* Shouldn't be an AND clause, else flatten_andors messed up */
Assert(!and_clause((Node *) clause));
orclause = NULL;
}
return make_restrictinfo_internal(clause, orclause,
is_pushed_down, valid_everywhere);
}
/*
* make_restrictinfo_from_indexclauses
*
* Given an indexclauses structure, convert to ordinary expression format
* and build RestrictInfo node(s).
*
* The result is a List since we might need to return multiple RestrictInfos.
*
* This could be done as make_restrictinfo(make_expr_from_indexclauses()),
* but if we did it that way then we would strip the original RestrictInfo
* nodes from the index clauses and be forced to build new ones. It's better
* to have a specialized routine that allows sharing of RestrictInfos.
*/
List *
make_restrictinfo_from_indexclauses(List *indexclauses,
bool is_pushed_down,
bool valid_everywhere)
{
List *withris = NIL;
List *withoutris = NIL;
List *orlist;
/* Empty list probably can't happen, but here's what to do */
if (indexclauses == NIL)
return NIL;
/* If single indexscan, just return the ANDed clauses */
if (lnext(indexclauses) == NIL)
return (List *) lfirst(indexclauses);
/* Else we need an OR RestrictInfo structure */
foreach(orlist, indexclauses)
{
List *andlist = (List *) lfirst(orlist);
/* Create AND subclause with RestrictInfos */
withris = lappend(withris, make_ands_explicit(andlist));
/* And one without */
andlist = get_actual_clauses(andlist);
withoutris = lappend(withoutris, make_ands_explicit(andlist));
}
return makeList1(make_restrictinfo_internal(make_orclause(withoutris),
make_orclause(withris),
is_pushed_down,
valid_everywhere));
}
/*
* make_restrictinfo_internal
*
* Common code for the above two entry points.
*/
static RestrictInfo *
make_restrictinfo_internal(Expr *clause, Expr *orclause,
bool is_pushed_down, bool valid_everywhere)
{ {
RestrictInfo *restrictinfo = makeNode(RestrictInfo); RestrictInfo *restrictinfo = makeNode(RestrictInfo);
restrictinfo->clause = clause; restrictinfo->clause = clause;
restrictinfo->orclause = orclause;
restrictinfo->is_pushed_down = is_pushed_down; restrictinfo->is_pushed_down = is_pushed_down;
restrictinfo->valid_everywhere = valid_everywhere; restrictinfo->valid_everywhere = valid_everywhere;
restrictinfo->can_join = false; /* may get set below */ restrictinfo->can_join = false; /* may get set below */
...@@ -83,24 +167,6 @@ make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere) ...@@ -83,24 +167,6 @@ make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere)
restrictinfo->clause_relids = pull_varnos((Node *) clause); restrictinfo->clause_relids = pull_varnos((Node *) clause);
} }
/*
* If it's an OR clause, set up a modified copy with RestrictInfos
* inserted above each subclause of the top-level AND/OR structure.
*/
if (or_clause((Node *) clause))
{
restrictinfo->orclause = make_sub_restrictinfos(clause,
is_pushed_down,
valid_everywhere);
}
else
{
/* Shouldn't be an AND clause, else flatten_andors messed up */
Assert(!and_clause((Node *) clause));
restrictinfo->orclause = NULL;
}
/* /*
* Fill in all the cacheable fields with "not yet set" markers. * Fill in all the cacheable fields with "not yet set" markers.
* None of these will be computed until/unless needed. Note in * None of these will be computed until/unless needed. Note in
...@@ -161,9 +227,10 @@ make_sub_restrictinfos(Expr *clause, bool is_pushed_down, ...@@ -161,9 +227,10 @@ make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
return make_andclause(andlist); return make_andclause(andlist);
} }
else else
return (Expr *) make_restrictinfo(clause, return (Expr *) make_restrictinfo_internal(clause,
is_pushed_down, NULL,
valid_everywhere); is_pushed_down,
valid_everywhere);
} }
/* /*
...@@ -193,9 +260,11 @@ get_actual_clauses(List *restrictinfo_list) ...@@ -193,9 +260,11 @@ get_actual_clauses(List *restrictinfo_list)
foreach(temp, restrictinfo_list) foreach(temp, restrictinfo_list)
{ {
RestrictInfo *clause = (RestrictInfo *) lfirst(temp); RestrictInfo *rinfo = (RestrictInfo *) lfirst(temp);
Assert(IsA(rinfo, RestrictInfo));
result = lappend(result, clause->clause); result = lappend(result, rinfo->clause);
} }
return result; return result;
} }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.152 2003/12/29 22:22:45 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.153 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -94,6 +94,7 @@ ...@@ -94,6 +94,7 @@
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/plancat.h" #include "optimizer/plancat.h"
#include "optimizer/prep.h" #include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "optimizer/var.h" #include "optimizer/var.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
...@@ -3896,13 +3897,13 @@ genericcostestimate(Query *root, RelOptInfo *rel, ...@@ -3896,13 +3897,13 @@ genericcostestimate(Query *root, RelOptInfo *rel,
double numIndexTuples; double numIndexTuples;
double numIndexPages; double numIndexPages;
QualCost index_qual_cost; QualCost index_qual_cost;
List *selectivityQuals = indexQuals; List *selectivityQuals;
/* /*
* If the index is partial, AND the index predicate with the * If the index is partial, AND the index predicate with the
* explicitly given indexquals to produce a more accurate idea of the * explicitly given indexquals to produce a more accurate idea of the
* index selectivity. This may produce redundant clauses. We can get * index selectivity. This may produce redundant clauses. We get rid
* rid of exact duplicates by using set_union(). We expect that most * of exact duplicates in the code below. We expect that most
* cases of partial redundancy (such as "x < 4" from the qual and * cases of partial redundancy (such as "x < 4" from the qual and
* "x < 5" from the predicate) will be recognized and handled correctly * "x < 5" from the predicate) will be recognized and handled correctly
* by clauselist_selectivity(). This assumption is somewhat fragile, * by clauselist_selectivity(). This assumption is somewhat fragile,
...@@ -3913,10 +3914,25 @@ genericcostestimate(Query *root, RelOptInfo *rel, ...@@ -3913,10 +3914,25 @@ genericcostestimate(Query *root, RelOptInfo *rel,
* necessarily a bad thing. But it'd be nice to do better someday. * necessarily a bad thing. But it'd be nice to do better someday.
* *
* Note that index->indpred and indexQuals are both in implicit-AND form, * Note that index->indpred and indexQuals are both in implicit-AND form,
* so ANDing them together just takes merging the lists. * so ANDing them together just takes merging the lists. However,
* eliminating duplicates is a bit trickier because indexQuals contains
* RestrictInfo nodes and the indpred does not. It is okay to pass a
* mixed list to clauselist_selectivity, but we have to work a bit to
* generate a list without logical duplicates. (We could just set_union
* indpred and strippedQuals, but then we'd not get caching of per-qual
* selectivity estimates.)
*/ */
if (index->indpred != NIL) if (index->indpred != NIL)
selectivityQuals = set_union(index->indpred, indexQuals); {
List *strippedQuals;
List *predExtraQuals;
strippedQuals = get_actual_clauses(indexQuals);
predExtraQuals = set_difference(index->indpred, strippedQuals);
selectivityQuals = nconc(predExtraQuals, indexQuals);
}
else
selectivityQuals = indexQuals;
/* Estimate the fraction of main-table tuples that will be visited */ /* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals, *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.92 2004/01/05 18:04:39 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.93 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -344,23 +344,24 @@ typedef struct Path ...@@ -344,23 +344,24 @@ typedef struct Path
* *
* 'indexinfo' is a list of IndexOptInfo nodes, one per scan to be performed. * 'indexinfo' is a list of IndexOptInfo nodes, one per scan to be performed.
* *
* 'indexqual' is a list of index qualifications, also one per scan. * 'indexclauses' is a list of index qualifications, also one per scan.
* Each entry in 'indexqual' is a sublist of qualification expressions with * Each entry in 'indexclauses' is a sublist of qualification clauses to be
* implicit AND semantics across the sublist items. Only expressions that * used for that scan, with implicit AND semantics across the sublist items.
* are usable as indexquals (as determined by indxpath.c) may appear here. * NOTE that the semantics of the top-level list in 'indexclauses' is OR
* NOTE that the semantics of the top-level list in 'indexqual' is OR
* combination, while the sublists are implicitly AND combinations! * combination, while the sublists are implicitly AND combinations!
* Also note that indexquals lists do not contain RestrictInfo nodes, *
* just bare clause expressions. * 'indexquals' has the same structure as 'indexclauses', but it contains
* * the actual indexqual conditions that can be used with the index(es).
* 'indexjoinclauses' is NIL for an ordinary indexpath (one that does not * In simple cases this is identical to 'indexclauses', but when special
* use any join clauses in the index conditions). For an innerjoin indexpath, * indexable operators appear in 'indexclauses', they are replaced by the
* it has the same structure as 'indexqual', but references the RestrictInfo * derived indexscannable conditions in 'indexquals'.
* nodes from which the indexqual was built, rather than the bare clause *
* expressions. (Note: there isn't necessarily a one-to-one correspondence * Both 'indexclauses' and 'indexquals' are lists of sublists of RestrictInfo
* between RestrictInfos and expressions, because of expansion of special * nodes. (Before 7.5, we kept bare operator expressions in these lists, but
* indexable operators.) We need this so that we can eliminate redundant * storing RestrictInfos is more efficient since selectivities can be cached.)
* join clauses when plans are built. *
* 'isjoininner' is TRUE if the path is a nestloop inner scan (that is,
* some of the index conditions are join rather than restriction clauses).
* *
* 'indexscandir' is one of: * 'indexscandir' is one of:
* ForwardScanDirection: forward scan of an ordered index * ForwardScanDirection: forward scan of an ordered index
...@@ -381,8 +382,9 @@ typedef struct IndexPath ...@@ -381,8 +382,9 @@ typedef struct IndexPath
{ {
Path path; Path path;
List *indexinfo; List *indexinfo;
List *indexqual; List *indexclauses;
List *indexjoinclauses; List *indexquals;
bool isjoininner;
ScanDirection indexscandir; ScanDirection indexscandir;
double rows; /* estimated number of result tuples */ double rows; /* estimated number of result tuples */
} IndexPath; } IndexPath;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.72 2004/01/05 05:07:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.73 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -44,6 +44,8 @@ extern List *group_clauses_by_indexkey_for_or(RelOptInfo *rel, ...@@ -44,6 +44,8 @@ extern List *group_clauses_by_indexkey_for_or(RelOptInfo *rel,
extern List *expand_indexqual_conditions(IndexOptInfo *index, extern List *expand_indexqual_conditions(IndexOptInfo *index,
List *clausegroups); List *clausegroups);
extern void check_partial_indexes(Query *root, RelOptInfo *rel); extern void check_partial_indexes(Query *root, RelOptInfo *rel);
extern List *flatten_clausegroups_list(List *clausegroups);
extern Expr *make_expr_from_indexclauses(List *indexclauses);
/* /*
* orindxpath.c * orindxpath.c
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.22 2004/01/05 05:07:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.23 2004/01/05 23:39:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
extern RestrictInfo *make_restrictinfo(Expr *clause, bool is_pushed_down, extern RestrictInfo *make_restrictinfo(Expr *clause, bool is_pushed_down,
bool valid_everywhere); bool valid_everywhere);
extern List *make_restrictinfo_from_indexclauses(List *indexclauses,
bool is_pushed_down,
bool valid_everywhere);
extern bool restriction_is_or_clause(RestrictInfo *restrictinfo); extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
extern List *get_actual_clauses(List *restrictinfo_list); extern List *get_actual_clauses(List *restrictinfo_list);
extern void get_actual_join_clauses(List *restrictinfo_list, extern void get_actual_join_clauses(List *restrictinfo_list,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment