mirror of
https://github.com/postgres/postgres.git
synced 2025-11-15 03:41:20 +03:00
Ye-old pgindent run. Same 4-space tabs.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.6 2000/01/26 05:56:36 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.7 2000/04/12 17:15:21 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -485,7 +485,7 @@ xfunc_form_groups(Query *queryInfo, Stream root, Stream bottom)
|
||||
}
|
||||
|
||||
|
||||
/* ------------------- UTILITY FUNCTIONS ------------------------- */
|
||||
/* ------------------- UTILITY FUNCTIONS ------------------------- */
|
||||
|
||||
/*
|
||||
** xfunc_free_stream
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.59 2000/02/15 20:49:16 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.60 2000/04/12 17:15:19 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -24,8 +24,10 @@
|
||||
|
||||
#ifdef GEQO
|
||||
bool enable_geqo = true;
|
||||
|
||||
#else
|
||||
bool enable_geqo = false;
|
||||
|
||||
#endif
|
||||
|
||||
int geqo_rels = GEQO_RELS;
|
||||
@@ -36,6 +38,7 @@ static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed);
|
||||
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
static void debug_print_rel(Query *root, RelOptInfo *rel);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -64,6 +67,7 @@ make_one_rel(Query *root)
|
||||
|
||||
if (levels_needed == 1)
|
||||
{
|
||||
|
||||
/*
|
||||
* Single relation, no more processing is required.
|
||||
*/
|
||||
@@ -71,6 +75,7 @@ make_one_rel(Query *root)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* Generate join tree.
|
||||
*/
|
||||
@@ -100,8 +105,8 @@ set_base_rel_pathlist(Query *root)
|
||||
/*
|
||||
* Generate paths and add them to the rel's pathlist.
|
||||
*
|
||||
* Note: add_path() will discard any paths that are dominated
|
||||
* by another available path, keeping only those paths that are
|
||||
* Note: add_path() will discard any paths that are dominated by
|
||||
* another available path, keeping only those paths that are
|
||||
* superior along at least one dimension of cost or sortedness.
|
||||
*/
|
||||
|
||||
@@ -116,9 +121,10 @@ set_base_rel_pathlist(Query *root)
|
||||
rel->baserestrictinfo,
|
||||
rel->joininfo);
|
||||
|
||||
/* Note: create_or_index_paths depends on create_index_paths
|
||||
* to have marked OR restriction clauses with relevant indices;
|
||||
* this is why it doesn't need to be given the list of indices.
|
||||
/*
|
||||
* Note: create_or_index_paths depends on create_index_paths to
|
||||
* have marked OR restriction clauses with relevant indices; this
|
||||
* is why it doesn't need to be given the list of indices.
|
||||
*/
|
||||
create_or_index_paths(root, rel, rel->baserestrictinfo);
|
||||
|
||||
@@ -153,11 +159,11 @@ make_one_rel_by_joins(Query *root, int levels_needed)
|
||||
return geqo(root);
|
||||
|
||||
/*
|
||||
* We employ a simple "dynamic programming" algorithm: we first
|
||||
* find all ways to build joins of two base relations, then all ways
|
||||
* to build joins of three base relations (from two-base-rel joins
|
||||
* and other base rels), then four-base-rel joins, and so on until
|
||||
* we have considered all ways to join all N relations into one rel.
|
||||
* We employ a simple "dynamic programming" algorithm: we first find
|
||||
* all ways to build joins of two base relations, then all ways to
|
||||
* build joins of three base relations (from two-base-rel joins and
|
||||
* other base rels), then four-base-rel joins, and so on until we have
|
||||
* considered all ways to join all N relations into one rel.
|
||||
*/
|
||||
|
||||
for (lev = 2; lev <= levels_needed; lev++)
|
||||
@@ -185,9 +191,10 @@ make_one_rel_by_joins(Query *root, int levels_needed)
|
||||
rel = (RelOptInfo *) lfirst(x);
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
/*
|
||||
* * for each expensive predicate in each path in each distinct
|
||||
* rel, * consider doing pullup -- JMH
|
||||
* * for each expensive predicate in each path in each
|
||||
* distinct rel, * consider doing pullup -- JMH
|
||||
*/
|
||||
if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF)
|
||||
xfunc_trypullup(rel);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.33 2000/03/23 23:35:47 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.34 2000/04/12 17:15:19 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -28,17 +28,18 @@
|
||||
* Data structure for accumulating info about possible range-query
|
||||
* clause pairs in clauselist_selectivity.
|
||||
*/
|
||||
typedef struct RangeQueryClause {
|
||||
struct RangeQueryClause *next; /* next in linked list */
|
||||
typedef struct RangeQueryClause
|
||||
{
|
||||
struct RangeQueryClause *next; /* next in linked list */
|
||||
Node *var; /* The common variable of the clauses */
|
||||
bool have_lobound; /* found a low-bound clause yet? */
|
||||
bool have_hibound; /* found a high-bound clause yet? */
|
||||
Selectivity lobound; /* Selectivity of a var > something clause */
|
||||
Selectivity hibound; /* Selectivity of a var < something clause */
|
||||
Selectivity lobound; /* Selectivity of a var > something clause */
|
||||
Selectivity hibound; /* Selectivity of a var < something clause */
|
||||
} RangeQueryClause;
|
||||
|
||||
static void addRangeClause(RangeQueryClause **rqlist, Node *clause,
|
||||
int flag, bool isLTsel, Selectivity s2);
|
||||
int flag, bool isLTsel, Selectivity s2);
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
@@ -59,7 +60,7 @@ restrictlist_selectivity(Query *root,
|
||||
int varRelid)
|
||||
{
|
||||
List *clauselist = get_actual_clauses(restrictinfo_list);
|
||||
Selectivity result;
|
||||
Selectivity result;
|
||||
|
||||
result = clauselist_selectivity(root, clauselist, varRelid);
|
||||
freeList(clauselist);
|
||||
@@ -75,7 +76,7 @@ restrictlist_selectivity(Query *root,
|
||||
* See clause_selectivity() for the meaning of the varRelid parameter.
|
||||
*
|
||||
* Our basic approach is to take the product of the selectivities of the
|
||||
* subclauses. However, that's only right if the subclauses have independent
|
||||
* subclauses. However, that's only right if the subclauses have independent
|
||||
* probabilities, and in reality they are often NOT independent. So,
|
||||
* we want to be smarter where we can.
|
||||
|
||||
@@ -92,7 +93,7 @@ restrictlist_selectivity(Query *root,
|
||||
* see that hisel is the fraction of the range below the high bound, while
|
||||
* losel is the fraction above the low bound; so hisel can be interpreted
|
||||
* directly as a 0..1 value but we need to convert losel to 1-losel before
|
||||
* interpreting it as a value. Then the available range is 1-losel to hisel.)
|
||||
* interpreting it as a value. Then the available range is 1-losel to hisel.)
|
||||
* If the calculation yields zero or negative, however, we chicken out and
|
||||
* use the default interpretation; that probably means that one or both
|
||||
* selectivities is a default estimate rather than an actual range value.
|
||||
@@ -104,9 +105,9 @@ clauselist_selectivity(Query *root,
|
||||
List *clauses,
|
||||
int varRelid)
|
||||
{
|
||||
Selectivity s1 = 1.0;
|
||||
RangeQueryClause *rqlist = NULL;
|
||||
List *clist;
|
||||
Selectivity s1 = 1.0;
|
||||
RangeQueryClause *rqlist = NULL;
|
||||
List *clist;
|
||||
|
||||
/*
|
||||
* Initial scan over clauses. Anything that doesn't look like a
|
||||
@@ -116,13 +117,13 @@ clauselist_selectivity(Query *root,
|
||||
foreach(clist, clauses)
|
||||
{
|
||||
Node *clause = (Node *) lfirst(clist);
|
||||
Selectivity s2;
|
||||
Selectivity s2;
|
||||
|
||||
/*
|
||||
* See if it looks like a restriction clause with a constant.
|
||||
* (If it's not a constant we can't really trust the selectivity!)
|
||||
* NB: for consistency of results, this fragment of code had
|
||||
* better match what clause_selectivity() would do.
|
||||
* See if it looks like a restriction clause with a constant. (If
|
||||
* it's not a constant we can't really trust the selectivity!) NB:
|
||||
* for consistency of results, this fragment of code had better
|
||||
* match what clause_selectivity() would do.
|
||||
*/
|
||||
if (varRelid != 0 || NumRelids(clause) == 1)
|
||||
{
|
||||
@@ -147,11 +148,12 @@ clauselist_selectivity(Query *root,
|
||||
root->rtable),
|
||||
attno,
|
||||
constval, flag);
|
||||
|
||||
/*
|
||||
* If we reach here, we have computed the same result
|
||||
* that clause_selectivity would, so we can just use s2
|
||||
* if it's the wrong oprrest. But if it's the right
|
||||
* oprrest, add the clause to rqlist for later processing.
|
||||
* If we reach here, we have computed the same result that
|
||||
* clause_selectivity would, so we can just use s2 if it's
|
||||
* the wrong oprrest. But if it's the right oprrest, add
|
||||
* the clause to rqlist for later processing.
|
||||
*/
|
||||
switch (oprrest)
|
||||
{
|
||||
@@ -166,7 +168,7 @@ clauselist_selectivity(Query *root,
|
||||
s1 = s1 * s2;
|
||||
break;
|
||||
}
|
||||
continue; /* drop to loop bottom */
|
||||
continue; /* drop to loop bottom */
|
||||
}
|
||||
}
|
||||
/* Not the right form, so treat it generically. */
|
||||
@@ -179,12 +181,12 @@ clauselist_selectivity(Query *root,
|
||||
*/
|
||||
while (rqlist != NULL)
|
||||
{
|
||||
RangeQueryClause *rqnext;
|
||||
RangeQueryClause *rqnext;
|
||||
|
||||
if (rqlist->have_lobound && rqlist->have_hibound)
|
||||
{
|
||||
/* Successfully matched a pair of range clauses */
|
||||
Selectivity s2 = rqlist->hibound + rqlist->lobound - 1.0;
|
||||
Selectivity s2 = rqlist->hibound + rqlist->lobound - 1.0;
|
||||
|
||||
/*
|
||||
* A zero or slightly negative s2 should be converted into a
|
||||
@@ -199,14 +201,20 @@ clauselist_selectivity(Query *root,
|
||||
{
|
||||
if (s2 < -0.01)
|
||||
{
|
||||
/* No data available --- use a default estimate that
|
||||
|
||||
/*
|
||||
* No data available --- use a default estimate that
|
||||
* is small, but not real small.
|
||||
*/
|
||||
s2 = 0.01;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's just roundoff error; use a small positive value */
|
||||
|
||||
/*
|
||||
* It's just roundoff error; use a small positive
|
||||
* value
|
||||
*/
|
||||
s2 = 1.0e-10;
|
||||
}
|
||||
}
|
||||
@@ -239,15 +247,15 @@ static void
|
||||
addRangeClause(RangeQueryClause **rqlist, Node *clause,
|
||||
int flag, bool isLTsel, Selectivity s2)
|
||||
{
|
||||
RangeQueryClause *rqelem;
|
||||
Node *var;
|
||||
bool is_lobound;
|
||||
RangeQueryClause *rqelem;
|
||||
Node *var;
|
||||
bool is_lobound;
|
||||
|
||||
/* get_relattval sets flag&SEL_RIGHT if the var is on the LEFT. */
|
||||
if (flag & SEL_RIGHT)
|
||||
{
|
||||
var = (Node *) get_leftop((Expr *) clause);
|
||||
is_lobound = ! isLTsel; /* x < something is high bound */
|
||||
is_lobound = !isLTsel; /* x < something is high bound */
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -257,23 +265,27 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause,
|
||||
|
||||
for (rqelem = *rqlist; rqelem; rqelem = rqelem->next)
|
||||
{
|
||||
/* We use full equal() here because the "var" might be a function
|
||||
|
||||
/*
|
||||
* We use full equal() here because the "var" might be a function
|
||||
* of one or more attributes of the same relation...
|
||||
*/
|
||||
if (! equal(var, rqelem->var))
|
||||
if (!equal(var, rqelem->var))
|
||||
continue;
|
||||
/* Found the right group to put this clause in */
|
||||
if (is_lobound)
|
||||
{
|
||||
if (! rqelem->have_lobound)
|
||||
if (!rqelem->have_lobound)
|
||||
{
|
||||
rqelem->have_lobound = true;
|
||||
rqelem->lobound = s2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have found two similar clauses, such as
|
||||
* x < y AND x < z. Keep only the more restrictive one.
|
||||
|
||||
/*
|
||||
* We have found two similar clauses, such as x < y AND x
|
||||
* < z. Keep only the more restrictive one.
|
||||
*/
|
||||
if (rqelem->lobound > s2)
|
||||
rqelem->lobound = s2;
|
||||
@@ -281,15 +293,17 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! rqelem->have_hibound)
|
||||
if (!rqelem->have_hibound)
|
||||
{
|
||||
rqelem->have_hibound = true;
|
||||
rqelem->hibound = s2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have found two similar clauses, such as
|
||||
* x > y AND x > z. Keep only the more restrictive one.
|
||||
|
||||
/*
|
||||
* We have found two similar clauses, such as x > y AND x
|
||||
* > z. Keep only the more restrictive one.
|
||||
*/
|
||||
if (rqelem->hibound > s2)
|
||||
rqelem->hibound = s2;
|
||||
@@ -331,7 +345,7 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause,
|
||||
* nestloop join's inner relation --- varRelid should then be the ID of the
|
||||
* inner relation.
|
||||
*
|
||||
* When varRelid is 0, all variables are treated as variables. This
|
||||
* When varRelid is 0, all variables are treated as variables. This
|
||||
* is appropriate for ordinary join clauses and restriction clauses.
|
||||
*/
|
||||
Selectivity
|
||||
@@ -339,12 +353,13 @@ clause_selectivity(Query *root,
|
||||
Node *clause,
|
||||
int varRelid)
|
||||
{
|
||||
Selectivity s1 = 1.0; /* default for any unhandled clause type */
|
||||
Selectivity s1 = 1.0; /* default for any unhandled clause type */
|
||||
|
||||
if (clause == NULL)
|
||||
return s1;
|
||||
if (IsA(clause, Var))
|
||||
{
|
||||
|
||||
/*
|
||||
* we have a bool Var. This is exactly equivalent to the clause:
|
||||
* reln.attribute = 't' so we compute the selectivity as if that
|
||||
@@ -352,7 +367,7 @@ clause_selectivity(Query *root,
|
||||
* didn't want to have to do system cache look ups to find out all
|
||||
* of that info.
|
||||
*/
|
||||
Index varno = ((Var *) clause)->varno;
|
||||
Index varno = ((Var *) clause)->varno;
|
||||
|
||||
if (varRelid == 0 || varRelid == (int) varno)
|
||||
s1 = restriction_selectivity(F_EQSEL,
|
||||
@@ -377,7 +392,7 @@ clause_selectivity(Query *root,
|
||||
{
|
||||
/* inverse of the selectivity of the underlying clause */
|
||||
s1 = 1.0 - clause_selectivity(root,
|
||||
(Node*) get_notclausearg((Expr*) clause),
|
||||
(Node *) get_notclausearg((Expr *) clause),
|
||||
varRelid);
|
||||
}
|
||||
else if (and_clause(clause))
|
||||
@@ -389,18 +404,21 @@ clause_selectivity(Query *root,
|
||||
}
|
||||
else if (or_clause(clause))
|
||||
{
|
||||
|
||||
/*
|
||||
* Selectivities for an 'or' clause are computed as s1+s2 - s1*s2
|
||||
* to account for the probable overlap of selected tuple sets.
|
||||
* XXX is this too conservative?
|
||||
* to account for the probable overlap of selected tuple sets. XXX
|
||||
* is this too conservative?
|
||||
*/
|
||||
List *arg;
|
||||
List *arg;
|
||||
|
||||
s1 = 0.0;
|
||||
foreach(arg, ((Expr *) clause)->args)
|
||||
{
|
||||
Selectivity s2 = clause_selectivity(root,
|
||||
Selectivity s2 = clause_selectivity(root,
|
||||
(Node *) lfirst(arg),
|
||||
varRelid);
|
||||
|
||||
s1 = s1 + s2 - s1 * s2;
|
||||
}
|
||||
}
|
||||
@@ -411,17 +429,20 @@ clause_selectivity(Query *root,
|
||||
|
||||
if (varRelid != 0)
|
||||
{
|
||||
|
||||
/*
|
||||
* If we are considering a nestloop join then all clauses
|
||||
* are restriction clauses, since we are only interested in
|
||||
* the one relation.
|
||||
* If we are considering a nestloop join then all clauses are
|
||||
* restriction clauses, since we are only interested in the
|
||||
* one relation.
|
||||
*/
|
||||
is_join_clause = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* Otherwise, it's a join if there's more than one relation used.
|
||||
* Otherwise, it's a join if there's more than one relation
|
||||
* used.
|
||||
*/
|
||||
is_join_clause = (NumRelids(clause) > 1);
|
||||
}
|
||||
@@ -432,8 +453,8 @@ clause_selectivity(Query *root,
|
||||
RegProcedure oprjoin = get_oprjoin(opno);
|
||||
|
||||
/*
|
||||
* if the oprjoin procedure is missing for whatever reason, use a
|
||||
* selectivity of 0.5
|
||||
* if the oprjoin procedure is missing for whatever reason,
|
||||
* use a selectivity of 0.5
|
||||
*/
|
||||
if (!oprjoin)
|
||||
s1 = (Selectivity) 0.5;
|
||||
@@ -460,8 +481,8 @@ clause_selectivity(Query *root,
|
||||
RegProcedure oprrest = get_oprrest(opno);
|
||||
|
||||
/*
|
||||
* if the oprrest procedure is missing for whatever reason, use a
|
||||
* selectivity of 0.5
|
||||
* if the oprrest procedure is missing for whatever reason,
|
||||
* use a selectivity of 0.5
|
||||
*/
|
||||
if (!oprrest)
|
||||
s1 = (Selectivity) 0.5;
|
||||
@@ -484,6 +505,7 @@ clause_selectivity(Query *root,
|
||||
}
|
||||
else if (is_funcclause(clause))
|
||||
{
|
||||
|
||||
/*
|
||||
* This is not an operator, so we guess at the selectivity. THIS
|
||||
* IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE ABLE TO HAVE
|
||||
@@ -493,6 +515,7 @@ clause_selectivity(Query *root,
|
||||
}
|
||||
else if (is_subplan(clause))
|
||||
{
|
||||
|
||||
/*
|
||||
* Just for the moment! FIX ME! - vadim 02/04/98
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* Obviously, taking constants for these values is an oversimplification,
|
||||
* but it's tough enough to get any useful estimates even at this level of
|
||||
* detail. Note that all of these parameters are user-settable, in case
|
||||
* detail. Note that all of these parameters are user-settable, in case
|
||||
* the default values are drastically off for a particular platform.
|
||||
*
|
||||
* We compute two separate costs for each path:
|
||||
@@ -37,12 +37,12 @@
|
||||
* will be no zero divide.) RelOptInfos, Paths, and Plans themselves never
|
||||
* account for LIMIT.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.56 2000/04/09 04:31:36 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.57 2000/04/12 17:15:19 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -118,6 +118,7 @@ cost_seqscan(Path *path, RelOptInfo *baserel)
|
||||
/* disk costs */
|
||||
if (lfirsti(baserel->relids) < 0)
|
||||
{
|
||||
|
||||
/*
|
||||
* cost of sequentially scanning a materialized temporary relation
|
||||
*/
|
||||
@@ -125,15 +126,17 @@ cost_seqscan(Path *path, RelOptInfo *baserel)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* The cost of reading a page sequentially is 1.0, by definition.
|
||||
* Note that the Unix kernel will typically do some amount of
|
||||
* read-ahead optimization, so that this cost is less than the true
|
||||
* cost of reading a page from disk. We ignore that issue here,
|
||||
* but must take it into account when estimating the cost of
|
||||
* read-ahead optimization, so that this cost is less than the
|
||||
* true cost of reading a page from disk. We ignore that issue
|
||||
* here, but must take it into account when estimating the cost of
|
||||
* non-sequential accesses!
|
||||
*/
|
||||
run_cost += baserel->pages; /* sequential fetches with cost 1.0 */
|
||||
run_cost += baserel->pages; /* sequential fetches with cost
|
||||
* 1.0 */
|
||||
}
|
||||
|
||||
/* CPU costs */
|
||||
@@ -151,7 +154,7 @@ cost_seqscan(Path *path, RelOptInfo *baserel)
|
||||
*
|
||||
* The simplistic model that the cost is random_page_cost is what we want
|
||||
* to use for large relations; but for small ones that is a serious
|
||||
* overestimate because of the effects of caching. This routine tries to
|
||||
* overestimate because of the effects of caching. This routine tries to
|
||||
* account for that.
|
||||
*
|
||||
* Unfortunately we don't have any good way of estimating the effective cache
|
||||
@@ -221,12 +224,12 @@ cost_index(Path *path, Query *root,
|
||||
Cost cpu_per_tuple;
|
||||
Cost indexStartupCost;
|
||||
Cost indexTotalCost;
|
||||
Selectivity indexSelectivity;
|
||||
Selectivity indexSelectivity;
|
||||
double tuples_fetched;
|
||||
double pages_fetched;
|
||||
|
||||
/* Should only be applied to base relations */
|
||||
Assert(IsA(baserel, RelOptInfo) && IsA(index, IndexOptInfo));
|
||||
Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
|
||||
Assert(length(baserel->relids) == 1);
|
||||
|
||||
if (!enable_indexscan && !is_injoin)
|
||||
@@ -234,8 +237,9 @@ cost_index(Path *path, Query *root,
|
||||
|
||||
/*
|
||||
* Call index-access-method-specific code to estimate the processing
|
||||
* cost for scanning the index, as well as the selectivity of the index
|
||||
* (ie, the fraction of main-table tuples we will have to retrieve).
|
||||
* cost for scanning the index, as well as the selectivity of the
|
||||
* index (ie, the fraction of main-table tuples we will have to
|
||||
* retrieve).
|
||||
*/
|
||||
fmgr(index->amcostestimate, root, baserel, index, indexQuals,
|
||||
&indexStartupCost, &indexTotalCost, &indexSelectivity);
|
||||
@@ -249,17 +253,18 @@ cost_index(Path *path, Query *root,
|
||||
*
|
||||
* If the number of tuples is much smaller than the number of pages in
|
||||
* the relation, each tuple will cost a separate nonsequential fetch.
|
||||
* If it is comparable or larger, then probably we will be able to avoid
|
||||
* some fetches. We use a growth rate of log(#tuples/#pages + 1) ---
|
||||
* probably totally bogus, but intuitively it gives the right shape of
|
||||
* curve at least.
|
||||
* If it is comparable or larger, then probably we will be able to
|
||||
* avoid some fetches. We use a growth rate of log(#tuples/#pages +
|
||||
* 1) --- probably totally bogus, but intuitively it gives the right
|
||||
* shape of curve at least.
|
||||
*
|
||||
* XXX if the relation has recently been "clustered" using this index,
|
||||
* then in fact the target tuples will be highly nonuniformly distributed,
|
||||
* and we will be seriously overestimating the scan cost! Currently we
|
||||
* have no way to know whether the relation has been clustered, nor how
|
||||
* much it's been modified since the last clustering, so we ignore this
|
||||
* effect. Would be nice to do better someday.
|
||||
* then in fact the target tuples will be highly nonuniformly
|
||||
* distributed, and we will be seriously overestimating the scan cost!
|
||||
* Currently we have no way to know whether the relation has been
|
||||
* clustered, nor how much it's been modified since the last
|
||||
* clustering, so we ignore this effect. Would be nice to do better
|
||||
* someday.
|
||||
*/
|
||||
|
||||
tuples_fetched = indexSelectivity * baserel->tuples;
|
||||
@@ -274,8 +279,8 @@ cost_index(Path *path, Query *root,
|
||||
pages_fetched = tuples_fetched;
|
||||
|
||||
/*
|
||||
* Now estimate one nonsequential access per page fetched,
|
||||
* plus appropriate CPU costs per tuple.
|
||||
* Now estimate one nonsequential access per page fetched, plus
|
||||
* appropriate CPU costs per tuple.
|
||||
*/
|
||||
|
||||
/* disk costs for main table */
|
||||
@@ -283,16 +288,18 @@ cost_index(Path *path, Query *root,
|
||||
|
||||
/* CPU costs */
|
||||
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
|
||||
|
||||
/*
|
||||
* Normally the indexquals will be removed from the list of restriction
|
||||
* clauses that we have to evaluate as qpquals, so we should subtract
|
||||
* their costs from baserestrictcost. For a lossy index, however, we
|
||||
* will have to recheck all the quals and so mustn't subtract anything.
|
||||
* Also, if we are doing a join then some of the indexquals are join
|
||||
* clauses and shouldn't be subtracted. Rather than work out exactly
|
||||
* how much to subtract, we don't subtract anything in that case either.
|
||||
* Normally the indexquals will be removed from the list of
|
||||
* restriction clauses that we have to evaluate as qpquals, so we
|
||||
* should subtract their costs from baserestrictcost. For a lossy
|
||||
* index, however, we will have to recheck all the quals and so
|
||||
* mustn't subtract anything. Also, if we are doing a join then some
|
||||
* of the indexquals are join clauses and shouldn't be subtracted.
|
||||
* Rather than work out exactly how much to subtract, we don't
|
||||
* subtract anything in that case either.
|
||||
*/
|
||||
if (! index->lossy && ! is_injoin)
|
||||
if (!index->lossy && !is_injoin)
|
||||
cpu_per_tuple -= cost_qual_eval(indexQuals);
|
||||
|
||||
run_cost += cpu_per_tuple * tuples_fetched;
|
||||
@@ -326,7 +333,7 @@ cost_tidscan(Path *path, RelOptInfo *baserel, List *tideval)
|
||||
path->startup_cost = startup_cost;
|
||||
path->total_cost = startup_cost + run_cost;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* cost_sort
|
||||
* Determines and returns the cost of sorting a relation.
|
||||
@@ -341,7 +348,7 @@ cost_tidscan(Path *path, RelOptInfo *baserel, List *tideval)
|
||||
* If the total volume exceeds SortMem, we switch to a tape-style merge
|
||||
* algorithm. There will still be about t*log2(t) tuple comparisons in
|
||||
* total, but we will also need to write and read each tuple once per
|
||||
* merge pass. We expect about ceil(log6(r)) merge passes where r is the
|
||||
* merge pass. We expect about ceil(log6(r)) merge passes where r is the
|
||||
* number of initial runs formed (log6 because tuplesort.c uses six-tape
|
||||
* merging). Since the average initial run should be about twice SortMem,
|
||||
* we have
|
||||
@@ -385,8 +392,8 @@ cost_sort(Path *path, List *pathkeys, double tuples, int width)
|
||||
/*
|
||||
* CPU costs
|
||||
*
|
||||
* Assume about two operator evals per tuple comparison
|
||||
* and N log2 N comparisons
|
||||
* Assume about two operator evals per tuple comparison and N log2 N
|
||||
* comparisons
|
||||
*/
|
||||
startup_cost += 2.0 * cpu_operator_cost * tuples * LOG2(tuples);
|
||||
|
||||
@@ -408,7 +415,7 @@ cost_sort(Path *path, List *pathkeys, double tuples, int width)
|
||||
|
||||
/*
|
||||
* Note: should we bother to assign a nonzero run_cost to reflect the
|
||||
* overhead of extracting tuples from the sort result? Probably not
|
||||
* overhead of extracting tuples from the sort result? Probably not
|
||||
* worth worrying about.
|
||||
*/
|
||||
path->startup_cost = startup_cost;
|
||||
@@ -440,19 +447,22 @@ cost_nestloop(Path *path,
|
||||
startup_cost += disable_cost;
|
||||
|
||||
/* cost of source data */
|
||||
|
||||
/*
|
||||
* NOTE: we assume that the inner path's startup_cost is paid once, not
|
||||
* over again on each restart. This is certainly correct if the inner
|
||||
* path is materialized. Are there any cases where it is wrong?
|
||||
* NOTE: we assume that the inner path's startup_cost is paid once,
|
||||
* not over again on each restart. This is certainly correct if the
|
||||
* inner path is materialized. Are there any cases where it is wrong?
|
||||
*/
|
||||
startup_cost += outer_path->startup_cost + inner_path->startup_cost;
|
||||
run_cost += outer_path->total_cost - outer_path->startup_cost;
|
||||
run_cost += outer_path->parent->rows *
|
||||
(inner_path->total_cost - inner_path->startup_cost);
|
||||
|
||||
/* Number of tuples processed (not number emitted!). If inner path is
|
||||
/*
|
||||
* Number of tuples processed (not number emitted!). If inner path is
|
||||
* an indexscan, be sure to use its estimated output row count, which
|
||||
* may be lower than the restriction-clause-only row count of its parent.
|
||||
* may be lower than the restriction-clause-only row count of its
|
||||
* parent.
|
||||
*/
|
||||
if (IsA(inner_path, IndexPath))
|
||||
ntuples = ((IndexPath *) inner_path)->rows;
|
||||
@@ -498,11 +508,12 @@ cost_mergejoin(Path *path,
|
||||
startup_cost += disable_cost;
|
||||
|
||||
/* cost of source data */
|
||||
|
||||
/*
|
||||
* Note we are assuming that each source tuple is fetched just once,
|
||||
* which is not right in the presence of equal keys. If we had a way of
|
||||
* estimating the proportion of equal keys, we could apply a correction
|
||||
* factor...
|
||||
* which is not right in the presence of equal keys. If we had a way
|
||||
* of estimating the proportion of equal keys, we could apply a
|
||||
* correction factor...
|
||||
*/
|
||||
if (outersortkeys) /* do we need to sort outer? */
|
||||
{
|
||||
@@ -537,10 +548,10 @@ cost_mergejoin(Path *path,
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimate the number of tuples to be processed in the mergejoin itself
|
||||
* as one per tuple in the two source relations. This could be a drastic
|
||||
* underestimate if there are many equal-keyed tuples in either relation,
|
||||
* but we have no good way of estimating that...
|
||||
* Estimate the number of tuples to be processed in the mergejoin
|
||||
* itself as one per tuple in the two source relations. This could be
|
||||
* a drastic underestimate if there are many equal-keyed tuples in
|
||||
* either relation, but we have no good way of estimating that...
|
||||
*/
|
||||
ntuples = outer_path->parent->rows + inner_path->parent->rows;
|
||||
|
||||
@@ -575,9 +586,9 @@ cost_hashjoin(Path *path,
|
||||
Cost cpu_per_tuple;
|
||||
double ntuples;
|
||||
double outerbytes = relation_byte_size(outer_path->parent->rows,
|
||||
outer_path->parent->width);
|
||||
outer_path->parent->width);
|
||||
double innerbytes = relation_byte_size(inner_path->parent->rows,
|
||||
inner_path->parent->width);
|
||||
inner_path->parent->width);
|
||||
long hashtablebytes = SortMem * 1024L;
|
||||
|
||||
if (!enable_hashjoin)
|
||||
@@ -592,7 +603,8 @@ cost_hashjoin(Path *path,
|
||||
startup_cost += cpu_operator_cost * inner_path->parent->rows;
|
||||
run_cost += cpu_operator_cost * outer_path->parent->rows;
|
||||
|
||||
/* the number of tuple comparisons needed is the number of outer
|
||||
/*
|
||||
* the number of tuple comparisons needed is the number of outer
|
||||
* tuples times the typical hash bucket size, which we estimate
|
||||
* conservatively as the inner disbursion times the inner tuple count.
|
||||
*/
|
||||
@@ -601,9 +613,9 @@ cost_hashjoin(Path *path,
|
||||
|
||||
/*
|
||||
* Estimate the number of tuples that get through the hashing filter
|
||||
* as one per tuple in the two source relations. This could be a drastic
|
||||
* underestimate if there are many equal-keyed tuples in either relation,
|
||||
* but we have no good way of estimating that...
|
||||
* as one per tuple in the two source relations. This could be a
|
||||
* drastic underestimate if there are many equal-keyed tuples in
|
||||
* either relation, but we have no good way of estimating that...
|
||||
*/
|
||||
ntuples = outer_path->parent->rows + inner_path->parent->rows;
|
||||
|
||||
@@ -614,33 +626,31 @@ cost_hashjoin(Path *path,
|
||||
/*
|
||||
* if inner relation is too big then we will need to "batch" the join,
|
||||
* which implies writing and reading most of the tuples to disk an
|
||||
* extra time. Charge one cost unit per page of I/O (correct since
|
||||
* it should be nice and sequential...). Writing the inner rel counts
|
||||
* as startup cost, all the rest as run cost.
|
||||
* extra time. Charge one cost unit per page of I/O (correct since it
|
||||
* should be nice and sequential...). Writing the inner rel counts as
|
||||
* startup cost, all the rest as run cost.
|
||||
*/
|
||||
if (innerbytes > hashtablebytes)
|
||||
{
|
||||
double outerpages = page_size(outer_path->parent->rows,
|
||||
outer_path->parent->width);
|
||||
double innerpages = page_size(inner_path->parent->rows,
|
||||
inner_path->parent->width);
|
||||
double outerpages = page_size(outer_path->parent->rows,
|
||||
outer_path->parent->width);
|
||||
double innerpages = page_size(inner_path->parent->rows,
|
||||
inner_path->parent->width);
|
||||
|
||||
startup_cost += innerpages;
|
||||
run_cost += innerpages + 2 * outerpages;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bias against putting larger relation on inside. We don't want
|
||||
* an absolute prohibition, though, since larger relation might have
|
||||
* Bias against putting larger relation on inside. We don't want an
|
||||
* absolute prohibition, though, since larger relation might have
|
||||
* better disbursion --- and we can't trust the size estimates
|
||||
* unreservedly, anyway. Instead, inflate the startup cost by
|
||||
* the square root of the size ratio. (Why square root? No real good
|
||||
* unreservedly, anyway. Instead, inflate the startup cost by the
|
||||
* square root of the size ratio. (Why square root? No real good
|
||||
* reason, but it seems reasonable...)
|
||||
*/
|
||||
if (innerbytes > outerbytes && outerbytes > 0)
|
||||
{
|
||||
startup_cost *= sqrt(innerbytes / outerbytes);
|
||||
}
|
||||
|
||||
path->startup_cost = startup_cost;
|
||||
path->total_cost = startup_cost + run_cost;
|
||||
@@ -656,7 +666,7 @@ cost_hashjoin(Path *path,
|
||||
Cost
|
||||
cost_qual_eval(List *quals)
|
||||
{
|
||||
Cost total = 0;
|
||||
Cost total = 0;
|
||||
|
||||
cost_qual_eval_walker((Node *) quals, &total);
|
||||
return total;
|
||||
@@ -667,10 +677,11 @@ cost_qual_eval_walker(Node *node, Cost *total)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Our basic strategy is to charge one cpu_operator_cost for each
|
||||
* operator or function node in the given tree. Vars and Consts
|
||||
* are charged zero, and so are boolean operators (AND, OR, NOT).
|
||||
* operator or function node in the given tree. Vars and Consts are
|
||||
* charged zero, and so are boolean operators (AND, OR, NOT).
|
||||
* Simplistic, but a lot better than no model at all.
|
||||
*
|
||||
* Should we try to account for the possibility of short-circuit
|
||||
@@ -678,7 +689,7 @@ cost_qual_eval_walker(Node *node, Cost *total)
|
||||
*/
|
||||
if (IsA(node, Expr))
|
||||
{
|
||||
Expr *expr = (Expr *) node;
|
||||
Expr *expr = (Expr *) node;
|
||||
|
||||
switch (expr->opType)
|
||||
{
|
||||
@@ -691,17 +702,19 @@ cost_qual_eval_walker(Node *node, Cost *total)
|
||||
case NOT_EXPR:
|
||||
break;
|
||||
case SUBPLAN_EXPR:
|
||||
|
||||
/*
|
||||
* A subplan node in an expression indicates that the subplan
|
||||
* will be executed on each evaluation, so charge accordingly.
|
||||
* (We assume that sub-selects that can be executed as
|
||||
* InitPlans have already been removed from the expression.)
|
||||
* A subplan node in an expression indicates that the
|
||||
* subplan will be executed on each evaluation, so charge
|
||||
* accordingly. (We assume that sub-selects that can be
|
||||
* executed as InitPlans have already been removed from
|
||||
* the expression.)
|
||||
*
|
||||
* NOTE: this logic should agree with the estimates used by
|
||||
* make_subplan() in plan/subselect.c.
|
||||
* make_subplan() in plan/subselect.c.
|
||||
*/
|
||||
{
|
||||
SubPlan *subplan = (SubPlan *) expr->oper;
|
||||
SubPlan *subplan = (SubPlan *) expr->oper;
|
||||
Plan *plan = subplan->plan;
|
||||
Cost subcost;
|
||||
|
||||
@@ -730,13 +743,14 @@ cost_qual_eval_walker(Node *node, Cost *total)
|
||||
}
|
||||
/* fall through to examine args of Expr node */
|
||||
}
|
||||
|
||||
/*
|
||||
* expression_tree_walker doesn't know what to do with RestrictInfo nodes,
|
||||
* but we just want to recurse through them.
|
||||
* expression_tree_walker doesn't know what to do with RestrictInfo
|
||||
* nodes, but we just want to recurse through them.
|
||||
*/
|
||||
if (IsA(node, RestrictInfo))
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) node;
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) node;
|
||||
|
||||
return cost_qual_eval_walker((Node *) restrictinfo->clause, total);
|
||||
}
|
||||
@@ -755,7 +769,7 @@ cost_qual_eval_walker(Node *node, Cost *total)
|
||||
*
|
||||
* We set the following fields of the rel node:
|
||||
* rows: the estimated number of output tuples (after applying
|
||||
* restriction clauses).
|
||||
* restriction clauses).
|
||||
* width: the estimated average output tuple width in bytes.
|
||||
* baserestrictcost: estimated cost of evaluating baserestrictinfo clauses.
|
||||
*/
|
||||
@@ -769,9 +783,11 @@ set_baserel_size_estimates(Query *root, RelOptInfo *rel)
|
||||
restrictlist_selectivity(root,
|
||||
rel->baserestrictinfo,
|
||||
lfirsti(rel->relids));
|
||||
|
||||
/*
|
||||
* Force estimate to be at least one row, to make explain output look
|
||||
* better and to avoid possible divide-by-zero when interpolating cost.
|
||||
* better and to avoid possible divide-by-zero when interpolating
|
||||
* cost.
|
||||
*/
|
||||
if (rel->rows < 1.0)
|
||||
rel->rows = 1.0;
|
||||
@@ -812,10 +828,10 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
|
||||
temp = outer_rel->rows * inner_rel->rows;
|
||||
|
||||
/*
|
||||
* Apply join restrictivity. Note that we are only considering clauses
|
||||
* that become restriction clauses at this join level; we are not
|
||||
* double-counting them because they were not considered in estimating
|
||||
* the sizes of the component rels.
|
||||
* Apply join restrictivity. Note that we are only considering
|
||||
* clauses that become restriction clauses at this join level; we are
|
||||
* not double-counting them because they were not considered in
|
||||
* estimating the sizes of the component rels.
|
||||
*/
|
||||
temp *= restrictlist_selectivity(root,
|
||||
restrictlist,
|
||||
@@ -823,7 +839,8 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
|
||||
|
||||
/*
|
||||
* Force estimate to be at least one row, to make explain output look
|
||||
* better and to avoid possible divide-by-zero when interpolating cost.
|
||||
* better and to avoid possible divide-by-zero when interpolating
|
||||
* cost.
|
||||
*/
|
||||
if (temp < 1.0)
|
||||
temp = 1.0;
|
||||
@@ -833,8 +850,8 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
|
||||
* We could apply set_rel_width() to compute the output tuple width
|
||||
* from scratch, but at present it's always just the sum of the input
|
||||
* widths, so why work harder than necessary? If relnode.c is ever
|
||||
* taught to remove unneeded columns from join targetlists, go back
|
||||
* to using set_rel_width here.
|
||||
* taught to remove unneeded columns from join targetlists, go back to
|
||||
* using set_rel_width here.
|
||||
*/
|
||||
rel->width = outer_rel->width + inner_rel->width;
|
||||
}
|
||||
@@ -859,7 +876,7 @@ set_rel_width(Query *root, RelOptInfo *rel)
|
||||
* compute_attribute_width
|
||||
* Given a target list entry, find the size in bytes of the attribute.
|
||||
*
|
||||
* If a field is variable-length, we make a default assumption. Would be
|
||||
* If a field is variable-length, we make a default assumption. Would be
|
||||
* better if VACUUM recorded some stats about the average field width...
|
||||
* also, we have access to the atttypmod, but fail to use it...
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.81 2000/03/22 22:08:33 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.82 2000/04/12 17:15:19 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -46,62 +46,63 @@
|
||||
#define is_indexable_operator(clause,opclass,relam,indexkey_on_left) \
|
||||
(indexable_operator(clause,opclass,relam,indexkey_on_left) != InvalidOid)
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
Prefix_None, Prefix_Partial, Prefix_Exact
|
||||
} Prefix_Status;
|
||||
|
||||
static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *restrictinfo_list);
|
||||
List *restrictinfo_list);
|
||||
static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *or_clauses,
|
||||
List *other_matching_indices);
|
||||
List *or_clauses,
|
||||
List *other_matching_indices);
|
||||
static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
Expr *clause);
|
||||
IndexOptInfo *index,
|
||||
Expr *clause);
|
||||
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index,
|
||||
int *indexkeys, Oid *classes,
|
||||
List *restrictinfo_list);
|
||||
int *indexkeys, Oid *classes,
|
||||
List *restrictinfo_list);
|
||||
static List *group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
int *indexkeys, Oid *classes,
|
||||
List *join_cinfo_list,
|
||||
List *restr_cinfo_list);
|
||||
IndexOptInfo *index,
|
||||
int *indexkeys, Oid *classes,
|
||||
List *join_cinfo_list,
|
||||
List *restr_cinfo_list);
|
||||
static bool match_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index,
|
||||
int indexkey, Oid opclass,
|
||||
Expr *clause, bool join);
|
||||
int indexkey, Oid opclass,
|
||||
Expr *clause, bool join);
|
||||
static bool pred_test(List *predicate_list, List *restrictinfo_list,
|
||||
List *joininfo_list);
|
||||
List *joininfo_list);
|
||||
static bool one_pred_test(Expr *predicate, List *restrictinfo_list);
|
||||
static bool one_pred_clause_expr_test(Expr *predicate, Node *clause);
|
||||
static bool one_pred_clause_test(Expr *predicate, Node *clause);
|
||||
static bool clause_pred_clause_test(Expr *predicate, Node *clause);
|
||||
static void indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *joininfo_list, List *restrictinfo_list,
|
||||
List **clausegroups, List **outerrelids);
|
||||
List *joininfo_list, List *restrictinfo_list,
|
||||
List **clausegroups, List **outerrelids);
|
||||
static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *clausegroup_list, List *outerrelids_list);
|
||||
List *clausegroup_list, List *outerrelids_list);
|
||||
static bool useful_for_mergejoin(RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *joininfo_list);
|
||||
List *joininfo_list);
|
||||
static bool useful_for_ordering(Query *root, RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
ScanDirection scandir);
|
||||
IndexOptInfo *index,
|
||||
ScanDirection scandir);
|
||||
static bool match_index_to_operand(int indexkey, Var *operand,
|
||||
RelOptInfo *rel, IndexOptInfo *index);
|
||||
RelOptInfo *rel, IndexOptInfo *index);
|
||||
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
|
||||
IndexOptInfo *index);
|
||||
IndexOptInfo *index);
|
||||
static bool match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
bool indexkey_on_left);
|
||||
bool indexkey_on_left);
|
||||
static Prefix_Status like_fixed_prefix(char *patt, char **prefix);
|
||||
static Prefix_Status regex_fixed_prefix(char *patt, bool case_insensitive,
|
||||
char **prefix);
|
||||
char **prefix);
|
||||
static List *prefix_quals(Var *leftop, Oid expr_op,
|
||||
char *prefix, Prefix_Status pstatus);
|
||||
static char *make_greater_string(const char * str, Oid datatype);
|
||||
static Oid find_operator(const char * opname, Oid datatype);
|
||||
static Datum string_to_datum(const char * str, Oid datatype);
|
||||
static Const *string_to_const(const char * str, Oid datatype);
|
||||
static bool string_lessthan(const char * str1, const char * str2,
|
||||
Oid datatype);
|
||||
char *prefix, Prefix_Status pstatus);
|
||||
static char *make_greater_string(const char *str, Oid datatype);
|
||||
static Oid find_operator(const char *opname, Oid datatype);
|
||||
static Datum string_to_datum(const char *str, Oid datatype);
|
||||
static Const *string_to_const(const char *str, Oid datatype);
|
||||
static bool string_lessthan(const char *str1, const char *str2,
|
||||
Oid datatype);
|
||||
|
||||
|
||||
/*
|
||||
@@ -153,34 +154,34 @@ create_index_paths(Query *root,
|
||||
List *joinouterrelids;
|
||||
|
||||
/*
|
||||
* If this is a partial index, we can only use it if it passes
|
||||
* the predicate test.
|
||||
* If this is a partial index, we can only use it if it passes the
|
||||
* predicate test.
|
||||
*/
|
||||
if (index->indpred != NIL)
|
||||
if (!pred_test(index->indpred, restrictinfo_list, joininfo_list))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* 1. Try matching the index against subclauses of restriction 'or'
|
||||
* clauses (ie, 'or' clauses that reference only this relation).
|
||||
* The restrictinfo nodes for the 'or' clauses are marked with lists
|
||||
* of the matching indices. No paths are actually created now;
|
||||
* that will be done in orindxpath.c after all indexes for the rel
|
||||
* have been examined. (We need to do it that way because we can
|
||||
* potentially use a different index for each subclause of an 'or',
|
||||
* so we can't build a path for an 'or' clause until all indexes have
|
||||
* been matched against it.)
|
||||
* 1. Try matching the index against subclauses of restriction
|
||||
* 'or' clauses (ie, 'or' clauses that reference only this
|
||||
* relation). The restrictinfo nodes for the 'or' clauses are
|
||||
* marked with lists of the matching indices. No paths are
|
||||
* actually created now; that will be done in orindxpath.c after
|
||||
* all indexes for the rel have been examined. (We need to do it
|
||||
* that way because we can potentially use a different index for
|
||||
* each subclause of an 'or', so we can't build a path for an 'or'
|
||||
* clause until all indexes have been matched against it.)
|
||||
*
|
||||
* We don't even think about special handling of 'or' clauses that
|
||||
* involve more than one relation (ie, are join clauses).
|
||||
* Can we do anything useful with those?
|
||||
* involve more than one relation (ie, are join clauses). Can we
|
||||
* do anything useful with those?
|
||||
*/
|
||||
match_index_orclauses(rel, index, restrictinfo_list);
|
||||
|
||||
/*
|
||||
* 2. If the keys of this index match any of the available non-'or'
|
||||
* restriction clauses, then create a path using those clauses
|
||||
* as indexquals.
|
||||
* 2. If the keys of this index match any of the available
|
||||
* non-'or' restriction clauses, then create a path using those
|
||||
* clauses as indexquals.
|
||||
*/
|
||||
restrictclauses = group_clauses_by_indexkey(rel,
|
||||
index,
|
||||
@@ -191,7 +192,7 @@ create_index_paths(Query *root,
|
||||
if (restrictclauses != NIL)
|
||||
add_path(rel, (Path *) create_index_path(root, rel, index,
|
||||
restrictclauses,
|
||||
NoMovementScanDirection));
|
||||
NoMovementScanDirection));
|
||||
|
||||
/*
|
||||
* 3. If this index can be used for a mergejoin, then create an
|
||||
@@ -205,16 +206,17 @@ create_index_paths(Query *root,
|
||||
if (restrictclauses == NIL)
|
||||
{
|
||||
if (useful_for_mergejoin(rel, index, joininfo_list) ||
|
||||
useful_for_ordering(root, rel, index, ForwardScanDirection))
|
||||
useful_for_ordering(root, rel, index, ForwardScanDirection))
|
||||
add_path(rel, (Path *)
|
||||
create_index_path(root, rel, index,
|
||||
NIL,
|
||||
ForwardScanDirection));
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently, backwards scan is never considered except for the case
|
||||
* of matching a query result ordering. Possibly should consider
|
||||
* it in other places?
|
||||
* Currently, backwards scan is never considered except for the
|
||||
* case of matching a query result ordering. Possibly should
|
||||
* consider it in other places?
|
||||
*/
|
||||
if (useful_for_ordering(root, rel, index, BackwardScanDirection))
|
||||
add_path(rel, (Path *)
|
||||
@@ -223,11 +225,11 @@ create_index_paths(Query *root,
|
||||
BackwardScanDirection));
|
||||
|
||||
/*
|
||||
* 4. Create an innerjoin index path for each combination of
|
||||
* other rels used in available join clauses. These paths will
|
||||
* be considered as the inner side of nestloop joins against
|
||||
* those sets of other rels. indexable_joinclauses() finds sets
|
||||
* of clauses that can be used with each combination of outer rels,
|
||||
* 4. Create an innerjoin index path for each combination of other
|
||||
* rels used in available join clauses. These paths will be
|
||||
* considered as the inner side of nestloop joins against those
|
||||
* sets of other rels. indexable_joinclauses() finds sets of
|
||||
* clauses that can be used with each combination of outer rels,
|
||||
* and index_innerjoin builds the paths themselves. We add the
|
||||
* paths to the rel's innerjoin list, NOT to the result list.
|
||||
*/
|
||||
@@ -247,7 +249,7 @@ create_index_paths(Query *root,
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* ---- ROUTINES TO PROCESS 'OR' CLAUSES ----
|
||||
* ---- ROUTINES TO PROCESS 'OR' CLAUSES ----
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
@@ -280,6 +282,7 @@ match_index_orclauses(RelOptInfo *rel,
|
||||
|
||||
if (restriction_is_or_clause(restrictinfo))
|
||||
{
|
||||
|
||||
/*
|
||||
* Add this index to the subclause index list for each
|
||||
* subclause that it matches.
|
||||
@@ -309,7 +312,7 @@ match_index_orclauses(RelOptInfo *rel,
|
||||
* that have already been matched to subclauses within this
|
||||
* particular 'or' clause (i.e., a list previously generated by
|
||||
* this routine), or NIL if this routine has not previously been
|
||||
* run for this 'or' clause.
|
||||
* run for this 'or' clause.
|
||||
*
|
||||
* Returns a list of the form ((a b c) (d e f) nil (g h) ...) where
|
||||
* a,b,c are nodes of indices that match the first subclause in
|
||||
@@ -326,7 +329,8 @@ match_index_orclause(RelOptInfo *rel,
|
||||
List *index_list;
|
||||
List *clist;
|
||||
|
||||
/* first time through, we create list of same length as OR clause,
|
||||
/*
|
||||
* first time through, we create list of same length as OR clause,
|
||||
* containing an empty sublist for each subclause.
|
||||
*/
|
||||
if (!other_matching_indices)
|
||||
@@ -374,8 +378,8 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
Expr *clause)
|
||||
{
|
||||
int indexkey = index->indexkeys[0];
|
||||
Oid opclass = index->classlist[0];
|
||||
int indexkey = index->indexkeys[0];
|
||||
Oid opclass = index->classlist[0];
|
||||
|
||||
if (and_clause((Node *) clause))
|
||||
{
|
||||
@@ -400,10 +404,10 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||
* used as indexquals.
|
||||
*
|
||||
* In the simplest case this just means making a one-element list of the
|
||||
* given opclause. However, if the OR subclause is an AND, we have to
|
||||
* given opclause. However, if the OR subclause is an AND, we have to
|
||||
* scan it to find the opclause(s) that match the index. (There should
|
||||
* be at least one, if match_or_subclause_to_indexkey succeeded, but there
|
||||
* could be more.) Also, we apply expand_indexqual_conditions() to convert
|
||||
* could be more.) Also, we apply expand_indexqual_conditions() to convert
|
||||
* any special matching opclauses to indexable operators.
|
||||
*
|
||||
* The passed-in clause is not changed.
|
||||
@@ -413,9 +417,9 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
Expr *orsubclause)
|
||||
{
|
||||
List *quals = NIL;
|
||||
int indexkey = index->indexkeys[0];
|
||||
Oid opclass = index->classlist[0];
|
||||
List *quals = NIL;
|
||||
int indexkey = index->indexkeys[0];
|
||||
Oid opclass = index->classlist[0];
|
||||
|
||||
if (and_clause((Node *) orsubclause))
|
||||
{
|
||||
@@ -514,8 +518,9 @@ group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
clausegroup = lappend(clausegroup, rinfo);
|
||||
}
|
||||
|
||||
/* If no clauses match this key, we're done; we don't want to
|
||||
* look at keys to its right.
|
||||
/*
|
||||
* If no clauses match this key, we're done; we don't want to look
|
||||
* at keys to its right.
|
||||
*/
|
||||
if (clausegroup == NIL)
|
||||
break;
|
||||
@@ -533,7 +538,7 @@ group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
|
||||
/*
|
||||
* group_clauses_by_ikey_for_joins
|
||||
* Generates a list of join clauses that can be used with an index
|
||||
* Generates a list of join clauses that can be used with an index
|
||||
* to scan the inner side of a nestloop join.
|
||||
*
|
||||
* This is much like group_clauses_by_indexkey(), but we consider both
|
||||
@@ -593,8 +598,9 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
clausegroup = lappend(clausegroup, rinfo);
|
||||
}
|
||||
|
||||
/* If no clauses match this key, we're done; we don't want to
|
||||
* look at keys to its right.
|
||||
/*
|
||||
* If no clauses match this key, we're done; we don't want to look
|
||||
* at keys to its right.
|
||||
*/
|
||||
if (clausegroup == NIL)
|
||||
break;
|
||||
@@ -607,8 +613,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
} while (!DoneMatchingIndexKeys(indexkeys, index));
|
||||
|
||||
/*
|
||||
* if no join clause was matched then there ain't clauses for
|
||||
* joins at all.
|
||||
* if no join clause was matched then there ain't clauses for joins at
|
||||
* all.
|
||||
*/
|
||||
if (!jfound)
|
||||
{
|
||||
@@ -623,8 +629,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
|
||||
/*
|
||||
* match_clause_to_indexkey()
|
||||
* Determines whether a restriction or join clause matches
|
||||
* a key of an index.
|
||||
* Determines whether a restriction or join clause matches
|
||||
* a key of an index.
|
||||
*
|
||||
* To match, the clause:
|
||||
|
||||
@@ -673,43 +679,46 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
*rightop;
|
||||
|
||||
/* Clause must be a binary opclause. */
|
||||
if (! is_opclause((Node *) clause))
|
||||
if (!is_opclause((Node *) clause))
|
||||
return false;
|
||||
leftop = get_leftop(clause);
|
||||
rightop = get_rightop(clause);
|
||||
if (! leftop || ! rightop)
|
||||
if (!leftop || !rightop)
|
||||
return false;
|
||||
|
||||
if (!join)
|
||||
{
|
||||
|
||||
/*
|
||||
* Not considering joins, so check for clauses of the form:
|
||||
* (indexkey operator constant) or (constant operator indexkey).
|
||||
* We will accept a Param as being constant.
|
||||
*/
|
||||
|
||||
if ((IsA(rightop, Const) || IsA(rightop, Param)) &&
|
||||
if ((IsA(rightop, Const) ||IsA(rightop, Param)) &&
|
||||
match_index_to_operand(indexkey, leftop, rel, index))
|
||||
{
|
||||
if (is_indexable_operator(clause, opclass, index->relam, true))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If we didn't find a member of the index's opclass,
|
||||
* see whether it is a "special" indexable operator.
|
||||
* If we didn't find a member of the index's opclass, see
|
||||
* whether it is a "special" indexable operator.
|
||||
*/
|
||||
if (match_special_index_operator(clause, opclass, index->relam,
|
||||
true))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if ((IsA(leftop, Const) || IsA(leftop, Param)) &&
|
||||
if ((IsA(leftop, Const) ||IsA(leftop, Param)) &&
|
||||
match_index_to_operand(indexkey, rightop, rel, index))
|
||||
{
|
||||
if (is_indexable_operator(clause, opclass, index->relam, false))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If we didn't find a member of the index's opclass,
|
||||
* see whether it is a "special" indexable operator.
|
||||
* If we didn't find a member of the index's opclass, see
|
||||
* whether it is a "special" indexable operator.
|
||||
*/
|
||||
if (match_special_index_operator(clause, opclass, index->relam,
|
||||
false))
|
||||
@@ -719,20 +728,21 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* Check for an indexqual that could be handled by a nestloop join.
|
||||
* We need the index key to be compared against an expression
|
||||
* that uses none of the indexed relation's vars.
|
||||
* Check for an indexqual that could be handled by a nestloop
|
||||
* join. We need the index key to be compared against an
|
||||
* expression that uses none of the indexed relation's vars.
|
||||
*/
|
||||
if (match_index_to_operand(indexkey, leftop, rel, index))
|
||||
{
|
||||
List *othervarnos = pull_varnos((Node *) rightop);
|
||||
bool isIndexable;
|
||||
|
||||
isIndexable = ! intMember(lfirsti(rel->relids), othervarnos);
|
||||
isIndexable = !intMember(lfirsti(rel->relids), othervarnos);
|
||||
freeList(othervarnos);
|
||||
if (isIndexable &&
|
||||
is_indexable_operator(clause, opclass, index->relam, true))
|
||||
is_indexable_operator(clause, opclass, index->relam, true))
|
||||
return true;
|
||||
}
|
||||
else if (match_index_to_operand(indexkey, rightop, rel, index))
|
||||
@@ -740,10 +750,10 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
List *othervarnos = pull_varnos((Node *) leftop);
|
||||
bool isIndexable;
|
||||
|
||||
isIndexable = ! intMember(lfirsti(rel->relids), othervarnos);
|
||||
isIndexable = !intMember(lfirsti(rel->relids), othervarnos);
|
||||
freeList(othervarnos);
|
||||
if (isIndexable &&
|
||||
is_indexable_operator(clause, opclass, index->relam, false))
|
||||
is_indexable_operator(clause, opclass, index->relam, false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -768,7 +778,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
*
|
||||
* Returns the OID of the matching operator, or InvalidOid if no match.
|
||||
* Note that the returned OID will be different from the one in the given
|
||||
* expression if we used a binary-compatible substitution. Also note that
|
||||
* expression if we used a binary-compatible substitution. Also note that
|
||||
* if indexkey_on_left is FALSE (meaning we need to commute), the returned
|
||||
* OID is *not* commuted; it can be plugged directly into the given clause.
|
||||
*/
|
||||
@@ -818,13 +828,14 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
|
||||
if (HeapTupleIsValid(newop))
|
||||
{
|
||||
Oid new_expr_op = oprid(newop);
|
||||
Oid new_expr_op = oprid(newop);
|
||||
|
||||
if (new_expr_op != expr_op)
|
||||
{
|
||||
|
||||
/*
|
||||
* OK, we found a binary-compatible operator of the same name;
|
||||
* now does it match the index?
|
||||
* OK, we found a binary-compatible operator of the same
|
||||
* name; now does it match the index?
|
||||
*/
|
||||
if (indexkey_on_left)
|
||||
commuted_op = new_expr_op;
|
||||
@@ -883,12 +894,12 @@ useful_for_mergejoin(RelOptInfo *rel,
|
||||
{
|
||||
if (restrictinfo->left_sortop == ordering[0] &&
|
||||
match_index_to_operand(indexkeys[0],
|
||||
get_leftop(restrictinfo->clause),
|
||||
get_leftop(restrictinfo->clause),
|
||||
rel, index))
|
||||
return true;
|
||||
if (restrictinfo->right_sortop == ordering[0] &&
|
||||
match_index_to_operand(indexkeys[0],
|
||||
get_rightop(restrictinfo->clause),
|
||||
get_rightop(restrictinfo->clause),
|
||||
rel, index))
|
||||
return true;
|
||||
}
|
||||
@@ -1127,7 +1138,7 @@ one_pred_clause_test(Expr *predicate, Node *clause)
|
||||
*/
|
||||
|
||||
static StrategyNumber
|
||||
BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
|
||||
BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
|
||||
{2, 2, 0, 0, 0},
|
||||
{1, 2, 0, 0, 0},
|
||||
{1, 2, 3, 4, 5},
|
||||
@@ -1346,13 +1357,13 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
* rel's restrictinfo list. Therefore, every clause in the group references
|
||||
* the current rel plus the same set of other rels (except for the restrict
|
||||
* clauses, which only reference the current rel). Therefore, this set
|
||||
* of clauses could be used as an indexqual if the relation is scanned
|
||||
* of clauses could be used as an indexqual if the relation is scanned
|
||||
* as the inner side of a nestloop join when the outer side contains
|
||||
* (at least) all those "other rels".
|
||||
*
|
||||
* XXX Actually, given that we are considering a join that requires an
|
||||
* outer rel set (A,B,C), we should use all qual clauses that reference
|
||||
* any subset of these rels, not just the full set or none. This is
|
||||
* any subset of these rels, not just the full set or none. This is
|
||||
* doable with a doubly nested loop over joininfo_list; is it worth it?
|
||||
*
|
||||
* Returns two parallel lists of the same length: the clause groups,
|
||||
@@ -1430,10 +1441,11 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
|
||||
/*
|
||||
* There's no point in marking the path with any pathkeys, since
|
||||
* it will only ever be used as the inner path of a nestloop,
|
||||
* and so its ordering does not matter.
|
||||
* it will only ever be used as the inner path of a nestloop, and
|
||||
* so its ordering does not matter.
|
||||
*/
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
@@ -1441,7 +1453,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
/* expand special operators to indexquals the executor can handle */
|
||||
indexquals = expand_indexqual_conditions(indexquals);
|
||||
|
||||
/* 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, both indexid and indexqual should be single-element
|
||||
* lists.
|
||||
*/
|
||||
@@ -1456,14 +1469,15 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
|
||||
/*
|
||||
* We must compute the estimated number of output rows for the
|
||||
* indexscan. This is less than rel->rows because of the additional
|
||||
* selectivity of the join clauses. Since clausegroup may contain
|
||||
* both restriction and join clauses, we have to do a set union to
|
||||
* get the full set of clauses that must be considered to compute
|
||||
* the correct selectivity. (We can't just nconc the two lists;
|
||||
* then we might have some restriction clauses appearing twice,
|
||||
* which'd mislead restrictlist_selectivity into double-counting
|
||||
* their selectivity.)
|
||||
* indexscan. This is less than rel->rows because of the
|
||||
* additional selectivity of the join clauses. Since clausegroup
|
||||
* may contain both restriction and join clauses, we have to do a
|
||||
* set union to get the full set of clauses that must be
|
||||
* considered to compute the correct selectivity. (We can't just
|
||||
* nconc the two lists; then we might have some restriction
|
||||
* clauses appearing twice, which'd mislead
|
||||
* restrictlist_selectivity into double-counting their
|
||||
* selectivity.)
|
||||
*/
|
||||
pathnode->rows = rel->tuples *
|
||||
restrictlist_selectivity(root,
|
||||
@@ -1490,7 +1504,7 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
* match_index_to_operand()
|
||||
* Generalized test for a match between an index's key
|
||||
* and the operand on one side of a restriction or join clause.
|
||||
* Now check for functional indices as well.
|
||||
* Now check for functional indices as well.
|
||||
*/
|
||||
static bool
|
||||
match_index_to_operand(int indexkey,
|
||||
@@ -1500,6 +1514,7 @@ match_index_to_operand(int indexkey,
|
||||
{
|
||||
if (index->indproc == InvalidOid)
|
||||
{
|
||||
|
||||
/*
|
||||
* Normal index.
|
||||
*/
|
||||
@@ -1530,7 +1545,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
|
||||
/*
|
||||
* sanity check, make sure we know what we're dealing with here.
|
||||
*/
|
||||
if (funcOpnd == NULL || ! IsA(funcOpnd, Expr) ||
|
||||
if (funcOpnd == NULL || !IsA(funcOpnd, Expr) ||
|
||||
funcOpnd->opType != FUNC_EXPR ||
|
||||
funcOpnd->oper == NULL || indexKeys == NULL)
|
||||
return false;
|
||||
@@ -1550,9 +1565,9 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
|
||||
i = 0;
|
||||
foreach(arg, funcargs)
|
||||
{
|
||||
Var *var = (Var *) lfirst(arg);
|
||||
Var *var = (Var *) lfirst(arg);
|
||||
|
||||
if (! IsA(var, Var))
|
||||
if (!IsA(var, Var))
|
||||
return false;
|
||||
if (indexKeys[i] == 0)
|
||||
return false;
|
||||
@@ -1578,10 +1593,10 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
|
||||
* indexscan machinery. The key idea is that these operators allow us
|
||||
* to derive approximate indexscan qual clauses, such that any tuples
|
||||
* that pass the operator clause itself must also satisfy the simpler
|
||||
* indexscan condition(s). Then we can use the indexscan machinery
|
||||
* indexscan condition(s). Then we can use the indexscan machinery
|
||||
* to avoid scanning as much of the table as we'd otherwise have to,
|
||||
* while applying the original operator as a qpqual condition to ensure
|
||||
* we deliver only the tuples we want. (In essence, we're using a regular
|
||||
* we deliver only the tuples we want. (In essence, we're using a regular
|
||||
* index as if it were a lossy index.)
|
||||
*
|
||||
* An example of what we're doing is
|
||||
@@ -1630,11 +1645,12 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
char *patt;
|
||||
char *prefix;
|
||||
|
||||
/* Currently, all known special operators require the indexkey
|
||||
* on the left, but this test could be pushed into the switch statement
|
||||
* if some are added that do not...
|
||||
/*
|
||||
* Currently, all known special operators require the indexkey on the
|
||||
* left, but this test could be pushed into the switch statement if
|
||||
* some are added that do not...
|
||||
*/
|
||||
if (! indexkey_on_left)
|
||||
if (!indexkey_on_left)
|
||||
return false;
|
||||
|
||||
/* we know these will succeed */
|
||||
@@ -1643,7 +1659,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
expr_op = ((Oper *) clause->oper)->opno;
|
||||
|
||||
/* again, required for all current special ops: */
|
||||
if (! IsA(rightop, Const) ||
|
||||
if (!IsA(rightop, Const) ||
|
||||
((Const *) rightop)->constisnull)
|
||||
return false;
|
||||
constvalue = ((Const *) rightop)->constvalue;
|
||||
@@ -1657,7 +1673,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
/* the right-hand const is type text for all of these */
|
||||
patt = textout((text *) DatumGetPointer(constvalue));
|
||||
isIndexable = like_fixed_prefix(patt, &prefix) != Prefix_None;
|
||||
if (prefix) pfree(prefix);
|
||||
if (prefix)
|
||||
pfree(prefix);
|
||||
pfree(patt);
|
||||
break;
|
||||
|
||||
@@ -1668,7 +1685,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
/* the right-hand const is type text for all of these */
|
||||
patt = textout((text *) DatumGetPointer(constvalue));
|
||||
isIndexable = regex_fixed_prefix(patt, false, &prefix) != Prefix_None;
|
||||
if (prefix) pfree(prefix);
|
||||
if (prefix)
|
||||
pfree(prefix);
|
||||
pfree(patt);
|
||||
break;
|
||||
|
||||
@@ -1679,13 +1697,14 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
/* the right-hand const is type text for all of these */
|
||||
patt = textout((text *) DatumGetPointer(constvalue));
|
||||
isIndexable = regex_fixed_prefix(patt, true, &prefix) != Prefix_None;
|
||||
if (prefix) pfree(prefix);
|
||||
if (prefix)
|
||||
pfree(prefix);
|
||||
pfree(patt);
|
||||
break;
|
||||
}
|
||||
|
||||
/* done if the expression doesn't look indexable */
|
||||
if (! isIndexable)
|
||||
if (!isIndexable)
|
||||
return false;
|
||||
|
||||
/*
|
||||
@@ -1699,32 +1718,32 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
case OID_TEXT_LIKE_OP:
|
||||
case OID_TEXT_REGEXEQ_OP:
|
||||
case OID_TEXT_ICREGEXEQ_OP:
|
||||
if (! op_class(find_operator(">=", TEXTOID), opclass, relam) ||
|
||||
! op_class(find_operator("<", TEXTOID), opclass, relam))
|
||||
if (!op_class(find_operator(">=", TEXTOID), opclass, relam) ||
|
||||
!op_class(find_operator("<", TEXTOID), opclass, relam))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
case OID_BPCHAR_LIKE_OP:
|
||||
case OID_BPCHAR_REGEXEQ_OP:
|
||||
case OID_BPCHAR_ICREGEXEQ_OP:
|
||||
if (! op_class(find_operator(">=", BPCHAROID), opclass, relam) ||
|
||||
! op_class(find_operator("<", BPCHAROID), opclass, relam))
|
||||
if (!op_class(find_operator(">=", BPCHAROID), opclass, relam) ||
|
||||
!op_class(find_operator("<", BPCHAROID), opclass, relam))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
case OID_VARCHAR_LIKE_OP:
|
||||
case OID_VARCHAR_REGEXEQ_OP:
|
||||
case OID_VARCHAR_ICREGEXEQ_OP:
|
||||
if (! op_class(find_operator(">=", VARCHAROID), opclass, relam) ||
|
||||
! op_class(find_operator("<", VARCHAROID), opclass, relam))
|
||||
if (!op_class(find_operator(">=", VARCHAROID), opclass, relam) ||
|
||||
!op_class(find_operator("<", VARCHAROID), opclass, relam))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
case OID_NAME_LIKE_OP:
|
||||
case OID_NAME_REGEXEQ_OP:
|
||||
case OID_NAME_ICREGEXEQ_OP:
|
||||
if (! op_class(find_operator(">=", NAMEOID), opclass, relam) ||
|
||||
! op_class(find_operator("<", NAMEOID), opclass, relam))
|
||||
if (!op_class(find_operator(">=", NAMEOID), opclass, relam) ||
|
||||
!op_class(find_operator("<", NAMEOID), opclass, relam))
|
||||
isIndexable = false;
|
||||
break;
|
||||
}
|
||||
@@ -1736,7 +1755,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
* expand_indexqual_conditions
|
||||
* Given a list of (implicitly ANDed) indexqual clauses,
|
||||
* expand any "special" index operators into clauses that the indexscan
|
||||
* machinery will know what to do with. Clauses that were not
|
||||
* machinery will know what to do with. Clauses that were not
|
||||
* recognized by match_special_index_operator() must be passed through
|
||||
* unchanged.
|
||||
*/
|
||||
@@ -1749,6 +1768,7 @@ expand_indexqual_conditions(List *indexquals)
|
||||
foreach(q, indexquals)
|
||||
{
|
||||
Expr *clause = (Expr *) lfirst(q);
|
||||
|
||||
/* we know these will succeed */
|
||||
Var *leftop = get_leftop(clause);
|
||||
Var *rightop = get_rightop(clause);
|
||||
@@ -1760,11 +1780,13 @@ expand_indexqual_conditions(List *indexquals)
|
||||
|
||||
switch (expr_op)
|
||||
{
|
||||
/*
|
||||
* LIKE and regex operators are not members of any index opclass,
|
||||
* so if we find one in an indexqual list we can assume that
|
||||
* it was accepted by match_special_index_operator().
|
||||
*/
|
||||
|
||||
/*
|
||||
* LIKE and regex operators are not members of any index
|
||||
* opclass, so if we find one in an indexqual list we can
|
||||
* assume that it was accepted by
|
||||
* match_special_index_operator().
|
||||
*/
|
||||
case OID_TEXT_LIKE_OP:
|
||||
case OID_BPCHAR_LIKE_OP:
|
||||
case OID_VARCHAR_LIKE_OP:
|
||||
@@ -1776,7 +1798,8 @@ expand_indexqual_conditions(List *indexquals)
|
||||
resultquals = nconc(resultquals,
|
||||
prefix_quals(leftop, expr_op,
|
||||
prefix, pstatus));
|
||||
if (prefix) pfree(prefix);
|
||||
if (prefix)
|
||||
pfree(prefix);
|
||||
pfree(patt);
|
||||
break;
|
||||
|
||||
@@ -1791,7 +1814,8 @@ expand_indexqual_conditions(List *indexquals)
|
||||
resultquals = nconc(resultquals,
|
||||
prefix_quals(leftop, expr_op,
|
||||
prefix, pstatus));
|
||||
if (prefix) pfree(prefix);
|
||||
if (prefix)
|
||||
pfree(prefix);
|
||||
pfree(patt);
|
||||
break;
|
||||
|
||||
@@ -1806,7 +1830,8 @@ expand_indexqual_conditions(List *indexquals)
|
||||
resultquals = nconc(resultquals,
|
||||
prefix_quals(leftop, expr_op,
|
||||
prefix, pstatus));
|
||||
if (prefix) pfree(prefix);
|
||||
if (prefix)
|
||||
pfree(prefix);
|
||||
pfree(patt);
|
||||
break;
|
||||
|
||||
@@ -1833,7 +1858,7 @@ like_fixed_prefix(char *patt, char **prefix)
|
||||
int pos,
|
||||
match_pos;
|
||||
|
||||
*prefix = match = palloc(strlen(patt)+1);
|
||||
*prefix = match = palloc(strlen(patt) + 1);
|
||||
match_pos = 0;
|
||||
|
||||
for (pos = 0; patt[pos]; pos++)
|
||||
@@ -1849,14 +1874,15 @@ like_fixed_prefix(char *patt, char **prefix)
|
||||
if (patt[pos] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: this code used to think that %% meant a literal %,
|
||||
* but textlike() itself does not think that, and the SQL92
|
||||
* spec doesn't say any such thing either.
|
||||
* NOTE: this code used to think that %% meant a literal %, but
|
||||
* textlike() itself does not think that, and the SQL92 spec
|
||||
* doesn't say any such thing either.
|
||||
*/
|
||||
match[match_pos++] = patt[pos];
|
||||
}
|
||||
|
||||
|
||||
match[match_pos] = '\0';
|
||||
|
||||
/* in LIKE, an empty pattern is an exact match! */
|
||||
@@ -1905,7 +1931,7 @@ regex_fixed_prefix(char *patt, bool case_insensitive,
|
||||
}
|
||||
|
||||
/* OK, allocate space for pattern */
|
||||
*prefix = match = palloc(strlen(patt)+1);
|
||||
*prefix = match = palloc(strlen(patt) + 1);
|
||||
match_pos = 0;
|
||||
|
||||
/* note start at pos 1 to skip leading ^ */
|
||||
@@ -1916,9 +1942,11 @@ regex_fixed_prefix(char *patt, bool case_insensitive,
|
||||
patt[pos] == '*' ||
|
||||
patt[pos] == '[' ||
|
||||
patt[pos] == '$' ||
|
||||
/* XXX I suspect isalpha() is not an adequately locale-sensitive
|
||||
* test for characters that can vary under case folding?
|
||||
*/
|
||||
|
||||
/*
|
||||
* XXX I suspect isalpha() is not an adequately locale-sensitive
|
||||
* test for characters that can vary under case folding?
|
||||
*/
|
||||
(case_insensitive && isalpha(patt[pos])))
|
||||
break;
|
||||
if (patt[pos] == '\\')
|
||||
@@ -1932,9 +1960,9 @@ regex_fixed_prefix(char *patt, bool case_insensitive,
|
||||
|
||||
match[match_pos] = '\0';
|
||||
|
||||
if (patt[pos] == '$' && patt[pos+1] == '\0')
|
||||
if (patt[pos] == '$' && patt[pos + 1] == '\0')
|
||||
return Prefix_Exact; /* pattern specifies exact match */
|
||||
|
||||
|
||||
if (match_pos > 0)
|
||||
return Prefix_Partial;
|
||||
return Prefix_None;
|
||||
@@ -2020,7 +2048,8 @@ prefix_quals(Var *leftop, Oid expr_op,
|
||||
result = lcons(expr, NIL);
|
||||
|
||||
/*
|
||||
* If we can create a string larger than the prefix, say "x < greaterstr".
|
||||
* If we can create a string larger than the prefix, say "x <
|
||||
* greaterstr".
|
||||
*/
|
||||
greaterstr = make_greater_string(prefix, datatype);
|
||||
if (greaterstr)
|
||||
@@ -2058,17 +2087,20 @@ prefix_quals(Var *leftop, Oid expr_op,
|
||||
* given "foos" and return "foot", will this actually be greater than "fooss"?
|
||||
*/
|
||||
static char *
|
||||
make_greater_string(const char * str, Oid datatype)
|
||||
make_greater_string(const char *str, Oid datatype)
|
||||
{
|
||||
char *workstr;
|
||||
int len;
|
||||
|
||||
/* Make a modifiable copy, which will be our return value if successful */
|
||||
/*
|
||||
* Make a modifiable copy, which will be our return value if
|
||||
* successful
|
||||
*/
|
||||
workstr = pstrdup((char *) str);
|
||||
|
||||
while ((len = strlen(workstr)) > 0)
|
||||
{
|
||||
unsigned char *lastchar = (unsigned char *) (workstr + len - 1);
|
||||
unsigned char *lastchar = (unsigned char *) (workstr + len - 1);
|
||||
|
||||
/*
|
||||
* Try to generate a larger string by incrementing the last byte.
|
||||
@@ -2077,14 +2109,15 @@ make_greater_string(const char * str, Oid datatype)
|
||||
{
|
||||
(*lastchar)++;
|
||||
if (string_lessthan(str, workstr, datatype))
|
||||
return workstr; /* Success! */
|
||||
return workstr; /* Success! */
|
||||
}
|
||||
|
||||
/*
|
||||
* Truncate off the last character, which might be more than 1 byte
|
||||
* in MULTIBYTE case.
|
||||
* Truncate off the last character, which might be more than 1
|
||||
* byte in MULTIBYTE case.
|
||||
*/
|
||||
#ifdef MULTIBYTE
|
||||
len = pg_mbcliplen((const unsigned char *) workstr, len, len-1);
|
||||
len = pg_mbcliplen((const unsigned char *) workstr, len, len - 1);
|
||||
workstr[len] = '\0';
|
||||
#else
|
||||
*lastchar = '\0';
|
||||
@@ -2102,7 +2135,7 @@ make_greater_string(const char * str, Oid datatype)
|
||||
|
||||
/* See if there is a binary op of the given name for the given datatype */
|
||||
static Oid
|
||||
find_operator(const char * opname, Oid datatype)
|
||||
find_operator(const char *opname, Oid datatype)
|
||||
{
|
||||
HeapTuple optup;
|
||||
|
||||
@@ -2122,10 +2155,12 @@ find_operator(const char * opname, Oid datatype)
|
||||
* returned value should be pfree'd if no longer needed.
|
||||
*/
|
||||
static Datum
|
||||
string_to_datum(const char * str, Oid datatype)
|
||||
string_to_datum(const char *str, Oid datatype)
|
||||
{
|
||||
/* We cheat a little by assuming that textin() will do for
|
||||
* bpchar and varchar constants too...
|
||||
|
||||
/*
|
||||
* We cheat a little by assuming that textin() will do for bpchar and
|
||||
* varchar constants too...
|
||||
*/
|
||||
if (datatype == NAMEOID)
|
||||
return PointerGetDatum(namein((char *) str));
|
||||
@@ -2137,7 +2172,7 @@ string_to_datum(const char * str, Oid datatype)
|
||||
* Generate a Const node of the appropriate type from a C string.
|
||||
*/
|
||||
static Const *
|
||||
string_to_const(const char * str, Oid datatype)
|
||||
string_to_const(const char *str, Oid datatype)
|
||||
{
|
||||
Datum conval = string_to_datum(str, datatype);
|
||||
|
||||
@@ -2151,7 +2186,7 @@ string_to_const(const char * str, Oid datatype)
|
||||
* "<" operator function, to ensure we get the right result...
|
||||
*/
|
||||
static bool
|
||||
string_lessthan(const char * str1, const char * str2, Oid datatype)
|
||||
string_lessthan(const char *str1, const char *str2, Oid datatype)
|
||||
{
|
||||
Datum datum1 = string_to_datum(str1, datatype);
|
||||
Datum datum2 = string_to_datum(str2, datatype);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.53 2000/02/18 23:47:19 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.54 2000/04/12 17:15:19 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -28,25 +28,27 @@
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
static void match_unsorted_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
|
||||
#ifdef NOT_USED
|
||||
static void match_unsorted_inner(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
|
||||
#endif
|
||||
static void hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist);
|
||||
static Path *best_innerjoin(List *join_paths, List *outer_relid);
|
||||
static Selectivity estimate_disbursion(Query *root, Var *var);
|
||||
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist);
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist);
|
||||
|
||||
|
||||
/*
|
||||
@@ -79,32 +81,33 @@ add_paths_to_joinrel(Query *root,
|
||||
restrictlist);
|
||||
|
||||
/*
|
||||
* 1. Consider mergejoin paths where both relations must be
|
||||
* explicitly sorted.
|
||||
* 1. Consider mergejoin paths where both relations must be explicitly
|
||||
* sorted.
|
||||
*/
|
||||
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
|
||||
restrictlist, mergeclause_list);
|
||||
|
||||
/*
|
||||
* 2. Consider paths where the outer relation need not be
|
||||
* explicitly sorted. This includes both nestloops and
|
||||
* mergejoins where the outer path is already ordered.
|
||||
* 2. Consider paths where the outer relation need not be explicitly
|
||||
* sorted. This includes both nestloops and mergejoins where the outer
|
||||
* path is already ordered.
|
||||
*/
|
||||
match_unsorted_outer(root, joinrel, outerrel, innerrel,
|
||||
restrictlist, mergeclause_list);
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
/*
|
||||
* 3. Consider paths where the inner relation need not be
|
||||
* explicitly sorted. This includes mergejoins only
|
||||
* (nestloops were already built in match_unsorted_outer).
|
||||
* 3. Consider paths where the inner relation need not be explicitly
|
||||
* sorted. This includes mergejoins only (nestloops were already
|
||||
* built in match_unsorted_outer).
|
||||
*
|
||||
* Diked out as redundant 2/13/2000 -- tgl. There isn't any
|
||||
* really significant difference between the inner and outer
|
||||
* side of a mergejoin, so match_unsorted_inner creates no paths
|
||||
* that aren't equivalent to those made by match_unsorted_outer
|
||||
* when add_paths_to_joinrel() is invoked with the two rels given
|
||||
* in the other order.
|
||||
* Diked out as redundant 2/13/2000 -- tgl. There isn't any really
|
||||
* significant difference between the inner and outer side of a
|
||||
* mergejoin, so match_unsorted_inner creates no paths that aren't
|
||||
* equivalent to those made by match_unsorted_outer when
|
||||
* add_paths_to_joinrel() is invoked with the two rels given in the
|
||||
* other order.
|
||||
*/
|
||||
match_unsorted_inner(root, joinrel, outerrel, innerrel,
|
||||
restrictlist, mergeclause_list);
|
||||
@@ -144,31 +147,31 @@ sort_inner_and_outer(Query *root,
|
||||
|
||||
/*
|
||||
* Each possible ordering of the available mergejoin clauses will
|
||||
* generate a differently-sorted result path at essentially the
|
||||
* same cost. We have no basis for choosing one over another at
|
||||
* this level of joining, but some sort orders may be more useful
|
||||
* than others for higher-level mergejoins. Generating a path here
|
||||
* for *every* permutation of mergejoin clauses doesn't seem like
|
||||
* a winning strategy, however; the cost in planning time is too high.
|
||||
* generate a differently-sorted result path at essentially the same
|
||||
* cost. We have no basis for choosing one over another at this level
|
||||
* of joining, but some sort orders may be more useful than others for
|
||||
* higher-level mergejoins. Generating a path here for *every*
|
||||
* permutation of mergejoin clauses doesn't seem like a winning
|
||||
* strategy, however; the cost in planning time is too high.
|
||||
*
|
||||
* For now, we generate one path for each mergejoin clause, listing that
|
||||
* clause first and the rest in random order. This should allow at least
|
||||
* a one-clause mergejoin without re-sorting against any other possible
|
||||
* mergejoin partner path. But if we've not guessed the right ordering
|
||||
* of secondary clauses, we may end up evaluating clauses as qpquals when
|
||||
* they could have been done as mergeclauses. We need to figure out a
|
||||
* better way. (Two possible approaches: look at all the relevant index
|
||||
* relations to suggest plausible sort orders, or make just one output
|
||||
* path and somehow mark it as having a sort-order that can be rearranged
|
||||
* freely.)
|
||||
* clause first and the rest in random order. This should allow at
|
||||
* least a one-clause mergejoin without re-sorting against any other
|
||||
* possible mergejoin partner path. But if we've not guessed the
|
||||
* right ordering of secondary clauses, we may end up evaluating
|
||||
* clauses as qpquals when they could have been done as mergeclauses.
|
||||
* We need to figure out a better way. (Two possible approaches: look
|
||||
* at all the relevant index relations to suggest plausible sort
|
||||
* orders, or make just one output path and somehow mark it as having
|
||||
* a sort-order that can be rearranged freely.)
|
||||
*/
|
||||
foreach(i, mergeclause_list)
|
||||
{
|
||||
RestrictInfo *restrictinfo = lfirst(i);
|
||||
List *curclause_list;
|
||||
List *outerkeys;
|
||||
List *innerkeys;
|
||||
List *merge_pathkeys;
|
||||
RestrictInfo *restrictinfo = lfirst(i);
|
||||
List *curclause_list;
|
||||
List *outerkeys;
|
||||
List *innerkeys;
|
||||
List *merge_pathkeys;
|
||||
|
||||
/* Make a mergeclause list with this guy first. */
|
||||
if (i != mergeclause_list)
|
||||
@@ -176,13 +179,14 @@ sort_inner_and_outer(Query *root,
|
||||
lremove(restrictinfo,
|
||||
listCopy(mergeclause_list)));
|
||||
else
|
||||
curclause_list = mergeclause_list; /* no work at first one... */
|
||||
curclause_list = mergeclause_list; /* no work at first one... */
|
||||
|
||||
/* Build sort pathkeys for both sides.
|
||||
/*
|
||||
* Build sort pathkeys for both sides.
|
||||
*
|
||||
* Note: it's possible that the cheapest paths will already be
|
||||
* sorted properly. create_mergejoin_path will detect that case
|
||||
* and suppress an explicit sort step, so we needn't do so here.
|
||||
* Note: it's possible that the cheapest paths will already be sorted
|
||||
* properly. create_mergejoin_path will detect that case and
|
||||
* suppress an explicit sort step, so we needn't do so here.
|
||||
*/
|
||||
outerkeys = make_pathkeys_for_mergeclauses(root,
|
||||
curclause_list,
|
||||
@@ -198,8 +202,8 @@ sort_inner_and_outer(Query *root,
|
||||
/*
|
||||
* And now we can make the path. We only consider the cheapest-
|
||||
* total-cost input paths, since we are assuming here that a sort
|
||||
* is required. We will consider cheapest-startup-cost input paths
|
||||
* later, and only if they don't need a sort.
|
||||
* is required. We will consider cheapest-startup-cost input
|
||||
* paths later, and only if they don't need a sort.
|
||||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
@@ -225,7 +229,7 @@ sort_inner_and_outer(Query *root,
|
||||
* inner path, one on the cheapest-startup-cost inner path (if different),
|
||||
* and one on the best inner-indexscan path (if any).
|
||||
*
|
||||
* We also consider mergejoins if mergejoin clauses are available. We have
|
||||
* We also consider mergejoins if mergejoin clauses are available. We have
|
||||
* two ways to generate the inner path for a mergejoin: sort the cheapest
|
||||
* inner path, or use an inner path that is already suitably ordered for the
|
||||
* merge. If we have several mergeclauses, it could be that there is no inner
|
||||
@@ -255,8 +259,8 @@ match_unsorted_outer(Query *root,
|
||||
List *i;
|
||||
|
||||
/*
|
||||
* Get the best innerjoin indexpath (if any) for this outer rel.
|
||||
* It's the same for all outer paths.
|
||||
* Get the best innerjoin indexpath (if any) for this outer rel. It's
|
||||
* the same for all outer paths.
|
||||
*/
|
||||
bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids);
|
||||
|
||||
@@ -274,8 +278,8 @@ match_unsorted_outer(Query *root,
|
||||
|
||||
/*
|
||||
* The result will have this sort order (even if it is implemented
|
||||
* as a nestloop, and even if some of the mergeclauses are implemented
|
||||
* by qpquals rather than as true mergeclauses):
|
||||
* as a nestloop, and even if some of the mergeclauses are
|
||||
* implemented by qpquals rather than as true mergeclauses):
|
||||
*/
|
||||
merge_pathkeys = build_join_pathkeys(outerpath->pathkeys,
|
||||
joinrel->targetlist,
|
||||
@@ -318,11 +322,12 @@ match_unsorted_outer(Query *root,
|
||||
/* Compute the required ordering of the inner path */
|
||||
innersortkeys = make_pathkeys_for_mergeclauses(root,
|
||||
mergeclauses,
|
||||
innerrel->targetlist);
|
||||
innerrel->targetlist);
|
||||
|
||||
/*
|
||||
* Generate a mergejoin on the basis of sorting the cheapest inner.
|
||||
* Since a sort will be needed, only cheapest total cost matters.
|
||||
* Generate a mergejoin on the basis of sorting the cheapest
|
||||
* inner. Since a sort will be needed, only cheapest total cost
|
||||
* matters.
|
||||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
@@ -335,11 +340,11 @@ match_unsorted_outer(Query *root,
|
||||
innersortkeys));
|
||||
|
||||
/*
|
||||
* Look for presorted inner paths that satisfy the mergeclause list
|
||||
* or any truncation thereof. Here, we consider both cheap startup
|
||||
* cost and cheap total cost.
|
||||
* Look for presorted inner paths that satisfy the mergeclause
|
||||
* list or any truncation thereof. Here, we consider both cheap
|
||||
* startup cost and cheap total cost.
|
||||
*/
|
||||
trialsortkeys = listCopy(innersortkeys); /* modifiable copy */
|
||||
trialsortkeys = listCopy(innersortkeys); /* modifiable copy */
|
||||
cheapest_startup_inner = NULL;
|
||||
cheapest_total_inner = NULL;
|
||||
num_mergeclauses = length(mergeclauses);
|
||||
@@ -349,8 +354,9 @@ match_unsorted_outer(Query *root,
|
||||
Path *innerpath;
|
||||
List *newclauses = NIL;
|
||||
|
||||
/* Look for an inner path ordered well enough to merge with
|
||||
* the first 'clausecnt' mergeclauses. NB: trialsortkeys list
|
||||
/*
|
||||
* Look for an inner path ordered well enough to merge with
|
||||
* the first 'clausecnt' mergeclauses. NB: trialsortkeys list
|
||||
* is modified destructively, which is why we made a copy...
|
||||
*/
|
||||
trialsortkeys = ltruncate(clausecnt, trialsortkeys);
|
||||
@@ -391,14 +397,16 @@ match_unsorted_outer(Query *root,
|
||||
/* Found a cheap (or even-cheaper) sorted path */
|
||||
if (innerpath != cheapest_total_inner)
|
||||
{
|
||||
/* Avoid rebuilding clause list if we already made one;
|
||||
* saves memory in big join trees...
|
||||
|
||||
/*
|
||||
* Avoid rebuilding clause list if we already made
|
||||
* one; saves memory in big join trees...
|
||||
*/
|
||||
if (newclauses == NIL)
|
||||
{
|
||||
if (clausecnt < num_mergeclauses)
|
||||
newclauses = ltruncate(clausecnt,
|
||||
listCopy(mergeclauses));
|
||||
listCopy(mergeclauses));
|
||||
else
|
||||
newclauses = mergeclauses;
|
||||
}
|
||||
@@ -461,11 +469,12 @@ match_unsorted_inner(Query *root,
|
||||
/* Compute the required ordering of the outer path */
|
||||
outersortkeys = make_pathkeys_for_mergeclauses(root,
|
||||
mergeclauses,
|
||||
outerrel->targetlist);
|
||||
outerrel->targetlist);
|
||||
|
||||
/*
|
||||
* Generate a mergejoin on the basis of sorting the cheapest outer.
|
||||
* Since a sort will be needed, only cheapest total cost matters.
|
||||
* Generate a mergejoin on the basis of sorting the cheapest
|
||||
* outer. Since a sort will be needed, only cheapest total cost
|
||||
* matters.
|
||||
*/
|
||||
merge_pathkeys = build_join_pathkeys(outersortkeys,
|
||||
joinrel->targetlist,
|
||||
@@ -479,10 +488,11 @@ match_unsorted_inner(Query *root,
|
||||
mergeclauses,
|
||||
outersortkeys,
|
||||
NIL));
|
||||
|
||||
/*
|
||||
* Now generate mergejoins based on already-sufficiently-ordered
|
||||
* outer paths. There's likely to be some redundancy here with paths
|
||||
* already generated by merge_unsorted_outer ... but since
|
||||
* outer paths. There's likely to be some redundancy here with
|
||||
* paths already generated by merge_unsorted_outer ... but since
|
||||
* merge_unsorted_outer doesn't consider all permutations of the
|
||||
* mergeclause list, it may fail to notice that this particular
|
||||
* innerpath could have been used with this outerpath.
|
||||
@@ -491,7 +501,8 @@ match_unsorted_inner(Query *root,
|
||||
outersortkeys,
|
||||
TOTAL_COST);
|
||||
if (totalouterpath == NULL)
|
||||
continue; /* there won't be a startup-cost path either */
|
||||
continue; /* there won't be a startup-cost path
|
||||
* either */
|
||||
|
||||
merge_pathkeys = build_join_pathkeys(totalouterpath->pathkeys,
|
||||
joinrel->targetlist,
|
||||
@@ -552,8 +563,8 @@ hash_inner_and_outer(Query *root,
|
||||
List *i;
|
||||
|
||||
/*
|
||||
* Scan the join's restrictinfo list to find hashjoinable clauses
|
||||
* that are usable with this pair of sub-relations. Since we currently
|
||||
* Scan the join's restrictinfo list to find hashjoinable clauses that
|
||||
* are usable with this pair of sub-relations. Since we currently
|
||||
* accept only var-op-var clauses as hashjoinable, we need only check
|
||||
* the membership of the vars to determine whether a particular clause
|
||||
* can be used with this pair of sub-relations. This code would need
|
||||
@@ -568,7 +579,7 @@ hash_inner_and_outer(Query *root,
|
||||
*right,
|
||||
*inner;
|
||||
List *hashclauses;
|
||||
Selectivity innerdisbursion;
|
||||
Selectivity innerdisbursion;
|
||||
|
||||
if (restrictinfo->hashjoinoperator == InvalidOid)
|
||||
continue; /* not hashjoinable */
|
||||
@@ -595,9 +606,9 @@ hash_inner_and_outer(Query *root,
|
||||
innerdisbursion = estimate_disbursion(root, inner);
|
||||
|
||||
/*
|
||||
* We consider both the cheapest-total-cost and cheapest-startup-cost
|
||||
* outer paths. There's no need to consider any but the cheapest-
|
||||
* total-cost inner path, however.
|
||||
* We consider both the cheapest-total-cost and
|
||||
* cheapest-startup-cost outer paths. There's no need to consider
|
||||
* any but the cheapest- total-cost inner path, however.
|
||||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_hashjoin_path(joinrel,
|
||||
@@ -644,7 +655,8 @@ best_innerjoin(List *join_paths, Relids outer_relids)
|
||||
|
||||
Assert(IsA(path, IndexPath));
|
||||
|
||||
/* path->joinrelids is the set of base rels that must be part of
|
||||
/*
|
||||
* path->joinrelids is the set of base rels that must be part of
|
||||
* outer_relids in order to use this inner path, because those
|
||||
* rels are used in the index join quals of this inner path.
|
||||
*/
|
||||
@@ -661,7 +673,7 @@ best_innerjoin(List *join_paths, Relids outer_relids)
|
||||
*
|
||||
* We use a default of 0.1 if we can't figure out anything better.
|
||||
* This will typically discourage use of a hash rather strongly,
|
||||
* if the inner relation is large. We do not want to hash unless
|
||||
* if the inner relation is large. We do not want to hash unless
|
||||
* we know that the inner rel is well-dispersed (or the alternatives
|
||||
* seem much worse).
|
||||
*/
|
||||
@@ -670,7 +682,7 @@ estimate_disbursion(Query *root, Var *var)
|
||||
{
|
||||
Oid relid;
|
||||
|
||||
if (! IsA(var, Var))
|
||||
if (!IsA(var, Var))
|
||||
return 0.1;
|
||||
|
||||
relid = getrelid(var->varno, root->rtable);
|
||||
@@ -690,7 +702,7 @@ estimate_disbursion(Query *root, Var *var)
|
||||
* Since we currently allow only plain Vars as the left and right sides
|
||||
* of mergejoin clauses, this test is relatively simple. This routine
|
||||
* would need to be upgraded to support more-complex expressions
|
||||
* as sides of mergejoins. In theory, we could allow arbitrarily complex
|
||||
* as sides of mergejoins. In theory, we could allow arbitrarily complex
|
||||
* expressions in mergejoins, so long as one side uses only vars from one
|
||||
* sub-relation and the other side uses only vars from the other.
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.43 2000/02/07 04:40:59 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.44 2000/04/12 17:15:20 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
static RelOptInfo *make_join_rel(Query *root, RelOptInfo *rel1,
|
||||
RelOptInfo *rel2);
|
||||
RelOptInfo *rel2);
|
||||
|
||||
|
||||
/*
|
||||
@@ -44,22 +44,23 @@ make_rels_by_joins(Query *root, int level)
|
||||
/*
|
||||
* First, consider left-sided and right-sided plans, in which rels of
|
||||
* exactly level-1 member relations are joined against base relations.
|
||||
* We prefer to join using join clauses, but if we find a rel of level-1
|
||||
* members that has no join clauses, we will generate Cartesian-product
|
||||
* joins against all base rels not already contained in it.
|
||||
* We prefer to join using join clauses, but if we find a rel of
|
||||
* level-1 members that has no join clauses, we will generate
|
||||
* Cartesian-product joins against all base rels not already contained
|
||||
* in it.
|
||||
*
|
||||
* In the first pass (level == 2), we try to join each base rel to each
|
||||
* base rel that appears later in base_rel_list. (The mirror-image
|
||||
* joins are handled automatically by make_join_rel.) In later passes,
|
||||
* we try to join rels of size level-1 from join_rel_list to each
|
||||
* base rel in base_rel_list.
|
||||
* joins are handled automatically by make_join_rel.) In later
|
||||
* passes, we try to join rels of size level-1 from join_rel_list to
|
||||
* each base rel in base_rel_list.
|
||||
*
|
||||
* We assume that the rels already present in join_rel_list appear in
|
||||
* decreasing order of level (number of members). This should be true
|
||||
* since we always add new higher-level rels to the front of the list.
|
||||
*/
|
||||
if (level == 2)
|
||||
r = root->base_rel_list; /* level-1 is base rels */
|
||||
r = root->base_rel_list;/* level-1 is base rels */
|
||||
else
|
||||
r = root->join_rel_list;
|
||||
for (; r != NIL; r = lnext(r))
|
||||
@@ -68,21 +69,23 @@ make_rels_by_joins(Query *root, int level)
|
||||
int old_level = length(old_rel->relids);
|
||||
List *other_rels;
|
||||
|
||||
if (old_level != level-1)
|
||||
if (old_level != level - 1)
|
||||
break;
|
||||
|
||||
if (level == 2)
|
||||
other_rels = lnext(r); /* only consider remaining base rels */
|
||||
other_rels = lnext(r); /* only consider remaining base
|
||||
* rels */
|
||||
else
|
||||
other_rels = root->base_rel_list; /* consider all base rels */
|
||||
other_rels = root->base_rel_list; /* consider all base rels */
|
||||
|
||||
if (old_rel->joininfo != NIL)
|
||||
{
|
||||
|
||||
/*
|
||||
* Note that if all available join clauses for this rel require
|
||||
* more than one other rel, we will fail to make any joins against
|
||||
* it here. That's OK; it'll be considered by "bushy plan" join
|
||||
* code in a higher-level pass.
|
||||
* Note that if all available join clauses for this rel
|
||||
* require more than one other rel, we will fail to make any
|
||||
* joins against it here. That's OK; it'll be considered by
|
||||
* "bushy plan" join code in a higher-level pass.
|
||||
*/
|
||||
make_rels_by_clause_joins(root,
|
||||
old_rel,
|
||||
@@ -90,6 +93,7 @@ make_rels_by_joins(Query *root, int level)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* Oops, we have a relation that is not joined to any other
|
||||
* relation. Cartesian product time.
|
||||
@@ -103,10 +107,11 @@ make_rels_by_joins(Query *root, int level)
|
||||
/*
|
||||
* Now, consider "bushy plans" in which relations of k base rels are
|
||||
* joined to relations of level-k base rels, for 2 <= k <= level-2.
|
||||
* The previous loop left r pointing to the first rel of level level-2.
|
||||
* The previous loop left r pointing to the first rel of level
|
||||
* level-2.
|
||||
*
|
||||
* We only consider bushy-plan joins for pairs of rels where there is
|
||||
* a suitable join clause, in order to avoid unreasonable growth of
|
||||
* We only consider bushy-plan joins for pairs of rels where there is a
|
||||
* suitable join clause, in order to avoid unreasonable growth of
|
||||
* planning time.
|
||||
*/
|
||||
for (; r != NIL; r = lnext(r))
|
||||
@@ -115,8 +120,9 @@ make_rels_by_joins(Query *root, int level)
|
||||
int old_level = length(old_rel->relids);
|
||||
List *r2;
|
||||
|
||||
/* We can quit once past the halfway point (make_join_rel took care
|
||||
* of making the opposite-direction joins)
|
||||
/*
|
||||
* We can quit once past the halfway point (make_join_rel took
|
||||
* care of making the opposite-direction joins)
|
||||
*/
|
||||
if (old_level * 2 < level)
|
||||
break;
|
||||
@@ -137,8 +143,10 @@ make_rels_by_joins(Query *root, int level)
|
||||
{
|
||||
List *i;
|
||||
|
||||
/* OK, we can build a rel of the right level from this pair of
|
||||
* rels. Do so if there is at least one usable join clause.
|
||||
/*
|
||||
* OK, we can build a rel of the right level from this
|
||||
* pair of rels. Do so if there is at least one usable
|
||||
* join clause.
|
||||
*/
|
||||
foreach(i, old_rel->joininfo)
|
||||
{
|
||||
@@ -192,7 +200,7 @@ make_rels_by_clause_joins(Query *root,
|
||||
|
||||
foreach(j, other_rels)
|
||||
{
|
||||
RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);
|
||||
RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);
|
||||
|
||||
if (is_subseti(unjoined_relids, other_rel->relids))
|
||||
result = make_join_rel(root, old_rel, other_rel);
|
||||
@@ -251,8 +259,8 @@ make_rels_by_clauseless_joins(Query *root,
|
||||
static RelOptInfo *
|
||||
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2)
|
||||
{
|
||||
RelOptInfo *joinrel;
|
||||
List *restrictlist;
|
||||
RelOptInfo *joinrel;
|
||||
List *restrictlist;
|
||||
|
||||
/*
|
||||
* Find or build the join RelOptInfo, and compute the restrictlist
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.38 2000/03/22 22:08:33 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.39 2000/04/12 17:15:20 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -27,14 +27,14 @@
|
||||
|
||||
|
||||
static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
|
||||
List *subclauses, List *indices,
|
||||
IndexPath *pathnode);
|
||||
List *subclauses, List *indices,
|
||||
IndexPath *pathnode);
|
||||
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
|
||||
Expr *subclause, List *indices,
|
||||
List **retIndexQual,
|
||||
Oid *retIndexid,
|
||||
Cost *retStartupCost,
|
||||
Cost *retTotalCost);
|
||||
Expr *subclause, List *indices,
|
||||
List **retIndexQual,
|
||||
Oid *retIndexid,
|
||||
Cost *retStartupCost,
|
||||
Cost *retTotalCost);
|
||||
|
||||
|
||||
/*
|
||||
@@ -61,8 +61,8 @@ create_or_index_paths(Query *root,
|
||||
/*
|
||||
* Check to see if this clause is an 'or' clause, and, if so,
|
||||
* whether or not each of the subclauses within the 'or' clause
|
||||
* has been matched by an index. The information used was
|
||||
* saved by create_index_paths().
|
||||
* has been matched by an index. The information used was saved
|
||||
* by create_index_paths().
|
||||
*/
|
||||
if (restriction_is_or_clause(clausenode) &&
|
||||
clausenode->subclauseindices)
|
||||
@@ -80,6 +80,7 @@ create_or_index_paths(Query *root,
|
||||
}
|
||||
if (all_indexable)
|
||||
{
|
||||
|
||||
/*
|
||||
* OK, build an IndexPath for this OR clause, using the
|
||||
* best available index for each subclause.
|
||||
@@ -88,19 +89,23 @@ create_or_index_paths(Query *root,
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
|
||||
/*
|
||||
* This is an IndexScan, but the overall result will consist
|
||||
* of tuples extracted in multiple passes (one for each
|
||||
* subclause of the OR), so the result cannot be claimed
|
||||
* to have any particular ordering.
|
||||
* This is an IndexScan, but the overall result will
|
||||
* consist of tuples extracted in multiple passes (one for
|
||||
* each subclause of the OR), so the result cannot be
|
||||
* claimed to have any particular ordering.
|
||||
*/
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* This isn't a nestloop innerjoin, so: */
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
best_or_subclause_indices(root,
|
||||
@@ -125,7 +130,7 @@ create_or_index_paths(Query *root,
|
||||
* This routine also creates the indexqual and indexid lists that will
|
||||
* be needed by the executor. The indexqual list has one entry for each
|
||||
* scan of the base rel, which is a sublist of indexqual conditions to
|
||||
* apply in that scan. The implicit semantics are AND across each sublist
|
||||
* apply in that scan. The implicit semantics are AND across each sublist
|
||||
* of quals, and OR across the toplevel list (note that the executor
|
||||
* takes care not to return any single tuple more than once). The indexid
|
||||
* list gives the OID of the index to be used in each scan.
|
||||
@@ -181,7 +186,7 @@ best_or_subclause_indices(Query *root,
|
||||
|
||||
pathnode->indexqual = lappend(pathnode->indexqual, best_indexqual);
|
||||
pathnode->indexid = lappendi(pathnode->indexid, best_indexid);
|
||||
if (slist == subclauses) /* first scan? */
|
||||
if (slist == subclauses)/* first scan? */
|
||||
pathnode->path.startup_cost = best_startup_cost;
|
||||
pathnode->path.total_cost += best_total_cost;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.20 2000/02/18 23:47:19 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.21 2000/04/12 17:15:20 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -28,7 +28,7 @@
|
||||
static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
|
||||
static List *make_canonical_pathkey(Query *root, PathKeyItem *item);
|
||||
static Var *find_indexkey_var(Query *root, RelOptInfo *rel,
|
||||
AttrNumber varattno);
|
||||
AttrNumber varattno);
|
||||
|
||||
|
||||
/*--------------------
|
||||
@@ -42,8 +42,8 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel,
|
||||
* of scanning the relation and the resulting ordering of the tuples.
|
||||
* Sequential scan Paths have NIL pathkeys, indicating no known ordering.
|
||||
* Index scans have Path.pathkeys that represent the chosen index's ordering,
|
||||
* if any. A single-key index would create a pathkey with a single sublist,
|
||||
* e.g. ( (tab1.indexkey1/sortop1) ). A multi-key index generates a sublist
|
||||
* if any. A single-key index would create a pathkey with a single sublist,
|
||||
* e.g. ( (tab1.indexkey1/sortop1) ). A multi-key index generates a sublist
|
||||
* per key, e.g. ( (tab1.indexkey1/sortop1) (tab1.indexkey2/sortop2) ) which
|
||||
* shows major sort by indexkey1 (ordering by sortop1) and minor sort by
|
||||
* indexkey2 with sortop2.
|
||||
@@ -56,10 +56,10 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel,
|
||||
* ordering operators used.
|
||||
*
|
||||
* Things get more interesting when we consider joins. Suppose we do a
|
||||
* mergejoin between A and B using the mergeclause A.X = B.Y. The output
|
||||
* mergejoin between A and B using the mergeclause A.X = B.Y. The output
|
||||
* of the mergejoin is sorted by X --- but it is also sorted by Y. We
|
||||
* represent this fact by listing both keys in a single pathkey sublist:
|
||||
* ( (A.X/xsortop B.Y/ysortop) ). This pathkey asserts that the major
|
||||
* ( (A.X/xsortop B.Y/ysortop) ). This pathkey asserts that the major
|
||||
* sort order of the Path can be taken to be *either* A.X or B.Y.
|
||||
* They are equal, so they are both primary sort keys. By doing this,
|
||||
* we allow future joins to use either var as a pre-sorted key, so upper
|
||||
@@ -120,12 +120,12 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel,
|
||||
* We did implement pathkeys just as described above, and found that the
|
||||
* planner spent a huge amount of time comparing pathkeys, because the
|
||||
* representation of pathkeys as unordered lists made it expensive to decide
|
||||
* whether two were equal or not. So, we've modified the representation
|
||||
* whether two were equal or not. So, we've modified the representation
|
||||
* as described next.
|
||||
*
|
||||
* If we scan the WHERE clause for equijoin clauses (mergejoinable clauses)
|
||||
* during planner startup, we can construct lists of equivalent pathkey items
|
||||
* for the query. There could be more than two items per equivalence set;
|
||||
* for the query. There could be more than two items per equivalence set;
|
||||
* for example, WHERE A.X = B.Y AND B.Y = C.Z AND D.R = E.S creates the
|
||||
* equivalence sets { A.X B.Y C.Z } and { D.R E.S } (plus associated sortops).
|
||||
* Any pathkey item that belongs to an equivalence set implies that all the
|
||||
@@ -147,20 +147,20 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel,
|
||||
* equivalence set, we instantly add all the other vars equivalenced to it,
|
||||
* whether they appear yet in the pathkey's relation or not. And we also
|
||||
* mandate that the pathkey sublist appear in the same order as the
|
||||
* equivalence set it comes from. (In practice, we simply return a pointer
|
||||
* equivalence set it comes from. (In practice, we simply return a pointer
|
||||
* to the relevant equivalence set without building any new sublist at all.)
|
||||
* This makes comparing pathkeys very simple and fast, and saves a lot of
|
||||
* work and memory space for pathkey construction as well.
|
||||
*
|
||||
* Note that pathkey sublists having just one item still exist, and are
|
||||
* not expected to be equal() to any equivalence set. This occurs when
|
||||
* not expected to be equal() to any equivalence set. This occurs when
|
||||
* we describe a sort order that involves a var that's not mentioned in
|
||||
* any equijoin clause of the WHERE. We could add singleton sets containing
|
||||
* such vars to the query's list of equivalence sets, but there's little
|
||||
* point in doing so.
|
||||
*
|
||||
* By the way, it's OK and even useful for us to build equivalence sets
|
||||
* that mention multiple vars from the same relation. For example, if
|
||||
* that mention multiple vars from the same relation. For example, if
|
||||
* we have WHERE A.X = A.Y and we are scanning A using an index on X,
|
||||
* we can legitimately conclude that the path is sorted by Y as well;
|
||||
* and this could be handy if Y is the variable used in other join clauses
|
||||
@@ -179,7 +179,7 @@ static Var *find_indexkey_var(Query *root, RelOptInfo *rel,
|
||||
static PathKeyItem *
|
||||
makePathKeyItem(Node *key, Oid sortop)
|
||||
{
|
||||
PathKeyItem *item = makeNode(PathKeyItem);
|
||||
PathKeyItem *item = makeNode(PathKeyItem);
|
||||
|
||||
item->key = key;
|
||||
item->sortop = sortop;
|
||||
@@ -219,11 +219,13 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
|
||||
/* We might see a clause X=X; don't make a single-element list from it */
|
||||
if (equal(item1, item2))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Our plan is to make a two-element set, then sweep through the existing
|
||||
* equijoin sets looking for matches to item1 or item2. When we find one,
|
||||
* we remove that set from equi_key_list and union it into our new set.
|
||||
* When done, we add the new set to the front of equi_key_list.
|
||||
* Our plan is to make a two-element set, then sweep through the
|
||||
* existing equijoin sets looking for matches to item1 or item2. When
|
||||
* we find one, we remove that set from equi_key_list and union it
|
||||
* into our new set. When done, we add the new set to the front of
|
||||
* equi_key_list.
|
||||
*
|
||||
* This is a standard UNION-FIND problem, for which there exist better
|
||||
* data structures than simple lists. If this code ever proves to be
|
||||
@@ -240,8 +242,11 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
|
||||
{
|
||||
/* Found a set to merge into our new set */
|
||||
newset = LispUnion(newset, curset);
|
||||
/* Remove old set from equi_key_list. NOTE this does not change
|
||||
* lnext(cursetlink), so the outer foreach doesn't break.
|
||||
|
||||
/*
|
||||
* Remove old set from equi_key_list. NOTE this does not
|
||||
* change lnext(cursetlink), so the outer foreach doesn't
|
||||
* break.
|
||||
*/
|
||||
root->equi_key_list = lremove(curset, root->equi_key_list);
|
||||
freeList(curset); /* might as well recycle old cons cells */
|
||||
@@ -256,7 +261,7 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
|
||||
* Given a PathKeyItem, find the equi_key_list subset it is a member of,
|
||||
* if any. If so, return a pointer to that sublist, which is the
|
||||
* canonical representation (for this query) of that PathKeyItem's
|
||||
* equivalence set. If it is not found, return a single-element list
|
||||
* equivalence set. If it is not found, return a single-element list
|
||||
* containing the PathKeyItem (when the item has no equivalence peers,
|
||||
* we just allow it to be a standalone list).
|
||||
*
|
||||
@@ -293,13 +298,13 @@ canonicalize_pathkeys(Query *root, List *pathkeys)
|
||||
|
||||
foreach(i, pathkeys)
|
||||
{
|
||||
List *pathkey = (List *) lfirst(i);
|
||||
PathKeyItem *item;
|
||||
List *pathkey = (List *) lfirst(i);
|
||||
PathKeyItem *item;
|
||||
|
||||
/*
|
||||
* It's sufficient to look at the first entry in the sublist;
|
||||
* if there are more entries, they're already part of an
|
||||
* equivalence set by definition.
|
||||
* It's sufficient to look at the first entry in the sublist; if
|
||||
* there are more entries, they're already part of an equivalence
|
||||
* set by definition.
|
||||
*/
|
||||
Assert(pathkey != NIL);
|
||||
item = (PathKeyItem *) lfirst(pathkey);
|
||||
@@ -319,12 +324,12 @@ canonicalize_pathkeys(Query *root, List *pathkeys)
|
||||
* one is "better" than the other.
|
||||
*
|
||||
* A pathkey can be considered better than another if it is a superset:
|
||||
* it contains all the keys of the other plus more. For example, either
|
||||
* it contains all the keys of the other plus more. For example, either
|
||||
* ((A) (B)) or ((A B)) is better than ((A)).
|
||||
*
|
||||
* Because we actually only expect to see canonicalized pathkey sublists,
|
||||
* we don't have to do the full two-way-subset-inclusion test on each
|
||||
* pair of sublists that is implied by the above statement. Instead we
|
||||
* pair of sublists that is implied by the above statement. Instead we
|
||||
* just do an equal(). In the normal case where multi-element sublists
|
||||
* are pointers into the root's equi_key_list, equal() will be very fast:
|
||||
* it will recognize pointer equality when the sublists are the same,
|
||||
@@ -345,23 +350,25 @@ compare_pathkeys(List *keys1, List *keys2)
|
||||
List *subkey1 = lfirst(key1);
|
||||
List *subkey2 = lfirst(key2);
|
||||
|
||||
/* We will never have two subkeys where one is a subset of the other,
|
||||
* because of the canonicalization explained above. Either they are
|
||||
* equal or they ain't.
|
||||
/*
|
||||
* We will never have two subkeys where one is a subset of the
|
||||
* other, because of the canonicalization explained above. Either
|
||||
* they are equal or they ain't.
|
||||
*/
|
||||
if (! equal(subkey1, subkey2))
|
||||
return PATHKEYS_DIFFERENT; /* no need to keep looking */
|
||||
if (!equal(subkey1, subkey2))
|
||||
return PATHKEYS_DIFFERENT; /* no need to keep looking */
|
||||
}
|
||||
|
||||
/* If we reached the end of only one list, the other is longer and
|
||||
* therefore not a subset. (We assume the additional sublist(s)
|
||||
* of the other list are not NIL --- no pathkey list should ever have
|
||||
* a NIL sublist.)
|
||||
/*
|
||||
* If we reached the end of only one list, the other is longer and
|
||||
* therefore not a subset. (We assume the additional sublist(s) of
|
||||
* the other list are not NIL --- no pathkey list should ever have a
|
||||
* NIL sublist.)
|
||||
*/
|
||||
if (key1 == NIL && key2 == NIL)
|
||||
return PATHKEYS_EQUAL;
|
||||
if (key1 != NIL)
|
||||
return PATHKEYS_BETTER1; /* key1 is longer */
|
||||
return PATHKEYS_BETTER1;/* key1 is longer */
|
||||
return PATHKEYS_BETTER2; /* key2 is longer */
|
||||
}
|
||||
|
||||
@@ -375,8 +382,8 @@ pathkeys_contained_in(List *keys1, List *keys2)
|
||||
{
|
||||
switch (compare_pathkeys(keys1, keys2))
|
||||
{
|
||||
case PATHKEYS_EQUAL:
|
||||
case PATHKEYS_BETTER2:
|
||||
case PATHKEYS_EQUAL:
|
||||
case PATHKEYS_BETTER2:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
@@ -448,7 +455,7 @@ get_cheapest_fractional_path_for_pathkeys(List *paths,
|
||||
* do that first.
|
||||
*/
|
||||
if (matched_path != NULL &&
|
||||
compare_fractional_path_costs(matched_path, path, fraction) <= 0)
|
||||
compare_fractional_path_costs(matched_path, path, fraction) <= 0)
|
||||
continue;
|
||||
|
||||
if (pathkeys_contained_in(pathkeys, path->pathkeys))
|
||||
@@ -469,7 +476,7 @@ get_cheapest_fractional_path_for_pathkeys(List *paths,
|
||||
* its "ordering" field, and we will return NIL.)
|
||||
*
|
||||
* If 'scandir' is BackwardScanDirection, attempt to build pathkeys
|
||||
* representing a backwards scan of the index. Return NIL if can't do it.
|
||||
* representing a backwards scan of the index. Return NIL if can't do it.
|
||||
*/
|
||||
List *
|
||||
build_index_pathkeys(Query *root,
|
||||
@@ -527,7 +534,7 @@ build_index_pathkeys(Query *root,
|
||||
/* Normal non-functional index */
|
||||
while (*indexkeys != 0 && *ordering != InvalidOid)
|
||||
{
|
||||
Var *relvar = find_indexkey_var(root, rel, *indexkeys);
|
||||
Var *relvar = find_indexkey_var(root, rel, *indexkeys);
|
||||
|
||||
sortop = *ordering;
|
||||
if (ScanDirectionIsBackward(scandir))
|
||||
@@ -569,9 +576,9 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
|
||||
|
||||
foreach(temp, rel->targetlist)
|
||||
{
|
||||
Var *tle_var = get_expr(lfirst(temp));
|
||||
Var *tle_var = get_expr(lfirst(temp));
|
||||
|
||||
if (IsA(tle_var, Var) && tle_var->varattno == varattno)
|
||||
if (IsA(tle_var, Var) &&tle_var->varattno == varattno)
|
||||
return tle_var;
|
||||
}
|
||||
|
||||
@@ -606,11 +613,12 @@ build_join_pathkeys(List *outer_pathkeys,
|
||||
List *join_rel_tlist,
|
||||
List *equi_key_list)
|
||||
{
|
||||
|
||||
/*
|
||||
* This used to be quite a complex bit of code, but now that all
|
||||
* pathkey sublists start out life canonicalized, we don't have to
|
||||
* do a darn thing here! The inner-rel vars we used to need to add
|
||||
* are *already* part of the outer pathkey!
|
||||
* pathkey sublists start out life canonicalized, we don't have to do
|
||||
* a darn thing here! The inner-rel vars we used to need to add are
|
||||
* *already* part of the outer pathkey!
|
||||
*
|
||||
* I'd remove the routine entirely, but maybe someday we'll need it...
|
||||
*/
|
||||
@@ -644,16 +652,17 @@ make_pathkeys_for_sortclauses(List *sortclauses,
|
||||
|
||||
foreach(i, sortclauses)
|
||||
{
|
||||
SortClause *sortcl = (SortClause *) lfirst(i);
|
||||
Node *sortkey;
|
||||
PathKeyItem *pathkey;
|
||||
SortClause *sortcl = (SortClause *) lfirst(i);
|
||||
Node *sortkey;
|
||||
PathKeyItem *pathkey;
|
||||
|
||||
sortkey = get_sortgroupclause_expr(sortcl, tlist);
|
||||
pathkey = makePathKeyItem(sortkey, sortcl->sortop);
|
||||
|
||||
/*
|
||||
* The pathkey becomes a one-element sublist, for now;
|
||||
* canonicalize_pathkeys() might replace it with a longer
|
||||
* sublist later.
|
||||
* canonicalize_pathkeys() might replace it with a longer sublist
|
||||
* later.
|
||||
*/
|
||||
pathkeys = lappend(pathkeys, lcons(pathkey, NIL));
|
||||
}
|
||||
@@ -691,28 +700,28 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
||||
|
||||
foreach(i, pathkeys)
|
||||
{
|
||||
List *pathkey = lfirst(i);
|
||||
RestrictInfo *matched_restrictinfo = NULL;
|
||||
List *j;
|
||||
List *pathkey = lfirst(i);
|
||||
RestrictInfo *matched_restrictinfo = NULL;
|
||||
List *j;
|
||||
|
||||
/*
|
||||
* We can match any of the keys in this pathkey sublist,
|
||||
* since they're all equivalent. And we can match against
|
||||
* either left or right side of any mergejoin clause we haven't
|
||||
* used yet. For the moment we use a dumb "greedy" algorithm
|
||||
* with no backtracking. Is it worth being any smarter to
|
||||
* make a longer list of usable mergeclauses? Probably not.
|
||||
* We can match any of the keys in this pathkey sublist, since
|
||||
* they're all equivalent. And we can match against either left
|
||||
* or right side of any mergejoin clause we haven't used yet. For
|
||||
* the moment we use a dumb "greedy" algorithm with no
|
||||
* backtracking. Is it worth being any smarter to make a longer
|
||||
* list of usable mergeclauses? Probably not.
|
||||
*/
|
||||
foreach(j, pathkey)
|
||||
{
|
||||
PathKeyItem *keyitem = lfirst(j);
|
||||
Node *key = keyitem->key;
|
||||
Oid keyop = keyitem->sortop;
|
||||
List *k;
|
||||
PathKeyItem *keyitem = lfirst(j);
|
||||
Node *key = keyitem->key;
|
||||
Oid keyop = keyitem->sortop;
|
||||
List *k;
|
||||
|
||||
foreach(k, restrictinfos)
|
||||
{
|
||||
RestrictInfo *restrictinfo = lfirst(k);
|
||||
RestrictInfo *restrictinfo = lfirst(k);
|
||||
|
||||
Assert(restrictinfo->mergejoinoperator != InvalidOid);
|
||||
|
||||
@@ -720,7 +729,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
||||
equal(key, get_leftop(restrictinfo->clause))) ||
|
||||
(keyop == restrictinfo->right_sortop &&
|
||||
equal(key, get_rightop(restrictinfo->clause)))) &&
|
||||
! member(restrictinfo, mergeclauses))
|
||||
!member(restrictinfo, mergeclauses))
|
||||
{
|
||||
matched_restrictinfo = restrictinfo;
|
||||
break;
|
||||
@@ -732,11 +741,12 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
||||
|
||||
/*
|
||||
* If we didn't find a mergeclause, we're done --- any additional
|
||||
* sort-key positions in the pathkeys are useless. (But we can
|
||||
* sort-key positions in the pathkeys are useless. (But we can
|
||||
* still mergejoin if we found at least one mergeclause.)
|
||||
*/
|
||||
if (! matched_restrictinfo)
|
||||
if (!matched_restrictinfo)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If we did find a usable mergeclause for this sort-key position,
|
||||
* add it to result list.
|
||||
@@ -756,7 +766,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
||||
* 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
|
||||
* that will be used in a merge join.
|
||||
* 'tlist' is a relation target list for either the inner or outer
|
||||
* side of the proposed join rel. (Not actually needed anymore)
|
||||
* side of the proposed join rel. (Not actually needed anymore)
|
||||
*
|
||||
* Returns a pathkeys list that can be applied to the indicated relation.
|
||||
*
|
||||
@@ -785,24 +795,26 @@ make_pathkeys_for_mergeclauses(Query *root,
|
||||
/*
|
||||
* Find the key and sortop needed for this mergeclause.
|
||||
*
|
||||
* Both sides of the mergeclause should appear in one of the
|
||||
* query's pathkey equivalence classes, so it doesn't matter
|
||||
* which one we use here.
|
||||
* Both sides of the mergeclause should appear in one of the query's
|
||||
* pathkey equivalence classes, so it doesn't matter which one we
|
||||
* use here.
|
||||
*/
|
||||
key = (Node *) get_leftop(restrictinfo->clause);
|
||||
sortop = restrictinfo->left_sortop;
|
||||
|
||||
/*
|
||||
* Find pathkey sublist for this sort item. We expect to find
|
||||
* the canonical set including the mergeclause's left and right
|
||||
* sides; if we get back just the one item, something is rotten.
|
||||
* Find pathkey sublist for this sort item. We expect to find the
|
||||
* canonical set including the mergeclause's left and right sides;
|
||||
* if we get back just the one item, something is rotten.
|
||||
*/
|
||||
item = makePathKeyItem(key, sortop);
|
||||
pathkey = make_canonical_pathkey(root, item);
|
||||
Assert(length(pathkey) > 1);
|
||||
|
||||
/*
|
||||
* Since the item we just made is not in the returned canonical set,
|
||||
* we can free it --- this saves a useful amount of storage in a
|
||||
* big join tree.
|
||||
* Since the item we just made is not in the returned canonical
|
||||
* set, we can free it --- this saves a useful amount of storage
|
||||
* in a big join tree.
|
||||
*/
|
||||
pfree(item);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.5 2000/02/15 20:49:17 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.6 2000/04/12 17:15:20 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -37,30 +37,34 @@
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static void create_tidscan_joinpaths(RelOptInfo *rel);
|
||||
static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo);
|
||||
static bool isEvaluable(int varno, Node *node);
|
||||
static Node *TidequalClause(int varno, Expr *node);
|
||||
static List *TidqualFromExpr(int varno, Expr *expr);
|
||||
static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo);
|
||||
static bool isEvaluable(int varno, Node *node);
|
||||
static Node *TidequalClause(int varno, Expr *node);
|
||||
static List *TidqualFromExpr(int varno, Expr *expr);
|
||||
|
||||
static
|
||||
bool isEvaluable(int varno, Node *node)
|
||||
bool
|
||||
isEvaluable(int varno, Node *node)
|
||||
{
|
||||
List *lst;
|
||||
Expr *expr;
|
||||
List *lst;
|
||||
Expr *expr;
|
||||
|
||||
if (IsA(node, Const)) return true;
|
||||
if (IsA(node, Param)) return true;
|
||||
if (IsA(node, Const))
|
||||
return true;
|
||||
if (IsA(node, Param))
|
||||
return true;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *)node;
|
||||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varno == varno)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if (!is_funcclause(node)) return false;
|
||||
expr = (Expr *)node;
|
||||
foreach (lst, expr->args)
|
||||
if (!is_funcclause(node))
|
||||
return false;
|
||||
expr = (Expr *) node;
|
||||
foreach(lst, expr->args)
|
||||
{
|
||||
if (!isEvaluable(varno, lfirst(lst)))
|
||||
return false;
|
||||
@@ -72,53 +76,60 @@ bool isEvaluable(int varno, Node *node)
|
||||
/*
|
||||
* The 2nd parameter should be an opclause
|
||||
* Extract the right node if the opclause is CTID= ....
|
||||
* or the left node if the opclause is ....=CTID
|
||||
* or the left node if the opclause is ....=CTID
|
||||
*/
|
||||
static
|
||||
Node *TidequalClause(int varno, Expr *node)
|
||||
Node *
|
||||
TidequalClause(int varno, Expr *node)
|
||||
{
|
||||
Node *rnode = 0, *arg1, *arg2, *arg;
|
||||
Oper *oper;
|
||||
Var *var;
|
||||
Const *aconst;
|
||||
Param *param;
|
||||
Expr *expr;
|
||||
Node *rnode = 0,
|
||||
*arg1,
|
||||
*arg2,
|
||||
*arg;
|
||||
Oper *oper;
|
||||
Var *var;
|
||||
Const *aconst;
|
||||
Param *param;
|
||||
Expr *expr;
|
||||
|
||||
if (!node->oper) return rnode;
|
||||
if (!node->args) return rnode;
|
||||
if (length(node->args) != 2) return rnode;
|
||||
oper = (Oper *) node->oper;
|
||||
if (!node->oper)
|
||||
return rnode;
|
||||
if (!node->args)
|
||||
return rnode;
|
||||
if (length(node->args) != 2)
|
||||
return rnode;
|
||||
oper = (Oper *) node->oper;
|
||||
if (oper->opno != TIDEqualOperator)
|
||||
return rnode;
|
||||
arg1 = lfirst(node->args);
|
||||
arg2 = lsecond(node->args);
|
||||
|
||||
arg = (Node *)0;
|
||||
arg = (Node *) 0;
|
||||
if (IsA(arg1, Var))
|
||||
{
|
||||
var = (Var *) arg1;
|
||||
if (var->varno == varno &&
|
||||
var->varattno == SelfItemPointerAttributeNumber &&
|
||||
var->vartype == TIDOID)
|
||||
var->varattno == SelfItemPointerAttributeNumber &&
|
||||
var->vartype == TIDOID)
|
||||
arg = arg2;
|
||||
else if (var->varnoold == varno &&
|
||||
var->varoattno == SelfItemPointerAttributeNumber &&
|
||||
var->vartype == TIDOID)
|
||||
var->varoattno == SelfItemPointerAttributeNumber &&
|
||||
var->vartype == TIDOID)
|
||||
arg = arg2;
|
||||
}
|
||||
if ((!arg) && IsA(arg2, Var))
|
||||
{
|
||||
var = (Var *) arg2;
|
||||
if (var->varno == varno &&
|
||||
var->varattno == SelfItemPointerAttributeNumber &&
|
||||
var->vartype == TIDOID)
|
||||
var->varattno == SelfItemPointerAttributeNumber &&
|
||||
var->vartype == TIDOID)
|
||||
arg = arg1;
|
||||
}
|
||||
if (!arg)
|
||||
return rnode;
|
||||
switch (nodeTag(arg))
|
||||
{
|
||||
case T_Const:
|
||||
case T_Const:
|
||||
aconst = (Const *) arg;
|
||||
if (aconst->consttype != TIDOID)
|
||||
return rnode;
|
||||
@@ -126,27 +137,29 @@ Node *TidequalClause(int varno, Expr *node)
|
||||
return rnode;
|
||||
rnode = arg;
|
||||
break;
|
||||
case T_Param:
|
||||
case T_Param:
|
||||
param = (Param *) arg;
|
||||
if (param->paramtype != TIDOID)
|
||||
return rnode;
|
||||
rnode = arg;
|
||||
break;
|
||||
case T_Var:
|
||||
case T_Var:
|
||||
var = (Var *) arg;
|
||||
if (var->varno == varno ||
|
||||
var->vartype != TIDOID)
|
||||
var->vartype != TIDOID)
|
||||
return rnode;
|
||||
rnode = arg;
|
||||
break;
|
||||
case T_Expr:
|
||||
case T_Expr:
|
||||
expr = (Expr *) arg;
|
||||
if (expr->typeOid != TIDOID) return rnode;
|
||||
if (expr->opType != FUNC_EXPR) return rnode;
|
||||
if (isEvaluable(varno, (Node *)expr))
|
||||
if (expr->typeOid != TIDOID)
|
||||
return rnode;
|
||||
if (expr->opType != FUNC_EXPR)
|
||||
return rnode;
|
||||
if (isEvaluable(varno, (Node *) expr))
|
||||
rnode = arg;
|
||||
break;
|
||||
default:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return rnode;
|
||||
@@ -160,43 +173,43 @@ Node *TidequalClause(int varno, Expr *node)
|
||||
* When the expr node is an and_clause,we return the list of
|
||||
* CTID values if we could extract the CTID values from a member
|
||||
* node.
|
||||
*/
|
||||
*/
|
||||
static
|
||||
List *TidqualFromExpr(int varno, Expr *expr)
|
||||
List *
|
||||
TidqualFromExpr(int varno, Expr *expr)
|
||||
{
|
||||
List *rlst = NIL, *lst, *frtn;
|
||||
Node *node = (Node *) expr, *rnode;
|
||||
List *rlst = NIL,
|
||||
*lst,
|
||||
*frtn;
|
||||
Node *node = (Node *) expr,
|
||||
*rnode;
|
||||
|
||||
if (is_opclause(node))
|
||||
{
|
||||
rnode = TidequalClause(varno, expr);
|
||||
if (rnode)
|
||||
{
|
||||
rlst = lcons(rnode, rlst);
|
||||
}
|
||||
}
|
||||
else if (and_clause(node))
|
||||
{
|
||||
foreach (lst, expr->args)
|
||||
foreach(lst, expr->args)
|
||||
{
|
||||
node = lfirst(lst);
|
||||
if (!IsA(node, Expr))
|
||||
if (!IsA(node, Expr))
|
||||
continue;
|
||||
rlst = TidqualFromExpr(varno, (Expr *)node);
|
||||
rlst = TidqualFromExpr(varno, (Expr *) node);
|
||||
if (rlst)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (or_clause(node))
|
||||
{
|
||||
foreach (lst, expr->args)
|
||||
foreach(lst, expr->args)
|
||||
{
|
||||
node = lfirst(lst);
|
||||
if (IsA(node, Expr) &&
|
||||
(frtn = TidqualFromExpr(varno, (Expr *)node)) )
|
||||
{
|
||||
(frtn = TidqualFromExpr(varno, (Expr *) node)))
|
||||
rlst = nconc(rlst, frtn);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rlst)
|
||||
@@ -207,24 +220,26 @@ List *TidqualFromExpr(int varno, Expr *expr)
|
||||
}
|
||||
}
|
||||
return rlst;
|
||||
}
|
||||
}
|
||||
|
||||
static List *
|
||||
TidqualFromRestrictinfo(List *relids, List *restrictinfo)
|
||||
{
|
||||
List *lst, *rlst = NIL;
|
||||
int varno;
|
||||
Node *node;
|
||||
Expr *expr;
|
||||
List *lst,
|
||||
*rlst = NIL;
|
||||
int varno;
|
||||
Node *node;
|
||||
Expr *expr;
|
||||
|
||||
if (length(relids) != 1)
|
||||
return NIL;
|
||||
varno = lfirsti(relids);
|
||||
foreach (lst, restrictinfo)
|
||||
foreach(lst, restrictinfo)
|
||||
{
|
||||
node = lfirst(lst);
|
||||
if (!IsA(node, RestrictInfo)) continue;
|
||||
expr = ((RestrictInfo *)node)->clause;
|
||||
if (!IsA(node, RestrictInfo))
|
||||
continue;
|
||||
expr = ((RestrictInfo *) node)->clause;
|
||||
rlst = TidqualFromExpr(varno, expr);
|
||||
if (rlst)
|
||||
break;
|
||||
@@ -241,20 +256,20 @@ TidqualFromRestrictinfo(List *relids, List *restrictinfo)
|
||||
static void
|
||||
create_tidscan_joinpaths(RelOptInfo *rel)
|
||||
{
|
||||
List *rlst = NIL,
|
||||
*lst;
|
||||
List *rlst = NIL,
|
||||
*lst;
|
||||
|
||||
foreach (lst, rel->joininfo)
|
||||
foreach(lst, rel->joininfo)
|
||||
{
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(lst);
|
||||
List *restinfo,
|
||||
*tideval;
|
||||
List *restinfo,
|
||||
*tideval;
|
||||
|
||||
restinfo = joininfo->jinfo_restrictinfo;
|
||||
tideval = TidqualFromRestrictinfo(rel->relids, restinfo);
|
||||
if (length(tideval) == 1)
|
||||
{
|
||||
TidPath *pathnode = makeNode(TidPath);
|
||||
TidPath *pathnode = makeNode(TidPath);
|
||||
|
||||
pathnode->path.pathtype = T_TidScan;
|
||||
pathnode->path.parent = rel;
|
||||
@@ -278,9 +293,9 @@ create_tidscan_joinpaths(RelOptInfo *rel)
|
||||
void
|
||||
create_tidscan_paths(Query *root, RelOptInfo *rel)
|
||||
{
|
||||
List *tideval = TidqualFromRestrictinfo(rel->relids,
|
||||
rel->baserestrictinfo);
|
||||
|
||||
List *tideval = TidqualFromRestrictinfo(rel->relids,
|
||||
rel->baserestrictinfo);
|
||||
|
||||
if (tideval)
|
||||
add_path(rel, (Path *) create_tidscan_path(rel, tideval));
|
||||
create_tidscan_joinpaths(rel);
|
||||
|
||||
Reference in New Issue
Block a user