Clean up messy clause-selectivity code in clausesel.c; repair bug
identified by Hiroshi (incorrect cost attributed to OR clauses after multiple passes through set_rest_selec()). I think the code was trying to allow selectivities of OR subclauses to be passed in from outside, but noplace was actually passing any useful data, and set_rest_selec() was passing wrong data. Restructure representation of "indexqual" in IndexPath nodes so that it is the same as for indxqual in completed IndexScan nodes: namely, a toplevel list with an entry for each pass of the index scan, having sublists that are implicitly-ANDed index qual conditions for that pass. You don't want to know what the old representation was :-( Improve documentation of OR-clause indexscan functions. Remove useless 'notclause' field from RestrictInfo nodes. (This might force an initdb for anyone who has stored rules containing RestrictInfos, but I do not think that RestrictInfo ever appears in completed plans.)
This commit is contained in:
parent
348bdbce79
commit
ac4913a0dd
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.86 1999/07/17 20:17:05 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.87 1999/07/24 23:21:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1317,7 +1317,6 @@ _copyRestrictInfo(RestrictInfo *from)
|
||||
Node_Copy(from, newnode, clause);
|
||||
|
||||
newnode->selectivity = from->selectivity;
|
||||
newnode->notclause = from->notclause;
|
||||
|
||||
Node_Copy(from, newnode, indexids);
|
||||
Node_Copy(from, newnode, mergejoinorder);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.43 1999/07/17 20:17:05 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.44 1999/07/24 23:21:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -305,8 +305,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
|
||||
return false;
|
||||
if (a->selectivity != b->selectivity)
|
||||
return false;
|
||||
if (a->notclause != b->notclause)
|
||||
return false;
|
||||
#ifdef EqualMergeOrderExists
|
||||
if (!EqualMergeOrder(a->mergejoinorder, b->mergejoinorder))
|
||||
return false;
|
||||
|
@ -5,7 +5,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: outfuncs.c,v 1.90 1999/07/18 19:02:49 tgl Exp $
|
||||
* $Id: outfuncs.c,v 1.91 1999/07/24 23:21:07 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||
@ -1110,9 +1110,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
|
||||
_outNode(str, node->clause);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :selectivity %f :notclause %s :indexids ",
|
||||
node->selectivity,
|
||||
node->notclause ? "true" : "false");
|
||||
" :selectivity %f :indexids ",
|
||||
node->selectivity);
|
||||
_outNode(str, node->indexids);
|
||||
|
||||
appendStringInfo(str, " :mergejoinorder ");
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.69 1999/07/17 20:17:08 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.70 1999/07/24 23:21:08 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||
@ -1856,14 +1856,6 @@ _readRestrictInfo()
|
||||
|
||||
local_node->selectivity = atof(token);
|
||||
|
||||
token = lsptok(NULL, &length); /* get :notclause */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
|
||||
if (!strncmp(token, "true", 4))
|
||||
local_node->notclause = true;
|
||||
else
|
||||
local_node->notclause = false;
|
||||
|
||||
token = lsptok(NULL, &length); /* get :indexids */
|
||||
local_node->indexids = nodeRead(true); /* now read it */
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.50 1999/07/17 20:17:11 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.51 1999/07/24 23:21:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -86,8 +86,8 @@ make_one_rel(Query *root, List *rels)
|
||||
* set_base_rel_pathlist
|
||||
* Finds all paths available for scanning each relation entry in
|
||||
* 'rels'. Sequential scan and any available indices are considered
|
||||
* if possible(indices are not considered for lower nesting levels).
|
||||
* All unique paths are attached to the relation's 'pathlist' field.
|
||||
* if possible (indices are not considered for lower nesting levels).
|
||||
* All useful paths are attached to the relation's 'pathlist' field.
|
||||
*
|
||||
* MODIFIES: rels
|
||||
*/
|
||||
@ -98,21 +98,32 @@ set_base_rel_pathlist(Query *root, List *rels)
|
||||
|
||||
foreach(temp, rels)
|
||||
{
|
||||
RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
|
||||
List *indices = find_relation_indices(root, rel);
|
||||
List *sequential_scan_list;
|
||||
List *rel_index_scan_list;
|
||||
List *or_index_scan_list;
|
||||
RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
|
||||
|
||||
sequential_scan_list = lcons(create_seqscan_path(rel), NIL);
|
||||
|
||||
rel_index_scan_list = create_index_paths(root,
|
||||
rel,
|
||||
find_relation_indices(root, rel),
|
||||
indices,
|
||||
rel->restrictinfo,
|
||||
rel->joininfo);
|
||||
|
||||
or_index_scan_list = create_or_index_paths(root, rel, rel->restrictinfo);
|
||||
/* 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 full list of indices.
|
||||
*/
|
||||
|
||||
or_index_scan_list = create_or_index_paths(root, rel,
|
||||
rel->restrictinfo);
|
||||
|
||||
/* add_pathlist 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.
|
||||
*/
|
||||
rel->pathlist = add_pathlist(rel,
|
||||
sequential_scan_list,
|
||||
nconc(rel_index_scan_list,
|
||||
@ -128,7 +139,6 @@ set_base_rel_pathlist(Query *root, List *rels)
|
||||
rel->size = compute_rel_size(rel);
|
||||
rel->width = compute_rel_width(rel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.23 1999/07/16 04:59:14 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.24 1999/07/24 23:21:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -23,32 +23,26 @@
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static Cost compute_selec(Query *root, List *clauses, List *or_selectivities);
|
||||
|
||||
/****************************************************************************
|
||||
* ROUTINES TO SET CLAUSE SELECTIVITIES
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* set_clause_selectivities -
|
||||
* Sets the selectivity field for each of clause in 'restrictinfo-list'
|
||||
* to 'new-selectivity'. If the selectivity has already been set, reset
|
||||
* it only if the new one is better.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*
|
||||
* Sets the selectivity field for each clause in 'restrictinfo-list'
|
||||
* to 'new-selectivity'. If the selectivity has already been set,
|
||||
* change it only if the new one is better.
|
||||
*/
|
||||
void
|
||||
set_clause_selectivities(List *restrictinfo_list, Cost new_selectivity)
|
||||
{
|
||||
List *temp;
|
||||
RestrictInfo *clausenode;
|
||||
Cost cost_clause;
|
||||
List *rlist;
|
||||
|
||||
foreach(temp, restrictinfo_list)
|
||||
foreach(rlist, restrictinfo_list)
|
||||
{
|
||||
clausenode = (RestrictInfo *) lfirst(temp);
|
||||
cost_clause = clausenode->selectivity;
|
||||
RestrictInfo *clausenode = (RestrictInfo *) lfirst(rlist);
|
||||
Cost cost_clause = clausenode->selectivity;
|
||||
|
||||
if (cost_clause <= 0 || new_selectivity < cost_clause)
|
||||
clausenode->selectivity = new_selectivity;
|
||||
}
|
||||
@ -63,18 +57,12 @@ set_clause_selectivities(List *restrictinfo_list, Cost new_selectivity)
|
||||
Cost
|
||||
product_selec(List *restrictinfo_list)
|
||||
{
|
||||
Cost result = 1.0;
|
||||
Cost result = (Cost) 1.0;
|
||||
List *rlist;
|
||||
|
||||
if (restrictinfo_list != NIL)
|
||||
foreach(rlist, restrictinfo_list)
|
||||
{
|
||||
List *xclausenode = NIL;
|
||||
Cost temp;
|
||||
|
||||
foreach(xclausenode, restrictinfo_list)
|
||||
{
|
||||
temp = ((RestrictInfo *) lfirst(xclausenode))->selectivity;
|
||||
result = result * temp;
|
||||
}
|
||||
result *= ((RestrictInfo *) lfirst(rlist))->selectivity;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -84,19 +72,16 @@ product_selec(List *restrictinfo_list)
|
||||
* Scans through clauses on each relation and assigns a selectivity to
|
||||
* those clauses that haven't been assigned a selectivity by an index.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
* MODIFIES: selectivities of the various rel's restrictinfo
|
||||
* slots.
|
||||
* MODIFIES: selectivities of the various rel's restrictinfo slots.
|
||||
*/
|
||||
void
|
||||
set_rest_relselec(Query *root, List *rel_list)
|
||||
{
|
||||
RelOptInfo *rel;
|
||||
List *x;
|
||||
|
||||
foreach(x, rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(x);
|
||||
RelOptInfo *rel = (RelOptInfo *) lfirst(x);
|
||||
set_rest_selec(root, rel->restrictinfo);
|
||||
}
|
||||
}
|
||||
@ -105,31 +90,20 @@ set_rest_relselec(Query *root, List *rel_list)
|
||||
* set_rest_selec -
|
||||
* Sets the selectivity fields for those clauses within a single
|
||||
* relation's 'restrictinfo-list' that haven't already been set.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*
|
||||
*/
|
||||
void
|
||||
set_rest_selec(Query *root, List *restrictinfo_list)
|
||||
{
|
||||
List *temp = NIL;
|
||||
RestrictInfo *clausenode = (RestrictInfo *) NULL;
|
||||
Cost cost_clause;
|
||||
List *rlist;
|
||||
|
||||
foreach(temp, restrictinfo_list)
|
||||
foreach(rlist, restrictinfo_list)
|
||||
{
|
||||
clausenode = (RestrictInfo *) lfirst(temp);
|
||||
cost_clause = clausenode->selectivity;
|
||||
RestrictInfo *clause = (RestrictInfo *) lfirst(rlist);
|
||||
|
||||
/*
|
||||
* Check to see if the selectivity of this clause or any 'or'
|
||||
* subclauses (if any) haven't been set yet.
|
||||
*/
|
||||
if (cost_clause <= 0 || valid_or_clause(clausenode))
|
||||
if (clause->selectivity <= 0)
|
||||
{
|
||||
clausenode->selectivity = compute_clause_selec(root,
|
||||
(Node *) clausenode->clause,
|
||||
lcons(makeFloat(cost_clause), NIL));
|
||||
clause->selectivity =
|
||||
compute_clause_selec(root, (Node *) clause->clause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,89 +114,29 @@ set_rest_selec(Query *root, List *restrictinfo_list)
|
||||
|
||||
/*
|
||||
* compute_clause_selec -
|
||||
* Given a clause, this routine will compute the selectivity of the
|
||||
* clause by calling 'compute_selec' with the appropriate parameters
|
||||
* and possibly use that return value to compute the real selectivity
|
||||
* of a clause.
|
||||
*
|
||||
* 'or-selectivities' are selectivities that have already been assigned
|
||||
* to subclauses of an 'or' clause.
|
||||
*
|
||||
* Returns a flonum corresponding to the clause selectivity.
|
||||
*
|
||||
* Computes the selectivity of a clause.
|
||||
*/
|
||||
Cost
|
||||
compute_clause_selec(Query *root, Node *clause, List *or_selectivities)
|
||||
compute_clause_selec(Query *root, Node *clause)
|
||||
{
|
||||
if (is_opclause(clause))
|
||||
return compute_selec(root, lcons(clause, NIL), or_selectivities);
|
||||
else if (not_clause(clause))
|
||||
{
|
||||
|
||||
/*
|
||||
* 'not' gets "1.0 - selectivity-of-inner-clause".
|
||||
*/
|
||||
return (1.000000 - compute_selec(root,
|
||||
lcons(get_notclausearg((Expr *) clause),
|
||||
NIL),
|
||||
or_selectivities));
|
||||
}
|
||||
else if (or_clause(clause))
|
||||
{
|
||||
|
||||
/*
|
||||
* Both 'or' and 'and' clauses are evaluated as described in
|
||||
* (compute_selec).
|
||||
*/
|
||||
return compute_selec(root, ((Expr *) clause)->args, or_selectivities);
|
||||
}
|
||||
else
|
||||
return compute_selec(root, lcons(clause, NIL), or_selectivities);
|
||||
}
|
||||
|
||||
/*
|
||||
* compute_selec -
|
||||
* Computes the selectivity of a clause.
|
||||
*
|
||||
* If there is more than one clause in the argument 'clauses', then the
|
||||
* desired selectivity is that of an 'or' clause. Selectivities for an
|
||||
* 'or' clause such as (OR a b) are computed by finding the selectivity
|
||||
* of a (s1) and b (s2) and computing s1+s2 - s1*s2.
|
||||
*
|
||||
* In addition, if the clause is an 'or' clause, individual selectivities
|
||||
* may have already been assigned by indices to subclauses. These values
|
||||
* are contained in the list 'or-selectivities'.
|
||||
*
|
||||
* Returns the clause selectivity as a flonum.
|
||||
*
|
||||
*/
|
||||
static Cost
|
||||
compute_selec(Query *root, List *clauses, List *or_selectivities)
|
||||
{
|
||||
Cost s1 = 0;
|
||||
List *clause = lfirst(clauses);
|
||||
Cost s1 = 1.0; /* default for any unhandled clause type */
|
||||
|
||||
if (clause == NULL)
|
||||
s1 = 1.0;
|
||||
else if (IsA(clause, Param))
|
||||
return s1;
|
||||
if (IsA(clause, Var))
|
||||
{
|
||||
/* XXX How're we handling this before?? -ay */
|
||||
s1 = 1.0;
|
||||
}
|
||||
else if (IsA(clause, Const))
|
||||
s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
|
||||
else if (IsA(clause, Var))
|
||||
{
|
||||
Oid relid = getrelid(((Var *) clause)->varno,
|
||||
root->rtable);
|
||||
|
||||
/*
|
||||
* we have a bool Var. This is exactly equivalent to the clause:
|
||||
* reln.attribute = 't' so we compute the selectivity as if that
|
||||
* is what we have. The magic #define constants are a hack. I
|
||||
* didn't want to have to do system cache look ups to find out all
|
||||
* of that info.
|
||||
*
|
||||
* XXX why are we using varno and varoattno? Seems like it should
|
||||
* be varno/varattno or varnoold/varoattno, not mix & match...
|
||||
*/
|
||||
Oid relid = getrelid(((Var *) clause)->varno,
|
||||
root->rtable);
|
||||
|
||||
s1 = restriction_selectivity(F_EQSEL,
|
||||
BooleanEqualOperator,
|
||||
@ -231,134 +145,141 @@ compute_selec(Query *root, List *clauses, List *or_selectivities)
|
||||
"t",
|
||||
_SELEC_CONSTANT_RIGHT_);
|
||||
}
|
||||
else if (or_selectivities)
|
||||
else if (IsA(clause, Param))
|
||||
{
|
||||
/* If s1 has already been assigned by an index, use that value. */
|
||||
List *this_sel = lfirst(or_selectivities);
|
||||
|
||||
s1 = floatVal(this_sel);
|
||||
/* XXX any way to do better? */
|
||||
s1 = 1.0;
|
||||
}
|
||||
else if (is_funcclause((Node *) clause))
|
||||
else if (IsA(clause, Const))
|
||||
{
|
||||
/* bool constant is pretty easy... */
|
||||
s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
|
||||
}
|
||||
else if (not_clause(clause))
|
||||
{
|
||||
/* inverse of the selectivity of the underlying clause */
|
||||
s1 = 1.0 - compute_clause_selec(root,
|
||||
(Node *) get_notclausearg((Expr *) clause));
|
||||
}
|
||||
else if (and_clause(clause))
|
||||
{
|
||||
/* Use the product of the selectivities of the subclauses.
|
||||
* XXX this is probably too optimistic, since the subclauses
|
||||
* are very likely not independent...
|
||||
*/
|
||||
List *arg;
|
||||
s1 = 1.0;
|
||||
foreach(arg, ((Expr *) clause)->args)
|
||||
{
|
||||
Cost s2 = compute_clause_selec(root, (Node *) lfirst(arg));
|
||||
s1 = s1 * s2;
|
||||
}
|
||||
}
|
||||
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?
|
||||
*/
|
||||
List *arg;
|
||||
s1 = 0.0;
|
||||
foreach(arg, ((Expr *) clause)->args)
|
||||
{
|
||||
Cost s2 = compute_clause_selec(root, (Node *) lfirst(arg));
|
||||
s1 = s1 + s2 - s1 * s2;
|
||||
}
|
||||
}
|
||||
else if (is_funcclause(clause))
|
||||
{
|
||||
/* this isn't an Oper, it's a Func!! */
|
||||
|
||||
/*
|
||||
* 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
|
||||
* SELECTIVITIES THEMSELVES. -- JMH 7/9/92
|
||||
*/
|
||||
s1 = 0.1;
|
||||
s1 = (Cost) 0.3333333;
|
||||
}
|
||||
else if (not_clause((Node *) clause))
|
||||
else if (is_subplan(clause))
|
||||
{
|
||||
/* negate this baby */
|
||||
return 1 - compute_selec(root, ((Expr *) clause)->args, or_selectivities);
|
||||
}
|
||||
else if (is_subplan((Node *) clause))
|
||||
{
|
||||
|
||||
/*
|
||||
* Just for the moment! FIX ME! - vadim 02/04/98
|
||||
*/
|
||||
s1 = 1.0;
|
||||
}
|
||||
else if (NumRelids((Node *) clause) == 1)
|
||||
else if (is_opclause(clause))
|
||||
{
|
||||
|
||||
/*
|
||||
* ...otherwise, calculate s1 from 'clauses'. The clause is not a
|
||||
* join clause, since there is only one relid in the clause. The
|
||||
* clause selectivity will be based on the operator selectivity
|
||||
* and operand values.
|
||||
*/
|
||||
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
|
||||
RegProcedure oprrest = get_oprrest(opno);
|
||||
Oid relid;
|
||||
int relidx;
|
||||
AttrNumber attno;
|
||||
Datum constval;
|
||||
int flag;
|
||||
|
||||
get_relattval((Node *) clause, &relidx, &attno, &constval, &flag);
|
||||
relid = getrelid(relidx, root->rtable);
|
||||
|
||||
/*
|
||||
* if the oprrest procedure is missing for whatever reason, use a
|
||||
* selectivity of 0.5
|
||||
*/
|
||||
if (!oprrest)
|
||||
s1 = (Cost) (0.5);
|
||||
else if (attno == InvalidAttrNumber)
|
||||
if (NumRelids(clause) == 1)
|
||||
{
|
||||
/* The clause is not a join clause, since there is only one
|
||||
* relid in the clause. The clause selectivity will be based on
|
||||
* the operator selectivity and operand values.
|
||||
*/
|
||||
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
|
||||
RegProcedure oprrest = get_oprrest(opno);
|
||||
Oid relid;
|
||||
int relidx;
|
||||
AttrNumber attno;
|
||||
Datum constval;
|
||||
int flag;
|
||||
|
||||
get_relattval(clause, &relidx, &attno, &constval, &flag);
|
||||
relid = getrelid(relidx, root->rtable);
|
||||
|
||||
/*
|
||||
* attno can be Invalid if the clause had a function in it,
|
||||
* i.e. WHERE myFunc(f) = 10
|
||||
* if the oprrest procedure is missing for whatever reason, use a
|
||||
* selectivity of 0.5
|
||||
*/
|
||||
/* this should be FIXED somehow to use function selectivity */
|
||||
s1 = (Cost) (0.5);
|
||||
if (!oprrest)
|
||||
s1 = (Cost) 0.5;
|
||||
else if (attno == InvalidAttrNumber)
|
||||
{
|
||||
/*
|
||||
* attno can be Invalid if the clause had a function in it,
|
||||
* i.e. WHERE myFunc(f) = 10
|
||||
*/
|
||||
/* this should be FIXED somehow to use function selectivity */
|
||||
s1 = (Cost) (0.5);
|
||||
}
|
||||
else
|
||||
s1 = (Cost) restriction_selectivity(oprrest,
|
||||
opno,
|
||||
relid,
|
||||
attno,
|
||||
(char *) constval,
|
||||
flag);
|
||||
}
|
||||
else
|
||||
s1 = (Cost) restriction_selectivity(oprrest,
|
||||
opno,
|
||||
relid,
|
||||
attno,
|
||||
(char *) constval,
|
||||
flag);
|
||||
{
|
||||
/*
|
||||
* The clause must be a join clause. The clause selectivity will
|
||||
* be based on the relations to be scanned and the attributes they
|
||||
* are to be joined on.
|
||||
*/
|
||||
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
|
||||
RegProcedure oprjoin = get_oprjoin(opno);
|
||||
int relid1,
|
||||
relid2;
|
||||
AttrNumber attno1,
|
||||
attno2;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
get_rels_atts(clause, &relid1, &attno1, &relid2, &attno2);
|
||||
relid1 = getrelid(relid1, root->rtable);
|
||||
relid2 = getrelid(relid2, root->rtable);
|
||||
|
||||
/*
|
||||
* The clause must be a join clause. The clause selectivity will
|
||||
* be based on the relations to be scanned and the attributes they
|
||||
* are to be joined on.
|
||||
*/
|
||||
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
|
||||
RegProcedure oprjoin = get_oprjoin(opno);
|
||||
int relid1,
|
||||
relid2;
|
||||
AttrNumber attno1,
|
||||
attno2;
|
||||
|
||||
get_rels_atts((Node *) clause, &relid1, &attno1, &relid2, &attno2);
|
||||
relid1 = getrelid(relid1, root->rtable);
|
||||
relid2 = getrelid(relid2, root->rtable);
|
||||
|
||||
/*
|
||||
* if the oprjoin procedure is missing for whatever reason, use a
|
||||
* selectivity of 0.5
|
||||
*/
|
||||
if (!oprjoin)
|
||||
s1 = (Cost) (0.5);
|
||||
else
|
||||
s1 = (Cost) join_selectivity(oprjoin,
|
||||
opno,
|
||||
relid1,
|
||||
attno1,
|
||||
relid2,
|
||||
attno2);
|
||||
/*
|
||||
* if the oprjoin procedure is missing for whatever reason, use a
|
||||
* selectivity of 0.5
|
||||
*/
|
||||
if (!oprjoin)
|
||||
s1 = (Cost) (0.5);
|
||||
else
|
||||
s1 = (Cost) join_selectivity(oprjoin,
|
||||
opno,
|
||||
relid1,
|
||||
attno1,
|
||||
relid2,
|
||||
attno2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A null clause list eliminates no tuples, so return a selectivity of
|
||||
* 1.0. If there is only one clause, the selectivity is not that of
|
||||
* an 'or' clause, but rather that of the single clause.
|
||||
*/
|
||||
|
||||
if (lnext(clauses) == NIL)
|
||||
return s1;
|
||||
else
|
||||
{
|
||||
/* Compute selectivity of the 'or'ed subclauses. */
|
||||
/* Added check for taking lnext(NIL). -- JMH 3/9/92 */
|
||||
Cost s2;
|
||||
|
||||
if (or_selectivities != NIL)
|
||||
s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities));
|
||||
else
|
||||
s2 = compute_selec(root, lnext(clauses), NIL);
|
||||
return s1 + s2 - s1 * s2;
|
||||
}
|
||||
return s1;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.62 1999/07/23 03:34:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.63 1999/07/24 23:21:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -109,12 +109,25 @@ create_index_paths(Query *root,
|
||||
continue;
|
||||
|
||||
/*
|
||||
* 1. Try matching the index against subclauses of an 'or' clause.
|
||||
* The fields of the restrictinfo nodes are marked with lists of
|
||||
* the matching indices. No paths are actually created. We
|
||||
* currently only look to match the first key. We don't find
|
||||
* multi-key index cases where an AND matches the first key, and
|
||||
* the OR matches the second key.
|
||||
* 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 currently only look to match the first key of each index against
|
||||
* 'or' subclauses. There are cases where a later key of a multi-key
|
||||
* index could be used (if other top-level clauses match earlier keys
|
||||
* of the index), but our poor brains are hurting already...
|
||||
*
|
||||
* We don't even think about special handling of 'or' clauses that
|
||||
* involve more than one relation, since they can't be processed by
|
||||
* a single indexscan path anyway. Currently, cnfify() is certain
|
||||
* to have restructured any such toplevel 'or' clauses anyway.
|
||||
*/
|
||||
match_index_orclauses(rel,
|
||||
index,
|
||||
@ -123,7 +136,7 @@ create_index_paths(Query *root,
|
||||
restrictinfo_list);
|
||||
|
||||
/*
|
||||
* 2. If the keys of this index match any of the available
|
||||
* 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.
|
||||
*/
|
||||
@ -179,11 +192,14 @@ create_index_paths(Query *root,
|
||||
/*
|
||||
* match_index_orclauses
|
||||
* Attempt to match an index against subclauses within 'or' clauses.
|
||||
* If the index does match, then the clause is marked with information
|
||||
* about the index.
|
||||
* Each subclause that does match is marked with the index's node.
|
||||
*
|
||||
* Essentially, this adds 'index' to the list of indices in the
|
||||
* RestrictInfo field of each of the clauses which it matches.
|
||||
* Essentially, this adds 'index' to the list of subclause indices in
|
||||
* the RestrictInfo field of each of the 'or' clauses where it matches.
|
||||
* NOTE: we can use storage in the RestrictInfo for this purpose because
|
||||
* this processing is only done on single-relation restriction clauses.
|
||||
* Therefore, we will never have indexes for more than one relation
|
||||
* mentioned in the same RestrictInfo node's list.
|
||||
*
|
||||
* 'rel' is the node of the relation on which the index is defined.
|
||||
* 'index' is the index node.
|
||||
@ -204,12 +220,11 @@ match_index_orclauses(RelOptInfo *rel,
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
|
||||
|
||||
if (valid_or_clause(restrictinfo))
|
||||
if (restriction_is_or_clause(restrictinfo))
|
||||
{
|
||||
/*
|
||||
* Mark the 'or' clause with a list of indices which match
|
||||
* each of its subclauses. We add entries to the existing
|
||||
* list, if any.
|
||||
* Add this index to the subclause index list for each
|
||||
* subclause that it matches.
|
||||
*/
|
||||
restrictinfo->indexids =
|
||||
match_index_orclause(rel, index,
|
||||
@ -253,7 +268,9 @@ match_index_orclause(RelOptInfo *rel,
|
||||
List *index_list;
|
||||
List *clist;
|
||||
|
||||
/* first time through, we create empty 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)
|
||||
{
|
||||
matching_indices = NIL;
|
||||
@ -1186,9 +1203,13 @@ index_innerjoin(Query *root, RelOptInfo *rel, List *clausegroup_list,
|
||||
pathnode->path.pathorder->ord.sortop = index->ordering;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
/* Note that we are making a pathnode for a single-scan indexscan;
|
||||
* therefore, both indexid and indexqual should be single-element
|
||||
* lists.
|
||||
*/
|
||||
pathnode->indexid = index->relids;
|
||||
pathnode->indexkeys = index->indexkeys;
|
||||
pathnode->indexqual = clausegroup;
|
||||
pathnode->indexqual = lcons(get_actual_clauses(clausegroup), NIL);
|
||||
|
||||
pathnode->path.joinid = ((RestrictInfo *) lfirst(clausegroup))->restrictinfojoinid;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.28 1999/07/16 04:59:15 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.29 1999/07/24 23:21:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -26,61 +26,66 @@
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
|
||||
static void best_or_subclause_indices(Query *root, RelOptInfo *rel, List *subclauses,
|
||||
List *indices, List **indexids, Cost *cost, Cost *selec);
|
||||
static void best_or_subclause_index(Query *root, RelOptInfo *rel, Expr *subclause,
|
||||
List *indices, int *indexid, Cost *cost, Cost *selec);
|
||||
static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
|
||||
List *subclauses, List *indices,
|
||||
List **indexids,
|
||||
Cost *cost, Cost *selec);
|
||||
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
|
||||
Expr *subclause, List *indices,
|
||||
int *indexid, Cost *cost, Cost *selec);
|
||||
|
||||
|
||||
/*
|
||||
* create_or_index_paths
|
||||
* Creates index paths for indices that match 'or' clauses.
|
||||
* create_index_paths() must already have been called.
|
||||
*
|
||||
* 'rel' is the relation entry for which the paths are to be defined on
|
||||
* 'clauses' is the list of available restriction clause nodes
|
||||
*
|
||||
* Returns a list of these index path nodes.
|
||||
* Returns a list of index path nodes.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
create_or_index_paths(Query *root,
|
||||
RelOptInfo *rel, List *clauses)
|
||||
{
|
||||
List *t_list = NIL;
|
||||
List *path_list = NIL;
|
||||
List *clist;
|
||||
|
||||
foreach(clist, clauses)
|
||||
{
|
||||
RestrictInfo *clausenode = (RestrictInfo *) (lfirst(clist));
|
||||
RestrictInfo *clausenode = (RestrictInfo *) lfirst(clist);
|
||||
|
||||
/*
|
||||
* 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 'Index field was set in
|
||||
* (match_or) if no index matches a given subclause, one of the
|
||||
* lists of index nodes returned by (get_index) will be 'nil').
|
||||
* has been matched by an index. The information used was
|
||||
* saved by create_index_paths().
|
||||
*/
|
||||
if (valid_or_clause(clausenode) &&
|
||||
if (restriction_is_or_clause(clausenode) &&
|
||||
clausenode->indexids)
|
||||
{
|
||||
List *temp = NIL;
|
||||
List *index_list = NIL;
|
||||
bool index_flag = true;
|
||||
bool all_indexable = true;
|
||||
List *temp;
|
||||
|
||||
index_list = clausenode->indexids;
|
||||
foreach(temp, index_list)
|
||||
foreach(temp, clausenode->indexids)
|
||||
{
|
||||
if (!lfirst(temp))
|
||||
if (lfirst(temp) == NIL)
|
||||
{
|
||||
index_flag = false;
|
||||
all_indexable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* do they all have indexes? */
|
||||
if (index_flag)
|
||||
{ /* used to be a lisp every function */
|
||||
if (all_indexable)
|
||||
{
|
||||
/*
|
||||
* OK, build an IndexPath for this OR clause, using the
|
||||
* best available index for each subclause.
|
||||
*/
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
List *indexids = NIL;
|
||||
List *indexids;
|
||||
List *orclause;
|
||||
Cost cost;
|
||||
Cost selec;
|
||||
|
||||
@ -98,16 +103,35 @@ create_or_index_paths(Query *root,
|
||||
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
|
||||
|
||||
/*
|
||||
* This is an IndexScan, but it does index lookups based
|
||||
* on the order of the fields specified in the WHERE
|
||||
* clause, not in any order, so the sortop is NULL.
|
||||
* 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.pathorder->ord.sortop = NULL;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
pathnode->indexqual = lcons(clausenode, NIL);
|
||||
/*
|
||||
* Generate an indexqual list from the OR clause's args.
|
||||
* We want two levels of sublist: the first is implicit OR
|
||||
* and the second is implicit AND. (Currently, we will never
|
||||
* see a sub-AND-clause because of cnfify(), but someday maybe
|
||||
* the code below will do something useful...)
|
||||
*/
|
||||
pathnode->indexqual = NIL;
|
||||
foreach(orclause, clausenode->clause->args)
|
||||
{
|
||||
List *sublist;
|
||||
if (and_clause(lfirst(orclause)))
|
||||
sublist = ((Expr *) lfirst(orclause))->args;
|
||||
else
|
||||
sublist = lcons(lfirst(orclause), NIL);
|
||||
pathnode->indexqual = lappend(pathnode->indexqual,
|
||||
sublist);
|
||||
}
|
||||
pathnode->indexid = indexids;
|
||||
pathnode->path.path_cost = cost;
|
||||
clausenode->selectivity = (Cost) selec;
|
||||
|
||||
/*
|
||||
* copy restrictinfo list into path for expensive function
|
||||
@ -121,33 +145,28 @@ create_or_index_paths(Query *root,
|
||||
if (XfuncMode != XFUNC_OFF)
|
||||
((Path *) pathnode)->path_cost += xfunc_get_path_cost((Path) pathnode);
|
||||
#endif
|
||||
clausenode->selectivity = (Cost) selec;
|
||||
t_list = lappend(t_list, pathnode);
|
||||
path_list = lappend(path_list, pathnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t_list;
|
||||
return path_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* best_or_subclause_indices
|
||||
* Determines the best index to be used in conjunction with each subclause
|
||||
* of an 'or' clause and the cost of scanning a relation using these
|
||||
* indices. The cost is the sum of the individual index costs.
|
||||
* indices. The cost is the sum of the individual index costs, since
|
||||
* the executor will perform a scan for each subclause of the 'or'.
|
||||
*
|
||||
* 'rel' is the node of the relation on which the index is defined
|
||||
* 'rel' is the node of the relation on which the indexes are defined
|
||||
* 'subclauses' are the subclauses of the 'or' clause
|
||||
* 'indices' are those index nodes that matched subclauses of the 'or'
|
||||
* clause
|
||||
* 'examined_indexids' is a list of those index ids to be used with
|
||||
* subclauses that have already been examined
|
||||
* 'subcost' is the cost of using the indices in 'examined_indexids'
|
||||
* 'selec' is a list of all subclauses that have already been examined
|
||||
*
|
||||
* Returns a list of the indexids, cost, and selectivities of each
|
||||
* subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID,
|
||||
* 'cost' is a flonum, and 's' is a flonum.
|
||||
* 'indices' is a list of sublists of the index nodes that matched each
|
||||
* subclause of the 'or' clause
|
||||
* '*indexids' gets a list of the best index ID to use for each subclause
|
||||
* '*cost' gets the total cost of the path
|
||||
* '*selec' gets the total selectivity of the path.
|
||||
*/
|
||||
static void
|
||||
best_or_subclause_indices(Query *root,
|
||||
@ -155,11 +174,12 @@ best_or_subclause_indices(Query *root,
|
||||
List *subclauses,
|
||||
List *indices,
|
||||
List **indexids, /* return value */
|
||||
Cost *cost, /* return value */
|
||||
Cost *selec) /* return value */
|
||||
Cost *cost, /* return value */
|
||||
Cost *selec) /* return value */
|
||||
{
|
||||
List *slist;
|
||||
|
||||
*indexids = NIL;
|
||||
*selec = (Cost) 0.0;
|
||||
*cost = (Cost) 0.0;
|
||||
|
||||
@ -180,8 +200,6 @@ best_or_subclause_indices(Query *root,
|
||||
|
||||
indices = lnext(indices);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -193,10 +211,9 @@ best_or_subclause_indices(Query *root,
|
||||
* 'rel' is the node of the relation on which the index is defined
|
||||
* 'subclause' is the subclause
|
||||
* 'indices' is a list of index nodes that match the subclause
|
||||
*
|
||||
* Returns a list (index_id index_subcost index_selectivity)
|
||||
* (a fixnum, a fixnum, and a flonum respectively).
|
||||
*
|
||||
* '*retIndexid' gets the ID of the best index
|
||||
* '*retCost' gets the cost of a scan with that index
|
||||
* '*retSelec' gets the selectivity of that scan
|
||||
*/
|
||||
static void
|
||||
best_or_subclause_index(Query *root,
|
||||
@ -207,49 +224,60 @@ best_or_subclause_index(Query *root,
|
||||
Cost *retCost, /* return value */
|
||||
Cost *retSelec) /* return value */
|
||||
{
|
||||
List *ilist;
|
||||
Oid relid = getrelid(lfirsti(rel->relids),
|
||||
root->rtable);
|
||||
Oid opno = ((Oper *) subclause->oper)->opno;
|
||||
AttrNumber attno = (get_leftop(subclause))->varattno;
|
||||
bool constant_on_right = non_null((Expr *) get_rightop(subclause));
|
||||
Datum value;
|
||||
int flag;
|
||||
List *opnos,
|
||||
*attnos,
|
||||
*values,
|
||||
*flags;
|
||||
bool first_run = true;
|
||||
List *ilist;
|
||||
|
||||
/* if we don't match anything, return zeros */
|
||||
*retIndexid = 0;
|
||||
*retCost = 0.0;
|
||||
*retSelec = 0.0;
|
||||
*retCost = (Cost) 0.0;
|
||||
*retSelec = (Cost) 0.0;
|
||||
|
||||
if (constant_on_right) /* XXX looks pretty bogus ... tgl */
|
||||
value = ((Const *) get_rightop(subclause))->constvalue;
|
||||
else
|
||||
value = NameGetDatum("");
|
||||
if (constant_on_right)
|
||||
flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_);
|
||||
else
|
||||
flag = _SELEC_CONSTANT_RIGHT_;
|
||||
|
||||
/* prebuild lists since we will pass same list to each index */
|
||||
opnos = lconsi(opno, NIL);
|
||||
attnos = lconsi(attno, NIL);
|
||||
values = lconsi(value, NIL);
|
||||
flags = lconsi(flag, NIL);
|
||||
|
||||
foreach(ilist, indices)
|
||||
{
|
||||
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
|
||||
|
||||
Datum value;
|
||||
int flag = 0;
|
||||
Oid indexid = (Oid) lfirsti(index->relids);
|
||||
Cost subcost;
|
||||
AttrNumber attno = (get_leftop(subclause))->varattno;
|
||||
Oid opno = ((Oper *) subclause->oper)->opno;
|
||||
bool constant_on_right = non_null((Expr *) get_rightop(subclause));
|
||||
float npages,
|
||||
selec;
|
||||
|
||||
if (constant_on_right)
|
||||
value = ((Const *) get_rightop(subclause))->constvalue;
|
||||
else
|
||||
value = NameGetDatum("");
|
||||
if (constant_on_right)
|
||||
flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_);
|
||||
else
|
||||
flag = _SELEC_CONSTANT_RIGHT_;
|
||||
|
||||
index_selectivity(lfirsti(index->relids),
|
||||
index_selectivity(indexid,
|
||||
index->classlist,
|
||||
lconsi(opno, NIL),
|
||||
getrelid(lfirsti(rel->relids),
|
||||
root->rtable),
|
||||
lconsi(attno, NIL),
|
||||
lconsi(value, NIL),
|
||||
lconsi(flag, NIL),
|
||||
opnos,
|
||||
relid,
|
||||
attnos,
|
||||
values,
|
||||
flags,
|
||||
1,
|
||||
&npages,
|
||||
&selec);
|
||||
|
||||
subcost = cost_index((Oid) lfirsti(index->relids),
|
||||
subcost = cost_index(indexid,
|
||||
(int) npages,
|
||||
(Cost) selec,
|
||||
rel->pages,
|
||||
@ -260,12 +288,11 @@ best_or_subclause_index(Query *root,
|
||||
|
||||
if (first_run || subcost < *retCost)
|
||||
{
|
||||
*retIndexid = lfirsti(index->relids);
|
||||
*retIndexid = indexid;
|
||||
*retCost = subcost;
|
||||
*retSelec = selec;
|
||||
first_run = false;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.62 1999/07/17 20:17:13 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.63 1999/07/24 23:21:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -301,54 +301,30 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
|
||||
* create_indexscan_node
|
||||
* Returns a indexscan node for the base relation scanned by 'best_path'
|
||||
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
|
||||
*
|
||||
* If an 'or' clause is to be used with this index, the indxqual field
|
||||
* will contain a list of the 'or' clause arguments, e.g., the
|
||||
* clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
|
||||
* indxqual will simply contain one conjunctive qualification: ((a)).
|
||||
*/
|
||||
static IndexScan *
|
||||
create_indexscan_node(IndexPath *best_path,
|
||||
List *tlist,
|
||||
List *scan_clauses)
|
||||
{
|
||||
|
||||
/*
|
||||
* Extract the(first if conjunct, only if disjunct) clause from the
|
||||
* restrictinfo list.
|
||||
*/
|
||||
Expr *index_clause = (Expr *) NULL;
|
||||
List *indxqual = NIL;
|
||||
List *qpqual = NIL;
|
||||
List *fixed_indxqual = NIL;
|
||||
List *indxqual = best_path->indexqual;
|
||||
List *qpqual;
|
||||
List *fixed_indxqual;
|
||||
List *ixid;
|
||||
IndexScan *scan_node = (IndexScan *) NULL;
|
||||
bool lossy = FALSE;
|
||||
HeapTuple indexTuple;
|
||||
Form_pg_index index;
|
||||
|
||||
/*
|
||||
* If an 'or' clause is to be used with this index, the indxqual field
|
||||
* will contain a list of the 'or' clause arguments, e.g., the
|
||||
* clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
|
||||
* indxqual will simply contain one conjunctive qualification: ((a)).
|
||||
*/
|
||||
if (best_path->indexqual != NULL)
|
||||
/* added call to fix_opids, JMH 6/23/92 */
|
||||
index_clause = (Expr *)
|
||||
lfirst(fix_opids(get_actual_clauses(best_path->indexqual)));
|
||||
|
||||
if (or_clause((Node *) index_clause))
|
||||
{
|
||||
List *temp = NIL;
|
||||
|
||||
foreach(temp, index_clause->args)
|
||||
indxqual = lappend(indxqual, lcons(lfirst(temp), NIL));
|
||||
}
|
||||
else
|
||||
{
|
||||
indxqual = lcons(get_actual_clauses(best_path->indexqual),
|
||||
NIL);
|
||||
}
|
||||
IndexScan *scan_node;
|
||||
bool lossy = false;
|
||||
|
||||
/* check and see if any indices are lossy */
|
||||
foreach(ixid, best_path->indexid)
|
||||
{
|
||||
HeapTuple indexTuple;
|
||||
Form_pg_index index;
|
||||
|
||||
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
||||
ObjectIdGetDatum(lfirsti(ixid)),
|
||||
0, 0, 0);
|
||||
@ -356,34 +332,70 @@ create_indexscan_node(IndexPath *best_path,
|
||||
elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
|
||||
index = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
if (index->indislossy)
|
||||
lossy = TRUE;
|
||||
{
|
||||
lossy = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The qpqual field contains all restrictions not automatically
|
||||
* The qpqual list must contain all restrictions not automatically
|
||||
* handled by the index. Note that for non-lossy indices, the
|
||||
* predicates in the indxqual are handled by the index, while for
|
||||
* lossy indices the indxqual predicates need to be double-checked
|
||||
* after the index fetches the best-guess tuples.
|
||||
*
|
||||
* There should not be any clauses in scan_clauses that duplicate
|
||||
* expressions checked by the index, but just in case, we will
|
||||
* get rid of them via set_difference.
|
||||
*/
|
||||
if (or_clause((Node *) index_clause))
|
||||
if (length(indxqual) > 1)
|
||||
{
|
||||
/*
|
||||
* Build an expression representation of the indexqual, expanding
|
||||
* the implicit OR and AND semantics of the first- and second-level
|
||||
* lists. XXX Is it really necessary to do a deep copy here?
|
||||
*/
|
||||
List *orclauses = NIL;
|
||||
List *orclause;
|
||||
Expr *indxqual_expr;
|
||||
|
||||
foreach(orclause, indxqual)
|
||||
{
|
||||
orclauses = lappend(orclauses,
|
||||
make_ands_explicit((List *) copyObject(lfirst(orclause))));
|
||||
}
|
||||
indxqual_expr = make_orclause(orclauses);
|
||||
|
||||
/* this set_difference is almost certainly a waste of time... */
|
||||
qpqual = set_difference(scan_clauses,
|
||||
lcons(index_clause, NIL));
|
||||
lcons(indxqual_expr, NIL));
|
||||
|
||||
if (lossy)
|
||||
qpqual = lappend(qpqual, (List *) copyObject(index_clause));
|
||||
qpqual = lappend(qpqual, indxqual_expr);
|
||||
}
|
||||
else
|
||||
else if (indxqual != NIL)
|
||||
{
|
||||
/* Here, we can simply treat the first sublist as an independent
|
||||
* set of qual expressions, since there is no top-level OR behavior.
|
||||
*/
|
||||
qpqual = set_difference(scan_clauses, lfirst(indxqual));
|
||||
if (lossy)
|
||||
qpqual = nconc(qpqual,
|
||||
(List *) copyObject(lfirst(indxqual)));
|
||||
qpqual = nconc(qpqual, (List *) copyObject(lfirst(indxqual)));
|
||||
}
|
||||
else
|
||||
qpqual = NIL;
|
||||
|
||||
fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual, (Path *) best_path);
|
||||
/*
|
||||
* Fix opids in the completed indxqual. We don't want to do this sooner
|
||||
* since it would screw up the set_difference calcs above. Really,
|
||||
* this ought to only happen at final exit from the planner...
|
||||
*/
|
||||
indxqual = fix_opids(indxqual);
|
||||
|
||||
/* The executor needs a copy with index attrs substituted for table ones */
|
||||
fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual,
|
||||
(Path *) best_path);
|
||||
|
||||
scan_node = make_indexscan(tlist,
|
||||
qpqual,
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.34 1999/07/16 04:59:19 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.35 1999/07/24 23:21:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -25,12 +25,11 @@
|
||||
#include "optimizer/var.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
extern int Quiet;
|
||||
|
||||
static void add_restrict_and_join_to_rel(Query *root, List *clause);
|
||||
static void add_restrict_and_join_to_rel(Query *root, Node *clause);
|
||||
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
Relids join_relids);
|
||||
static void add_vars_to_targetlist(Query *root, List *vars, Relids join_relids);
|
||||
static void add_vars_to_targetlist(Query *root, List *vars);
|
||||
|
||||
static MergeOrder *mergejoinop(Expr *clause);
|
||||
static Oid hashjoinop(Expr *clause);
|
||||
@ -129,7 +128,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist)
|
||||
* relations appearing within clauses. Creates new relation entries if
|
||||
* necessary, adding them to *query_relation_list*.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
* 'clauses': the list of clauses in the cnfify'd query qualification.
|
||||
*/
|
||||
void
|
||||
add_restrict_and_join_to_rels(Query *root, List *clauses)
|
||||
@ -137,95 +136,71 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
|
||||
List *clause;
|
||||
|
||||
foreach(clause, clauses)
|
||||
add_restrict_and_join_to_rel(root, lfirst(clause));
|
||||
return;
|
||||
add_restrict_and_join_to_rel(root, (Node*) lfirst(clause));
|
||||
}
|
||||
|
||||
/*
|
||||
* add_restrict_and_join_to_rel-
|
||||
* Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
|
||||
* of a relation entry(depending on whether or not the clause is a join)
|
||||
* of a relation entry (depending on whether or not the clause is a join)
|
||||
* by creating a new RestrictInfo node and setting appropriate fields
|
||||
* within the nodes.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*/
|
||||
static void
|
||||
add_restrict_and_join_to_rel(Query *root, List *clause)
|
||||
add_restrict_and_join_to_rel(Query *root, Node *clause)
|
||||
{
|
||||
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
||||
Relids relids;
|
||||
List *vars;
|
||||
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
||||
|
||||
/*
|
||||
* Retrieve all relids and vars contained within the clause.
|
||||
*/
|
||||
clause_get_relids_vars((Node *) clause, &relids, &vars);
|
||||
|
||||
restrictinfo->clause = (Expr *) clause;
|
||||
restrictinfo->notclause = contains_not((Node *) clause);
|
||||
restrictinfo->selectivity = 0;
|
||||
restrictinfo->indexids = NIL;
|
||||
restrictinfo->mergejoinorder = (MergeOrder *) NULL;
|
||||
restrictinfo->hashjoinoperator = (Oid) 0;
|
||||
|
||||
/*
|
||||
* The selectivity of the clause must be computed regardless of
|
||||
* whether it's a restriction or a join clause
|
||||
*/
|
||||
restrictinfo->selectivity = compute_clause_selec(root, clause);
|
||||
|
||||
/*
|
||||
* Retrieve all relids and vars contained within the clause.
|
||||
*/
|
||||
clause_get_relids_vars(clause, &relids, &vars);
|
||||
|
||||
if (length(relids) == 1)
|
||||
{
|
||||
|
||||
/*
|
||||
* There is only one relation participating in 'clause', so
|
||||
* 'clause' must be a restriction clause.
|
||||
* 'clause' must be a restriction clause for that relation.
|
||||
*/
|
||||
RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
|
||||
|
||||
/*
|
||||
* The selectivity of the clause must be computed regardless of
|
||||
* whether it's a restriction or a join clause
|
||||
*/
|
||||
if (is_funcclause((Node *) clause))
|
||||
|
||||
/*
|
||||
* XXX If we have a func clause set selectivity to 1/3, really
|
||||
* need a true selectivity function.
|
||||
*/
|
||||
restrictinfo->selectivity = (Cost) 0.3333333;
|
||||
else
|
||||
restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
|
||||
|
||||
rel->restrictinfo = lcons(restrictinfo, rel->restrictinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* 'clause' is a join clause, since there is more than one atom in
|
||||
* the relid list.
|
||||
* the relid list. Add it to the join lists of all the relevant
|
||||
* relations. (If, perchance, 'clause' contains NO vars, then
|
||||
* nothing will happen...)
|
||||
*/
|
||||
if (is_funcclause((Node *) clause))
|
||||
|
||||
/*
|
||||
* XXX If we have a func clause set selectivity to 1/3, really
|
||||
* need a true selectivity function.
|
||||
*/
|
||||
restrictinfo->selectivity = (Cost) 0.3333333;
|
||||
else
|
||||
restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
|
||||
|
||||
add_join_info_to_rels(root, restrictinfo, relids);
|
||||
/* we are going to be doing a join, so add var to targetlist */
|
||||
add_vars_to_targetlist(root, vars, relids);
|
||||
/* we are going to be doing a join, so add vars to targetlists */
|
||||
add_vars_to_targetlist(root, vars);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add_join_info_to_rels
|
||||
* For every relation participating in a join clause, add 'restrictinfo' to
|
||||
* the appropriate joininfo node(creating a new one and adding it to the
|
||||
* the appropriate joininfo node (creating a new one and adding it to the
|
||||
* appropriate rel node if necessary).
|
||||
*
|
||||
* 'restrictinfo' describes the join clause
|
||||
* 'join_relids' is the list of relations participating in the join clause
|
||||
*
|
||||
*/
|
||||
static void
|
||||
add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
@ -233,7 +208,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
{
|
||||
List *join_relid;
|
||||
|
||||
/* For every relid, find the rel, and add the proper join entries */
|
||||
/* For every relid, find the joininfo, and add the proper join entries */
|
||||
foreach(join_relid, join_relids)
|
||||
{
|
||||
JoinInfo *joininfo;
|
||||
@ -247,43 +222,39 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
unjoined_relids = lappendi(unjoined_relids, lfirsti(rel));
|
||||
}
|
||||
|
||||
/*
|
||||
* Find or make the joininfo node for this combination of rels
|
||||
*/
|
||||
joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
|
||||
unjoined_relids);
|
||||
|
||||
/*
|
||||
* And add the restrictinfo node to it. NOTE that each joininfo
|
||||
* gets its own copy of the restrictinfo node! (Is this really
|
||||
* necessary? Possibly ... later parts of the optimizer destructively
|
||||
* modify restrict/join clauses...)
|
||||
*/
|
||||
joininfo->jinfo_restrictinfo = lcons(copyObject((void *) restrictinfo),
|
||||
joininfo->jinfo_restrictinfo);
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add_vars_to_targetlist
|
||||
* For each variable appearing in a clause,
|
||||
* (1) If a targetlist entry for the variable is not already present in
|
||||
* the appropriate relation's target list, add one.
|
||||
* (2) If a targetlist entry is already present, but the var is part of a
|
||||
* join clause, add the relids of the join relations to the JoinList
|
||||
* entry of the targetlist entry.
|
||||
*
|
||||
* 'vars' is the list of var nodes
|
||||
* 'join_relids' is the list of relids appearing in the join clause
|
||||
* (if this is a join clause)
|
||||
*
|
||||
* Returns nothing.
|
||||
* For each variable appearing in a clause, add it to the relation's
|
||||
* targetlist if not already present.
|
||||
*/
|
||||
static void
|
||||
add_vars_to_targetlist(Query *root, List *vars, Relids join_relids)
|
||||
add_vars_to_targetlist(Query *root, List *vars)
|
||||
{
|
||||
Var *var;
|
||||
List *temp = NIL;
|
||||
RelOptInfo *rel = (RelOptInfo *) NULL;
|
||||
TargetEntry *tlistentry;
|
||||
List *temp;
|
||||
|
||||
foreach(temp, vars)
|
||||
{
|
||||
var = (Var *) lfirst(temp);
|
||||
rel = get_base_rel(root, var->varno);
|
||||
tlistentry = tlistentry_member(var, rel->targetlist);
|
||||
if (tlistentry == NULL)
|
||||
/* add a new entry */
|
||||
Var *var = (Var *) lfirst(temp);
|
||||
RelOptInfo *rel = get_base_rel(root, var->varno);
|
||||
|
||||
if (tlistentry_member(var, rel->targetlist) == NULL)
|
||||
add_var_to_tlist(rel, var);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.40 1999/07/16 04:59:23 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.41 1999/07/24 23:21:13 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -290,6 +290,21 @@ make_andclause(List *andclauses)
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sometimes (such as in the result of cnfify), we use lists of expression
|
||||
* nodes with implicit AND semantics. This function converts back to an
|
||||
* explicit-AND representation.
|
||||
*/
|
||||
Expr *
|
||||
make_ands_explicit(List *andclauses)
|
||||
{
|
||||
if (andclauses == NIL)
|
||||
return NULL;
|
||||
else if (length(andclauses) == 1)
|
||||
return (Expr *) lfirst(andclauses);
|
||||
else
|
||||
return make_andclause(andclauses);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* CASE clause functions
|
||||
@ -410,38 +425,6 @@ NumRelids(Node *clause)
|
||||
return length(var_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* contains_not
|
||||
*
|
||||
* Returns t iff the clause is a 'not' clause or if any of the
|
||||
* subclauses within an 'or' clause contain 'not's.
|
||||
*
|
||||
* NOTE that only the top-level AND/OR structure is searched for NOTs;
|
||||
* we are not interested in buried substructure.
|
||||
*/
|
||||
bool
|
||||
contains_not(Node *clause)
|
||||
{
|
||||
if (single_node(clause))
|
||||
return false;
|
||||
|
||||
if (not_clause(clause))
|
||||
return true;
|
||||
|
||||
if (or_clause(clause) || and_clause(clause))
|
||||
{
|
||||
List *a;
|
||||
|
||||
foreach(a, ((Expr *) clause)->args)
|
||||
{
|
||||
if (contains_not(lfirst(a)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_joinable
|
||||
*
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.46 1999/07/16 04:59:25 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.47 1999/07/24 23:21:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -426,7 +426,8 @@ create_index_path(Query *root,
|
||||
/* each clause gets an equal selectivity */
|
||||
clausesel = pow(selec, 1.0 / (double) length(restriction_clauses));
|
||||
|
||||
pathnode->indexqual = restriction_clauses;
|
||||
pathnode->indexqual = lcons(get_actual_clauses(restriction_clauses),
|
||||
NIL);
|
||||
pathnode->path.path_cost = cost_index(lfirsti(index->relids),
|
||||
(int) npages,
|
||||
selec,
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.6 1999/07/16 04:59:27 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.7 1999/07/24 23:21:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -20,17 +20,15 @@
|
||||
#include "optimizer/restrictinfo.h"
|
||||
|
||||
/*
|
||||
* valid_or_clause
|
||||
* restriction_is_or_clause
|
||||
*
|
||||
* Returns t iff the restrictinfo node contains a 'normal' 'or' clause.
|
||||
* Returns t iff the restrictinfo node contains an 'or' clause.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
valid_or_clause(RestrictInfo *restrictinfo)
|
||||
restriction_is_or_clause(RestrictInfo *restrictinfo)
|
||||
{
|
||||
if (restrictinfo != NULL &&
|
||||
!single_node((Node *) restrictinfo->clause) &&
|
||||
!restrictinfo->notclause &&
|
||||
or_clause((Node *) restrictinfo->clause))
|
||||
return true;
|
||||
else
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: relation.h,v 1.34 1999/07/15 23:03:56 momjian Exp $
|
||||
* $Id: relation.h,v 1.35 1999/07/24 23:21:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -151,6 +151,23 @@ typedef struct Path
|
||||
List *loc_restrictinfo;
|
||||
} Path;
|
||||
|
||||
/*----------
|
||||
* IndexPath represents an index scan. Although an indexscan can only read
|
||||
* a single relation, it can scan it more than once, potentially using a
|
||||
* different index during each scan. The result is the union (OR) of all the
|
||||
* tuples matched during any scan. (The executor is smart enough not to return
|
||||
* the same tuple more than once, even if it is matched in multiple scans.)
|
||||
* 'indexid' is a list of index relation OIDs, one per scan to be performed.
|
||||
* 'indexqual' is a list of index qualifications, also one per scan.
|
||||
* Each entry in 'indexqual' is a sublist of qualification expressions with
|
||||
* implicit AND semantics across the sublist items. Each one of the sublist
|
||||
* items must be an operator expression of the form (var op something) or
|
||||
* (something op var), where the var is a field the associated index keys on
|
||||
* and the op is a member of the operator class of the index.
|
||||
* NOTE that the semantics of the top-level list in 'indexqual' is OR
|
||||
* combination, while the sublists are implicitly AND combinations!
|
||||
*----------
|
||||
*/
|
||||
typedef struct IndexPath
|
||||
{
|
||||
Path path;
|
||||
@ -205,16 +222,20 @@ typedef struct JoinKey
|
||||
} JoinKey;
|
||||
|
||||
/*
|
||||
* clause info
|
||||
* Restriction clause info.
|
||||
* We create one of these for each AND sub-clause of a restriction condition
|
||||
* (WHERE clause). Since the restriction clauses are logically ANDed, we
|
||||
* can use any one of them or any subset of them to filter out tuples,
|
||||
* without having to evaluate the rest. The RestrictInfo node itself stores
|
||||
* data used by the optimizer while choosing the best query plan.
|
||||
*/
|
||||
|
||||
typedef struct RestrictInfo
|
||||
{
|
||||
NodeTag type;
|
||||
Expr *clause; /* should be an OP clause */
|
||||
Cost selectivity;
|
||||
bool notclause;
|
||||
List *indexids;
|
||||
Expr *clause; /* the represented subclause of WHERE cond */
|
||||
Cost selectivity; /* estimated selectivity */
|
||||
List *indexids; /* subclause index IDs if clause is an OR */
|
||||
|
||||
/* mergejoin only */
|
||||
MergeOrder *mergejoinorder;
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: clauses.h,v 1.21 1999/07/15 23:03:57 momjian Exp $
|
||||
* $Id: clauses.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -34,13 +34,13 @@ extern Expr *get_notclausearg(Expr *notclause);
|
||||
|
||||
extern bool and_clause(Node *clause);
|
||||
extern Expr *make_andclause(List *andclauses);
|
||||
extern Expr *make_ands_explicit(List *andclauses);
|
||||
|
||||
extern bool case_clause(Node *clause);
|
||||
|
||||
extern List *pull_constant_clauses(List *quals, List **constantQual);
|
||||
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
|
||||
extern int NumRelids(Node *clause);
|
||||
extern bool contains_not(Node *clause);
|
||||
extern bool is_joinable(Node *clause);
|
||||
extern bool qual_clause_p(Node *clause);
|
||||
extern void fix_opid(Node *clause);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: cost.h,v 1.21 1999/07/15 15:21:20 momjian Exp $
|
||||
* $Id: cost.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -59,7 +59,6 @@ extern void set_clause_selectivities(List *restrictinfo_list, Cost new_selectivi
|
||||
extern Cost product_selec(List *restrictinfo_list);
|
||||
extern void set_rest_relselec(Query *root, List *rel_list);
|
||||
extern void set_rest_selec(Query *root, List *restrictinfo_list);
|
||||
extern Cost compute_clause_selec(Query *root,
|
||||
Node *clause, List *or_selectivities);
|
||||
extern Cost compute_clause_selec(Query *root, Node *clause);
|
||||
|
||||
#endif /* COST_H */
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: restrictinfo.h,v 1.5 1999/07/15 15:21:23 momjian Exp $
|
||||
* $Id: restrictinfo.h,v 1.6 1999/07/24 23:21:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
#include "nodes/relation.h"
|
||||
|
||||
extern bool valid_or_clause(RestrictInfo *restrictinfo);
|
||||
extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
|
||||
extern List *get_actual_clauses(List *restrictinfo_list);
|
||||
extern void get_relattvals(List *restrictinfo_list, List **attnos,
|
||||
List **values, List **flags);
|
||||
|
Loading…
x
Reference in New Issue
Block a user