mirror of
https://github.com/postgres/postgres.git
synced 2025-05-21 00:02:53 -04:00
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.
This commit is contained in:
parent
5d472f6464
commit
fa559a86ee
@ -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)
|
|||||||
_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 @@
|
|||||||
* 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)
|
|||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
result = makeList1(clause);
|
result = makeList1(rinfo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
(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)
|
|||||||
(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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
* '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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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);
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
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.
|
||||||
*/
|
*/
|
||||||
FastListInit(&indexids);
|
if (best_path->isjoininner)
|
||||||
foreach(ixinfo, best_path->indexinfo)
|
|
||||||
{
|
{
|
||||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
|
/*
|
||||||
|
* 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);
|
||||||
|
foreach(i, best_path->indexinfo)
|
||||||
|
{
|
||||||
|
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,
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
* 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,
|
|||||||
*
|
*
|
||||||
* 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,
|
|||||||
*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,
|
|||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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 @@
|
|||||||
#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,
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
|
|
||||||
foreach(temp, restrictinfo_list)
|
foreach(temp, restrictinfo_list)
|
||||||
{
|
{
|
||||||
RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(temp);
|
||||||
|
|
||||||
result = lappend(result, clause->clause);
|
Assert(IsA(rinfo, RestrictInfo));
|
||||||
|
|
||||||
|
result = lappend(result, rinfo->clause);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -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 @@
|
|||||||
#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,
|
|||||||
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,
|
|||||||
* 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 @@
|
|||||||
* 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
|
|||||||
*
|
*
|
||||||
* '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.
|
|
||||||
*
|
*
|
||||||
* 'indexjoinclauses' is NIL for an ordinary indexpath (one that does not
|
* 'indexquals' has the same structure as 'indexclauses', but it contains
|
||||||
* use any join clauses in the index conditions). For an innerjoin indexpath,
|
* the actual indexqual conditions that can be used with the index(es).
|
||||||
* it has the same structure as 'indexqual', but references the RestrictInfo
|
* In simple cases this is identical to 'indexclauses', but when special
|
||||||
* nodes from which the indexqual was built, rather than the bare clause
|
* indexable operators appear in 'indexclauses', they are replaced by the
|
||||||
* expressions. (Note: there isn't necessarily a one-to-one correspondence
|
* derived indexscannable conditions in 'indexquals'.
|
||||||
* between RestrictInfos and expressions, because of expansion of special
|
*
|
||||||
* indexable operators.) We need this so that we can eliminate redundant
|
* Both 'indexclauses' and 'indexquals' are lists of sublists of RestrictInfo
|
||||||
* join clauses when plans are built.
|
* nodes. (Before 7.5, we kept bare operator expressions in these lists, but
|
||||||
|
* storing RestrictInfos is more efficient since selectivities can be cached.)
|
||||||
|
*
|
||||||
|
* '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
|
|||||||
{
|
{
|
||||||
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 @@
|
|||||||
* 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,
|
|||||||
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 @@
|
|||||||
* 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 @@
|
|||||||
|
|
||||||
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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user