1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Modify optimizer data structures so that IndexOptInfo lists built for

create_index_paths are not immediately discarded, but are available for
subsequent planner work.  This allows avoiding redundant syscache lookups
in several places.  Change interface to operator selectivity estimation
procedures to allow faster and more flexible estimation.
Initdb forced due to change of pg_proc entries for selectivity functions!
This commit is contained in:
Tom Lane
2001-05-20 20:28:20 +00:00
parent 5d53389cfe
commit be03eb25f3
32 changed files with 1147 additions and 1218 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.73 2001/05/08 17:25:28 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.74 2001/05/20 20:28:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,8 +35,8 @@ static void set_base_rel_pathlists(Query *root);
static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte,
List *inheritlist);
Index rti, RangeTblEntry *rte,
List *inheritlist);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
@ -69,7 +69,7 @@ make_one_rel(Query *root)
rel = make_fromexpr_rel(root, root->jointree);
/*
* The result should join all the query's rels.
* The result should join all the query's base rels.
*/
Assert(length(rel->relids) == length(root->base_rel_list));
@ -190,10 +190,11 @@ set_base_rel_pathlists(Query *root)
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
else if ((inheritlist = expand_inherted_rtentry(root, rti)) != NIL)
else if ((inheritlist = expand_inherted_rtentry(root, rti, true))
!= NIL)
{
/* Relation is root of an inheritance tree, process specially */
set_inherited_rel_pathlist(root, rel, rte, inheritlist);
set_inherited_rel_pathlist(root, rel, rti, rte, inheritlist);
}
else
{
@ -210,8 +211,6 @@ set_base_rel_pathlists(Query *root)
static void
set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
{
List *indices = find_secondary_indexes(rte->relid);
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
@ -230,13 +229,9 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
create_tidscan_paths(root, rel);
/* Consider index paths for both simple and OR index clauses */
create_index_paths(root, rel, indices);
create_index_paths(root, rel);
/*
* 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_index_paths must be done before create_or_index_paths */
create_or_index_paths(root, rel, rel->baserestrictinfo);
/* Now find the cheapest of the paths for this rel */
@ -248,14 +243,26 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
* Build access paths for a inheritance tree rooted at rel
*
* inheritlist is a list of RT indexes of all tables in the inheritance tree,
* including the parent itself. Note we will not come here unless there's
* at least one child in addition to the parent.
* including a duplicate of the parent itself. Note we will not come here
* unless there's at least one child in addition to the parent.
*
* NOTE: the passed-in rel and RTE will henceforth represent the appended
* result of the whole inheritance tree. The members of inheritlist represent
* the individual tables --- in particular, the inheritlist member that is a
* duplicate of the parent RTE represents the parent table alone.
* We will generate plans to scan the individual tables that refer to
* the inheritlist RTEs, whereas Vars elsewhere in the plan tree that
* refer to the original RTE are taken to refer to the append output.
* In particular, this means we have separate RelOptInfos for the parent
* table and for the append output, which is a good thing because they're
* not the same size.
*/
static void
set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte,
List *inheritlist)
{
int parentRTindex = lfirsti(rel->relids);
int parentRTindex = rti;
Oid parentOID = rte->relid;
List *subpaths = NIL;
List *il;
@ -268,7 +275,15 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
/*
* Recompute size estimates for whole inheritance tree
* The executor will check the parent table's access permissions when it
* examines the parent's inheritlist entry. There's no need to check
* twice, so turn off access check bits in the original RTE.
*/
rte->checkForRead = false;
rte->checkForWrite = false;
/*
* Initialize to compute size estimates for whole inheritance tree
*/
rel->rows = 0;
rel->width = 0;
@ -289,21 +304,17 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
/*
* Make a RelOptInfo for the child so we can do planning. Do NOT
* attach the RelOptInfo to the query's base_rel_list, however.
*
* NOTE: when childRTindex == parentRTindex, we create a second
* RelOptInfo for the same relation. This RelOptInfo will
* represent the parent table alone, whereas the original
* RelOptInfo represents the union of the inheritance tree
* members.
* attach the RelOptInfo to the query's base_rel_list, however,
* since the child is not part of the main join tree. Instead,
* the child RelOptInfo is added to other_rel_list.
*/
childrel = make_base_rel(root, childRTindex);
childrel = build_other_rel(root, childRTindex);
/*
* Copy the parent's targetlist and restriction quals to the
* child, with attribute-number adjustment if needed. We don't
* child, with attribute-number adjustment as needed. We don't
* bother to copy the join quals, since we can't do any joining
* here.
* of the individual tables.
*/
childrel->targetlist = (List *)
adjust_inherited_attrs((Node *) rel->targetlist,

View File

@ -8,13 +8,15 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.43 2001/03/23 04:49:53 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.44 2001/05/20 20:28:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/plancat.h"
@ -24,6 +26,12 @@
#include "utils/lsyscache.h"
/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
#define MAKEBOOLCONST(val,isnull) \
((Node *) makeConst(BOOLOID, 1, (Datum) (val), \
(isnull), true, false, false))
/*
* Data structure for accumulating info about possible range-query
* clause pairs in clauselist_selectivity.
@ -39,7 +47,7 @@ typedef struct RangeQueryClause
} RangeQueryClause;
static void addRangeClause(RangeQueryClause **rqlist, Node *clause,
int flag, bool isLTsel, Selectivity s2);
bool varonleft, bool isLTsel, Selectivity s2);
static Selectivity clause_selectivity(Query *root,
Node *clause,
int varRelid);
@ -131,35 +139,24 @@ clauselist_selectivity(Query *root,
* match what clause_selectivity() would do in the cases it
* handles.
*/
if (varRelid != 0 || NumRelids(clause) == 1)
if (is_opclause(clause) &&
(varRelid != 0 || NumRelids(clause) == 1))
{
int relidx;
AttrNumber attno;
Datum constval;
int flag;
Expr *expr = (Expr *) clause;
get_relattval(clause, varRelid,
&relidx, &attno, &constval, &flag);
if (relidx != 0)
if (length(expr->args) == 2)
{
/* if get_relattval succeeded, it must be an opclause */
Var *other;
bool varonleft = true;
other = (flag & SEL_RIGHT) ? get_rightop((Expr *) clause) :
get_leftop((Expr *) clause);
if (is_pseudo_constant_clause((Node *) other))
if (is_pseudo_constant_clause(lsecond(expr->args)) ||
(varonleft = false,
is_pseudo_constant_clause(lfirst(expr->args))))
{
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
Oid opno = ((Oper *) expr->oper)->opno;
RegProcedure oprrest = get_oprrest(opno);
if (!oprrest)
s2 = (Selectivity) 0.5;
else
s2 = restriction_selectivity(oprrest, opno,
getrelid(relidx,
root->rtable),
attno,
constval, flag);
s2 = restriction_selectivity(root, opno,
expr->args, varRelid);
/*
* If we reach here, we have computed the same result
@ -171,10 +168,12 @@ clauselist_selectivity(Query *root,
switch (oprrest)
{
case F_SCALARLTSEL:
addRangeClause(&rqlist, clause, flag, true, s2);
addRangeClause(&rqlist, clause,
varonleft, true, s2);
break;
case F_SCALARGTSEL:
addRangeClause(&rqlist, clause, flag, false, s2);
addRangeClause(&rqlist, clause,
varonleft, false, s2);
break;
default:
/* Just merge the selectivity in generically */
@ -220,7 +219,7 @@ clauselist_selectivity(Query *root,
* No data available --- use a default estimate that
* is small, but not real small.
*/
s2 = 0.01;
s2 = 0.005;
}
else
{
@ -259,14 +258,13 @@ clauselist_selectivity(Query *root,
*/
static void
addRangeClause(RangeQueryClause **rqlist, Node *clause,
int flag, bool isLTsel, Selectivity s2)
bool varonleft, bool isLTsel, Selectivity s2)
{
RangeQueryClause *rqelem;
Node *var;
bool is_lobound;
/* get_relattval sets flag&SEL_RIGHT if the var is on the LEFT. */
if (flag & SEL_RIGHT)
if (varonleft)
{
var = (Node *) get_leftop((Expr *) clause);
is_lobound = !isLTsel; /* x < something is high bound */
@ -405,12 +403,12 @@ clause_selectivity(Query *root,
* is equivalent to the clause reln.attribute = 't', so we
* compute the selectivity as if that is what we have.
*/
s1 = restriction_selectivity(F_EQSEL,
s1 = restriction_selectivity(root,
BooleanEqualOperator,
rte->relid,
var->varattno,
BoolGetDatum(true),
SEL_CONSTANT | SEL_RIGHT);
makeList2(var,
MAKEBOOLCONST(true,
false)),
varRelid);
}
}
}
@ -486,57 +484,14 @@ clause_selectivity(Query *root,
if (is_join_clause)
{
/* Estimate selectivity for a join clause. */
RegProcedure oprjoin = get_oprjoin(opno);
/*
* if the oprjoin procedure is missing for whatever reason,
* use a selectivity of 0.5
*/
if (!oprjoin)
s1 = (Selectivity) 0.5;
else
{
int relid1,
relid2;
AttrNumber attno1,
attno2;
Oid reloid1,
reloid2;
get_rels_atts(clause, &relid1, &attno1, &relid2, &attno2);
reloid1 = relid1 ? getrelid(relid1, root->rtable) : InvalidOid;
reloid2 = relid2 ? getrelid(relid2, root->rtable) : InvalidOid;
s1 = join_selectivity(oprjoin, opno,
reloid1, attno1,
reloid2, attno2);
}
s1 = join_selectivity(root, opno,
((Expr *) clause)->args);
}
else
{
/* Estimate selectivity for a restriction clause. */
RegProcedure oprrest = get_oprrest(opno);
/*
* if the oprrest procedure is missing for whatever reason,
* use a selectivity of 0.5
*/
if (!oprrest)
s1 = (Selectivity) 0.5;
else
{
int relidx;
AttrNumber attno;
Datum constval;
int flag;
Oid reloid;
get_relattval(clause, varRelid,
&relidx, &attno, &constval, &flag);
reloid = relidx ? getrelid(relidx, root->rtable) : InvalidOid;
s1 = restriction_selectivity(oprrest, opno,
reloid, attno,
constval, flag);
}
s1 = restriction_selectivity(root, opno,
((Expr *) clause)->args, varRelid);
}
}
else if (is_funcclause(clause))
@ -555,7 +510,7 @@ clause_selectivity(Query *root,
/*
* Just for the moment! FIX ME! - vadim 02/04/98
*/
s1 = 1.0;
s1 = (Selectivity) 0.5;
}
else if (IsA(clause, RelabelType))
{

View File

@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.73 2001/05/09 23:13:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.74 2001/05/20 20:28:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -773,7 +773,7 @@ estimate_hash_bucketsize(Query *root, Var *var)
if (relid == InvalidOid)
return 0.1;
rel = get_base_rel(root, var->varno);
rel = find_base_rel(root, var->varno);
if (rel->tuples <= 0.0 || rel->rows <= 0.0)
return 0.1; /* ensure we can divide below */

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.104 2001/03/23 04:49:53 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.105 2001/05/20 20:28:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -127,18 +127,15 @@ static Const *string_to_const(const char *str, Oid datatype);
* consideration in nested-loop joins.
*
* 'rel' is the relation for which we want to generate index paths
* 'indices' is a list of available indexes for 'rel'
*/
void
create_index_paths(Query *root,
RelOptInfo *rel,
List *indices)
create_index_paths(Query *root, RelOptInfo *rel)
{
List *restrictinfo_list = rel->baserestrictinfo;
List *joininfo_list = rel->joininfo;
List *ilist;
foreach(ilist, indices)
foreach(ilist, rel->indexlist)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
List *restrictclauses;
@ -1435,10 +1432,10 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
/*
* Note that we are making a pathnode for a single-scan indexscan;
* therefore, both indexid and indexqual should be single-element
* therefore, both indexinfo and indexqual should be single-element
* lists.
*/
pathnode->indexid = makeListi1(index->indexoid);
pathnode->indexinfo = makeList1(index);
pathnode->indexqual = makeList1(indexquals);
/* We don't actually care what order the index scans in ... */
@ -2030,7 +2027,6 @@ find_operator(const char *opname, Oid datatype)
static Datum
string_to_datum(const char *str, Oid datatype)
{
/*
* We cheat a little by assuming that textin() will do for bpchar and
* varchar constants too...

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.52 2001/03/22 03:59:35 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.53 2001/05/20 20:28:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -332,7 +332,7 @@ make_rels_by_clauseless_joins(Query *root,
/*
* make_jointree_rel
* Find or build a RelOptInfojoin rel representing a specific
* Find or build a RelOptInfo join rel representing a specific
* jointree item. For JoinExprs, we only consider the construction
* path that corresponds exactly to what the user wrote.
*/
@ -343,7 +343,7 @@ make_jointree_rel(Query *root, Node *jtnode)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
return get_base_rel(root, varno);
return build_base_rel(root, varno);
}
else if (IsA(jtnode, FromExpr))
{
@ -402,7 +402,7 @@ make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
* Find or build the join RelOptInfo, and compute the restrictlist
* that goes with this particular joining.
*/
joinrel = get_join_rel(root, rel1, rel2, jointype, &restrictlist);
joinrel = build_join_rel(root, rel1, rel2, jointype, &restrictlist);
/*
* Consider paths using each rel as both outer and inner.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.42 2001/01/24 19:42:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.43 2001/05/20 20:28:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,8 +26,8 @@ static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
IndexPath *pathnode);
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
Expr *subclause, List *indices,
IndexOptInfo **retIndexInfo,
List **retIndexQual,
Oid *retIndexid,
Cost *retStartupCost,
Cost *retTotalCost);
@ -122,14 +122,14 @@ create_or_index_paths(Query *root,
* of an 'or' clause and the cost of scanning a relation using these
* indices. The cost is the sum of the individual index costs, since
* the executor will perform a scan for each subclause of the 'or'.
* Returns a list of IndexOptInfo nodes, one per scan.
*
* 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
* 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.
* This routine also creates the indexqual list 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 of quals, and OR across
* the toplevel list (note that the executor takes care not to return any
* single tuple more than once).
*
* 'rel' is the node of the relation on which the indexes are defined
* 'subclauses' are the subclauses of the 'or' clause
@ -138,9 +138,9 @@ create_or_index_paths(Query *root,
* 'pathnode' is the IndexPath node being built.
*
* Results are returned by setting these fields of the passed pathnode:
* 'indexinfo' gets a list of the index IndexOptInfo nodes, one per scan
* 'indexqual' gets the constructed indexquals for the path (a list
* of sublists of clauses, one sublist per scan of the base rel)
* 'indexid' gets a list of the index OIDs for each scan of the rel
* 'startup_cost' and 'total_cost' get the complete path costs.
*
* 'startup_cost' is the startup cost for the first index scan only;
@ -161,28 +161,28 @@ best_or_subclause_indices(Query *root,
{
List *slist;
pathnode->indexinfo = NIL;
pathnode->indexqual = NIL;
pathnode->indexid = NIL;
pathnode->path.startup_cost = 0;
pathnode->path.total_cost = 0;
foreach(slist, subclauses)
{
Expr *subclause = lfirst(slist);
IndexOptInfo *best_indexinfo;
List *best_indexqual;
Oid best_indexid;
Cost best_startup_cost;
Cost best_total_cost;
best_or_subclause_index(root, rel, subclause, lfirst(indices),
&best_indexqual, &best_indexid,
&best_indexinfo, &best_indexqual,
&best_startup_cost, &best_total_cost);
Assert(best_indexid != InvalidOid);
Assert(best_indexinfo != NULL);
pathnode->indexinfo = lappend(pathnode->indexinfo, best_indexinfo);
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;
@ -199,8 +199,8 @@ best_or_subclause_indices(Query *root,
* 'rel' is the node of the relation on which the index is defined
* 'subclause' is the OR subclause being considered
* 'indices' is a list of IndexOptInfo nodes that match the subclause
* '*retIndexInfo' gets the IndexOptInfo of the best index
* '*retIndexQual' gets a list of the indexqual conditions for the best index
* '*retIndexid' gets the OID of the best index
* '*retStartupCost' gets the startup cost of a scan with that index
* '*retTotalCost' gets the total cost of a scan with that index
*/
@ -209,8 +209,8 @@ best_or_subclause_index(Query *root,
RelOptInfo *rel,
Expr *subclause,
List *indices,
IndexOptInfo **retIndexInfo, /* return value */
List **retIndexQual, /* return value */
Oid *retIndexid, /* return value */
Cost *retStartupCost, /* return value */
Cost *retTotalCost) /* return value */
{
@ -218,8 +218,8 @@ best_or_subclause_index(Query *root,
List *ilist;
/* if we don't match anything, return zeros */
*retIndexInfo = NULL;
*retIndexQual = NIL;
*retIndexid = InvalidOid;
*retStartupCost = 0;
*retTotalCost = 0;
@ -238,8 +238,8 @@ best_or_subclause_index(Query *root,
if (first_time || subclause_path.total_cost < *retTotalCost)
{
*retIndexInfo = index;
*retIndexQual = indexqual;
*retIndexid = index->indexoid;
*retStartupCost = subclause_path.startup_cost;
*retTotalCost = subclause_path.total_cost;
first_time = false;

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.105 2001/05/07 00:43:20 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.106 2001/05/20 20:28:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,7 +18,6 @@
#include <sys/types.h>
#include "catalog/pg_index.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
@ -27,6 +26,7 @@
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_expr.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@ -56,11 +56,11 @@ static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
Form_pg_index index);
static List *fix_indxqual_sublist(List *indexqual, int baserelid,
IndexOptInfo *index);
static Node *fix_indxqual_operand(Node *node, int baserelid,
Form_pg_index index,
Oid *opclass);
IndexOptInfo *index,
Oid *opclass);
static List *switch_outer(List *clauses);
static void copy_path_costsize(Plan *dest, Path *src);
static void copy_plan_costsize(Plan *dest, Plan *src);
@ -365,7 +365,7 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
* The indexqual of the path contains a sublist of implicitly-ANDed qual
* conditions for each scan of the index(es); if there is more than one
* scan then the retrieved tuple sets are ORed together. The indexqual
* and indexid lists must have the same length, ie, the number of scans
* and indexinfo lists must have the same length, ie, the number of scans
* that will occur. Note it is possible for a qual condition sublist
* to be empty --- then no index restrictions will be applied during that
* scan.
@ -380,9 +380,10 @@ create_indexscan_plan(Query *root,
Index baserelid;
List *qpqual;
List *fixed_indxqual;
List *ixid;
List *indexids;
List *ixinfo;
IndexScan *scan_plan;
bool lossy = false;
bool lossy;
/* there should be exactly one base rel involved... */
Assert(length(best_path->path.parent->relids) == 1);
@ -390,25 +391,18 @@ create_indexscan_plan(Query *root,
baserelid = lfirsti(best_path->path.parent->relids);
/* check to see if any of the indices are lossy */
foreach(ixid, best_path->indexid)
/*
* Build list of index OIDs, and check to see if any of the indices
* are lossy.
*/
indexids = NIL;
lossy = false;
foreach(ixinfo, best_path->indexinfo)
{
HeapTuple indexTuple;
Form_pg_index index;
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
indexTuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(lfirsti(ixid)),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
index = (Form_pg_index) GETSTRUCT(indexTuple);
if (index->indislossy)
{
lossy = true;
ReleaseSysCache(indexTuple);
break;
}
ReleaseSysCache(indexTuple);
indexids = lappendi(indexids, index->indexoid);
lossy |= index->lossy;
}
/*
@ -471,7 +465,7 @@ create_indexscan_plan(Query *root,
scan_plan = make_indexscan(tlist,
qpqual,
baserelid,
best_path->indexid,
indexids,
fixed_indxqual,
indxqual,
best_path->indexscandir);
@ -895,45 +889,19 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path)
{
List *fixed_quals = NIL;
int baserelid = lfirsti(index_path->path.parent->relids);
List *indexids = index_path->indexid;
List *ixinfo = index_path->indexinfo;
List *i;
foreach(i, indexquals)
{
List *indexqual = lfirst(i);
Oid indexid = lfirsti(indexids);
HeapTuple indexTuple;
Oid relam;
Form_pg_index index;
/* Get the relam from the index's pg_class entry */
indexTuple = SearchSysCache(RELOID,
ObjectIdGetDatum(indexid),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "fix_indxqual_references: index %u not found in pg_class",
indexid);
relam = ((Form_pg_class) GETSTRUCT(indexTuple))->relam;
ReleaseSysCache(indexTuple);
/* Need the index's pg_index entry for other stuff */
indexTuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(indexid),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "fix_indxqual_references: index %u not found in pg_index",
indexid);
index = (Form_pg_index) GETSTRUCT(indexTuple);
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
fixed_quals = lappend(fixed_quals,
fix_indxqual_sublist(indexqual,
baserelid,
relam,
index));
ReleaseSysCache(indexTuple);
indexids = lnext(indexids);
ixinfo = lnext(ixinfo);
}
return fixed_quals;
}
@ -946,8 +914,7 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path)
* of the clause.) Also change the operator if necessary.
*/
static List *
fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
Form_pg_index index)
fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index)
{
List *fixed_qual = NIL;
List *i;
@ -955,26 +922,14 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
foreach(i, indexqual)
{
Expr *clause = (Expr *) lfirst(i);
int relid;
AttrNumber attno;
Datum constval;
int flag;
Expr *newclause;
List *leftvarnos;
Oid opclass,
newopno;
if (!is_opclause((Node *) clause) ||
length(clause->args) != 2)
if (!is_opclause((Node *) clause) || length(clause->args) != 2)
elog(ERROR, "fix_indxqual_sublist: indexqual clause is not binary opclause");
/*
* Which side is the indexkey on?
*
* get_relattval sets flag&SEL_RIGHT if the indexkey is on the LEFT.
*/
get_relattval((Node *) clause, baserelid,
&relid, &attno, &constval, &flag);
/*
* Make a copy that will become the fixed clause.
*
@ -984,9 +939,15 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
*/
newclause = (Expr *) copyObject((Node *) clause);
/* If the indexkey is on the right, commute the clause. */
if ((flag & SEL_RIGHT) == 0)
/*
* Check to see if the indexkey is on the right; if so, commute
* the clause. The indexkey should be the side that refers to
* (only) the base relation.
*/
leftvarnos = pull_varnos((Node *) lfirst(newclause->args));
if (length(leftvarnos) != 1 || lfirsti(leftvarnos) != baserelid)
CommuteClause(newclause);
freeList(leftvarnos);
/*
* Now, determine which index attribute this is, change the
@ -1002,7 +963,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
* is merely binary-compatible with the index. This shouldn't
* fail, since indxpath.c found it before...
*/
newopno = indexable_operator(newclause, opclass, relam, true);
newopno = indexable_operator(newclause, opclass, index->relam, true);
if (newopno == InvalidOid)
elog(ERROR, "fix_indxqual_sublist: failed to find substitute op");
((Oper *) newclause->oper)->opno = newopno;
@ -1013,7 +974,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
}
static Node *
fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index,
Oid *opclass)
{
@ -1033,27 +994,29 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
if (IsA(node, Var))
{
/* If it's a var, find which index key position it occupies */
Assert(index->indproc == InvalidOid);
if (((Var *) node)->varno == baserelid)
{
int varatt = ((Var *) node)->varattno;
int pos;
for (pos = 0; pos < INDEX_MAX_KEYS; pos++)
for (pos = 0; pos < index->nkeys; pos++)
{
if (index->indkey[pos] == varatt)
if (index->indexkeys[pos] == varatt)
{
Node *newnode = copyObject(node);
((Var *) newnode)->varattno = pos + 1;
/* return the correct opclass, too */
*opclass = index->indclass[pos];
*opclass = index->classlist[pos];
return newnode;
}
}
}
/*
* Oops, this Var isn't the indexkey!
* Oops, this Var isn't an indexkey!
*/
elog(ERROR, "fix_indxqual_operand: var is not index attribute");
}
@ -1063,11 +1026,11 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
* Since we currently only support single-column functional indexes,
* the returned varattno must be 1.
*/
Assert(index->indproc != InvalidOid);
Assert(is_funcclause(node)); /* not a very thorough check, but easy */
Assert(is_funcclause(node));/* not a very thorough check, but easy */
/* indclass[0] is the only class of a functional index */
*opclass = index->indclass[0];
/* classlist[0] is the only class of a functional index */
*opclass = index->classlist[0];
return (Node *) makeVar(baserelid, 1, exprType(node), -1, 0);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.61 2001/05/14 20:25:00 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.62 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -73,8 +73,8 @@ build_base_rel_tlists(Query *root, List *tlist)
/*
* add_vars_to_targetlist
* For each variable appearing in the list, add it to the relation's
* targetlist if not already present. Rel nodes will also be created
* if not already present.
* targetlist if not already present. Corresponding base rel nodes
* will be created if not already present.
*/
static void
add_vars_to_targetlist(Query *root, List *vars)
@ -84,7 +84,7 @@ add_vars_to_targetlist(Query *root, List *vars)
foreach(temp, vars)
{
Var *var = (Var *) lfirst(temp);
RelOptInfo *rel = get_base_rel(root, var->varno);
RelOptInfo *rel = build_base_rel(root, var->varno);
add_var_to_tlist(rel, var);
}
@ -120,8 +120,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
/* This call to get_base_rel does the primary work... */
RelOptInfo *rel = get_base_rel(root, varno);
/* This call to build_base_rel does the primary work... */
RelOptInfo *rel = build_base_rel(root, varno);
result = makeList1(rel);
}
@ -299,7 +299,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
foreach(relid, rels)
{
int relno = lfirsti(relid);
RelOptInfo *rel = get_base_rel(root, relno);
RelOptInfo *rel = build_base_rel(root, relno);
/*
* Since we do this bottom-up, any outer-rels previously marked
@ -422,7 +422,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
can_be_equijoin = true;
foreach(relid, relids)
{
RelOptInfo *rel = get_base_rel(root, lfirsti(relid));
RelOptInfo *rel = build_base_rel(root, lfirsti(relid));
if (rel->outerjoinset &&
!is_subseti(rel->outerjoinset, relids))
@ -454,12 +454,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
if (length(relids) == 1)
{
/*
* There is only one relation participating in 'clause', so
* 'clause' is a restriction clause for that relation.
*/
RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
rel->baserestrictinfo = lappend(rel->baserestrictinfo,
restrictinfo);
@ -564,7 +563,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
* Find or make the joininfo node for this combination of rels,
* and add the restrictinfo node to it.
*/
joininfo = find_joininfo_node(get_base_rel(root, cur_relid),
joininfo = find_joininfo_node(build_base_rel(root, cur_relid),
unjoined_relids);
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
restrictinfo);
@ -609,8 +608,11 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
* If both vars belong to same rel, we need to look at that rel's
* baserestrictinfo list. If different rels, each will have a
* joininfo node for the other, and we can scan either list.
*
* All baserel entries should already exist at this point, so use
* find_base_rel not build_base_rel.
*/
rel1 = get_base_rel(root, irel1);
rel1 = find_base_rel(root, irel1);
if (irel1 == irel2)
restrictlist = rel1->baserestrictinfo;
else

View File

@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.64 2001/03/22 03:59:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.65 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -194,6 +194,7 @@ subplanner(Query *root,
* construction.
*/
root->base_rel_list = NIL;
root->other_rel_list = NIL;
root->join_rel_list = NIL;
root->equi_key_list = NIL;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.106 2001/05/07 00:43:21 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.107 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -206,7 +206,8 @@ subquery_planner(Query *parse, double tuple_fraction)
* grouping_planner.
*/
if (parse->resultRelation &&
(lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
(lst = expand_inherted_rtentry(parse, parse->resultRelation, false))
!= NIL)
plan = inheritance_planner(parse, lst);
else
plan = grouping_planner(parse, tuple_fraction);

View File

@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.63 2001/05/07 00:43:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.64 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -515,6 +515,11 @@ find_all_inheritors(Oid parentrel)
* whole inheritance set (parent and children).
* If not, return NIL.
*
* When dup_parent is false, the initially given RT index is part of the
* returned list (if any). When dup_parent is true, the given RT index
* is *not* in the returned list; a duplicate RTE will be made for the
* parent table.
*
* A childless table is never considered to be an inheritance set; therefore
* the result will never be a one-element list. It'll be either empty
* or have two or more elements.
@ -525,7 +530,7 @@ find_all_inheritors(Oid parentrel)
* for the case of an inherited UPDATE/DELETE target relation.
*/
List *
expand_inherted_rtentry(Query *parse, Index rti)
expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
{
RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
Oid parentOID = rte->relid;
@ -544,7 +549,6 @@ expand_inherted_rtentry(Query *parse, Index rti)
return NIL;
/* Scan for all members of inheritance set */
inhOIDs = find_all_inheritors(parentOID);
/*
* Check that there's at least one descendant, else treat as no-child
* case. This could happen despite above has_subclass() check, if
@ -553,15 +557,19 @@ expand_inherted_rtentry(Query *parse, Index rti)
if (lnext(inhOIDs) == NIL)
return NIL;
/* OK, it's an inheritance set; expand it */
inhRTIs = makeListi1(rti);
if (dup_parent)
inhRTIs = NIL;
else
inhRTIs = makeListi1(rti); /* include original RTE in result */
foreach(l, inhOIDs)
{
Oid childOID = (Oid) lfirsti(l);
RangeTblEntry *childrte;
Index childRTindex;
/* parent will be in the list too, so ignore it */
if (childOID == parentOID)
/* parent will be in the list too; skip it if not dup requested */
if (childOID == parentOID && !dup_parent)
continue;
/*
@ -578,6 +586,7 @@ expand_inherted_rtentry(Query *parse, Index rti)
inhRTIs = lappendi(inhRTIs, childRTindex);
}
return inhRTIs;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.84 2001/03/27 17:12:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.85 2001/05/20 20:28:19 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -46,7 +46,6 @@ static bool pull_subplans_walker(Node *node, List **listptr);
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
Query *context);
static bool contain_noncachable_functions_walker(Node *node, void *context);
static int is_single_func(Node *node);
static Node *eval_const_expressions_mutator(Node *node, void *context);
static Expr *simplify_op_or_func(Expr *expr, List *args);
@ -797,202 +796,6 @@ NumRelids(Node *clause)
return result;
}
/*
* get_relattval
* Extract information from a restriction or join clause for
* selectivity estimation. The inputs are an expression
* and a relation number (which can be 0 if we don't care which
* relation is used; that'd normally be the case for restriction
* clauses, where the caller already knows that only one relation
* is referenced in the clause). The routine checks that the
* expression is of the form (var op something) or (something op var)
* where the var is an attribute of the specified relation, or
* a function of a var of the specified relation. If so, it
* returns the following info:
* the found relation number (same as targetrelid unless that is 0)
* the found var number (or InvalidAttrNumber if a function)
* if the "something" is a constant, the value of the constant
* flags indicating whether a constant was found, and on which side.
* Default values are returned if the expression is too complicated,
* specifically 0 for the relid and attno, 0 for the constant value.
*
* Note that negative attno values are *not* invalid, but represent
* system attributes such as OID. It's sufficient to check for relid=0
* to determine whether the routine succeeded.
*/
void
get_relattval(Node *clause,
int targetrelid,
int *relid,
AttrNumber *attno,
Datum *constval,
int *flag)
{
Var *left,
*right,
*other;
int funcvarno;
/* Careful; the passed clause might not be a binary operator at all */
if (!is_opclause(clause))
goto default_results;
left = get_leftop((Expr *) clause);
right = get_rightop((Expr *) clause);
if (!right)
goto default_results;
/* Ignore any binary-compatible relabeling */
if (IsA(left, RelabelType))
left = (Var *) ((RelabelType *) left)->arg;
if (IsA(right, RelabelType))
right = (Var *) ((RelabelType *) right)->arg;
/* First look for the var or func */
if (IsA(left, Var) &&
(targetrelid == 0 || targetrelid == left->varno))
{
*relid = left->varno;
*attno = left->varattno;
*flag = SEL_RIGHT;
}
else if (IsA(right, Var) &&
(targetrelid == 0 || targetrelid == right->varno))
{
*relid = right->varno;
*attno = right->varattno;
*flag = 0;
}
else if ((funcvarno = is_single_func((Node *) left)) != 0 &&
(targetrelid == 0 || targetrelid == funcvarno))
{
*relid = funcvarno;
*attno = InvalidAttrNumber;
*flag = SEL_RIGHT;
}
else if ((funcvarno = is_single_func((Node *) right)) != 0 &&
(targetrelid == 0 || targetrelid == funcvarno))
{
*relid = funcvarno;
*attno = InvalidAttrNumber;
*flag = 0;
}
else
{
/* Duh, it's too complicated for me... */
default_results:
*relid = 0;
*attno = 0;
*constval = 0;
*flag = 0;
return;
}
/* OK, we identified the var or func; now look at the other side */
other = (*flag == 0) ? left : right;
if (IsA(other, Const) &&
!((Const *) other)->constisnull)
{
*constval = ((Const *) other)->constvalue;
*flag |= SEL_CONSTANT;
}
else
*constval = 0;
}
/*
* is_single_func
* If the given expression is a function of a single relation,
* return the relation number; else return 0
*/
static int
is_single_func(Node *node)
{
if (is_funcclause(node))
{
List *varnos = pull_varnos(node);
if (length(varnos) == 1)
{
int funcvarno = lfirsti(varnos);
freeList(varnos);
return funcvarno;
}
freeList(varnos);
}
return 0;
}
/*
* get_rels_atts
*
* Returns the info
* ( relid1 attno1 relid2 attno2 )
* for a joinclause.
*
* If the clause is not of the form (var op var) or if any of the vars
* refer to nested attributes, then zeroes are returned.
*/
void
get_rels_atts(Node *clause,
int *relid1,
AttrNumber *attno1,
int *relid2,
AttrNumber *attno2)
{
/* set default values */
*relid1 = 0;
*attno1 = 0;
*relid2 = 0;
*attno2 = 0;
if (is_opclause(clause))
{
Var *left = get_leftop((Expr *) clause);
Var *right = get_rightop((Expr *) clause);
if (left && right)
{
int funcvarno;
/* Ignore any binary-compatible relabeling */
if (IsA(left, RelabelType))
left = (Var *) ((RelabelType *) left)->arg;
if (IsA(right, RelabelType))
right = (Var *) ((RelabelType *) right)->arg;
if (IsA(left, Var))
{
*relid1 = left->varno;
*attno1 = left->varattno;
}
else if ((funcvarno = is_single_func((Node *) left)) != 0)
{
*relid1 = funcvarno;
*attno1 = InvalidAttrNumber;
}
if (IsA(right, Var))
{
*relid2 = right->varno;
*attno2 = right->varattno;
}
else if ((funcvarno = is_single_func((Node *) right)) != 0)
{
*relid2 = funcvarno;
*attno2 = InvalidAttrNumber;
}
}
}
}
/*--------------------
* CommuteClause: commute a binary operator clause
*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.72 2001/05/07 00:43:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.73 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -346,9 +346,9 @@ create_index_path(Query *root,
/*
* We are making a pathnode for a single-scan indexscan; therefore,
* both indexid and indexqual should be single-element lists.
* both indexinfo and indexqual should be single-element lists.
*/
pathnode->indexid = makeListi1(index->indexoid);
pathnode->indexinfo = makeList1(index);
pathnode->indexqual = makeList1(indexquals);
pathnode->indexscandir = indexscandir;

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.65 2001/05/07 00:43:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.66 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,9 +23,12 @@
#include "catalog/pg_amop.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_index.h"
#include "optimizer/clauses.h"
#include "optimizer/plancat.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
#include "catalog/catalog.h"
@ -33,7 +36,7 @@
/*
* relation_info -
* get_relation_info -
* Retrieves catalog information for a given relation.
* Given the Oid of the relation, return the following info:
* whether the relation has secondary indices
@ -41,8 +44,8 @@
* number of tuples
*/
void
relation_info(Oid relationObjectId,
bool *hasindex, long *pages, double *tuples)
get_relation_info(Oid relationObjectId,
bool *hasindex, long *pages, double *tuples)
{
HeapTuple relationTuple;
Form_pg_class relation;
@ -51,16 +54,19 @@ relation_info(Oid relationObjectId,
ObjectIdGetDatum(relationObjectId),
0, 0, 0);
if (!HeapTupleIsValid(relationTuple))
elog(ERROR, "relation_info: Relation %u not found",
elog(ERROR, "get_relation_info: Relation %u not found",
relationObjectId);
relation = (Form_pg_class) GETSTRUCT(relationTuple);
if (IsIgnoringSystemIndexes() && IsSystemRelationName(NameStr(relation->relname)))
if (IsIgnoringSystemIndexes() &&
IsSystemRelationName(NameStr(relation->relname)))
*hasindex = false;
else
*hasindex = (relation->relhasindex) ? true : false;
*hasindex = relation->relhasindex;
*pages = relation->relpages;
*tuples = relation->reltuples;
ReleaseSysCache(relationTuple);
}
@ -110,8 +116,8 @@ find_secondary_indexes(Oid relationObjectId)
info = makeNode(IndexOptInfo);
/*
* Need to make these arrays large enough to be sure there is a
* terminating 0 at the end of each one.
* Need to make these arrays large enough to be sure there is
* room for a terminating 0 at the end of each one.
*/
info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1));
@ -131,14 +137,26 @@ find_secondary_indexes(Oid relationObjectId)
}
else
info->indpred = NIL;
info->unique = index->indisunique;
info->lossy = index->indislossy;
for (i = 0; i < INDEX_MAX_KEYS; i++)
info->indexkeys[i] = index->indkey[i];
info->indexkeys[INDEX_MAX_KEYS] = 0;
for (i = 0; i < INDEX_MAX_KEYS; i++)
{
if (index->indclass[i] == (Oid) 0)
break;
info->classlist[i] = index->indclass[i];
info->classlist[INDEX_MAX_KEYS] = (Oid) 0;
}
info->classlist[i] = (Oid) 0;
info->ncolumns = i;
for (i = 0; i < INDEX_MAX_KEYS; i++)
{
if (index->indkey[i] == 0)
break;
info->indexkeys[i] = index->indkey[i];
}
info->indexkeys[i] = 0;
info->nkeys = i;
/* Extract info from the relation descriptor for the index */
indexRelation = index_open(index->indexrelid);
@ -156,7 +174,7 @@ find_secondary_indexes(Oid relationObjectId)
MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1));
if (amorderstrategy != 0)
{
for (i = 0; i < INDEX_MAX_KEYS && index->indclass[i]; i++)
for (i = 0; i < info->ncolumns; i++)
{
HeapTuple amopTuple;
Form_pg_amop amop;
@ -193,30 +211,34 @@ find_secondary_indexes(Oid relationObjectId)
/*
* restriction_selectivity
*
* Returns the selectivity of a specified operator.
* Returns the selectivity of a specified restriction operator clause.
* This code executes registered procedures stored in the
* operator relation, by calling the function manager.
*
* XXX The assumption in the selectivity procedures is that if the
* relation OIDs or attribute numbers are 0, then the clause
* isn't of the form (op var const).
* varRelid is either 0 or a rangetable index. See clause_selectivity()
* for details about its meaning.
*/
Selectivity
restriction_selectivity(Oid functionObjectId,
Oid operatorObjectId,
Oid relationObjectId,
AttrNumber attributeNumber,
Datum constValue,
int constFlag)
restriction_selectivity(Query *root,
Oid operator,
List *args,
int varRelid)
{
RegProcedure oprrest = get_oprrest(operator);
float8 result;
result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
ObjectIdGetDatum(operatorObjectId),
ObjectIdGetDatum(relationObjectId),
Int16GetDatum(attributeNumber),
constValue,
Int32GetDatum(constFlag)));
/*
* if the oprrest procedure is missing for whatever reason,
* use a selectivity of 0.5
*/
if (!oprrest)
return (Selectivity) 0.5;
result = DatumGetFloat8(OidFunctionCall4(oprrest,
PointerGetDatum(root),
ObjectIdGetDatum(operator),
PointerGetDatum(args),
Int32GetDatum(varRelid)));
if (result < 0.0 || result > 1.0)
elog(ERROR, "restriction_selectivity: bad value %f", result);
@ -227,29 +249,29 @@ restriction_selectivity(Oid functionObjectId,
/*
* join_selectivity
*
* Returns the selectivity of an operator, given the join clause
* information.
*
* XXX The assumption in the selectivity procedures is that if the
* relation OIDs or attribute numbers are 0, then the clause
* isn't of the form (op var var).
* Returns the selectivity of a specified join operator clause.
* This code executes registered procedures stored in the
* operator relation, by calling the function manager.
*/
Selectivity
join_selectivity(Oid functionObjectId,
Oid operatorObjectId,
Oid relationObjectId1,
AttrNumber attributeNumber1,
Oid relationObjectId2,
AttrNumber attributeNumber2)
join_selectivity(Query *root,
Oid operator,
List *args)
{
RegProcedure oprjoin = get_oprjoin(operator);
float8 result;
result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
ObjectIdGetDatum(operatorObjectId),
ObjectIdGetDatum(relationObjectId1),
Int16GetDatum(attributeNumber1),
ObjectIdGetDatum(relationObjectId2),
Int16GetDatum(attributeNumber2)));
/*
* if the oprjoin procedure is missing for whatever reason,
* use a selectivity of 0.5
*/
if (!oprjoin)
return (Selectivity) 0.5;
result = DatumGetFloat8(OidFunctionCall3(oprjoin,
PointerGetDatum(root),
ObjectIdGetDatum(operator),
PointerGetDatum(args)));
if (result < 0.0 || result > 1.0)
elog(ERROR, "join_selectivity: bad value %f", result);
@ -330,3 +352,36 @@ has_subclass(Oid relationId)
ReleaseSysCache(tuple);
return result;
}
/*
* has_unique_index
*
* Detect whether there is a unique index on the specified attribute
* of the specified relation, thus allowing us to conclude that all
* the (non-null) values of the attribute are distinct.
*/
bool
has_unique_index(RelOptInfo *rel, AttrNumber attno)
{
List *ilist;
foreach(ilist, rel->indexlist)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
/*
* Note: ignore functional, partial, or lossy indexes, since they
* don't allow us to conclude that all attr values are distinct.
* Also, a multicolumn unique index doesn't allow us to conclude
* that just the specified attr is unique.
*/
if (index->unique &&
index->nkeys == 1 &&
index->indexkeys[0] == attno &&
index->indproc == InvalidOid &&
index->indpred == NIL &&
!index->lossy)
return true;
}
return false;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.32 2001/02/16 00:03:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.33 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -22,6 +22,7 @@
#include "parser/parsetree.h"
static RelOptInfo *make_base_rel(Query *root, int relid);
static List *new_join_tlist(List *tlist, int first_resdomno);
static List *build_joinrel_restrictlist(RelOptInfo *joinrel,
RelOptInfo *outer_rel,
@ -36,28 +37,35 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
/*
* get_base_rel
* build_base_rel
* Returns relation entry corresponding to 'relid', creating a new one
* if necessary. This is for base relations.
*/
RelOptInfo *
get_base_rel(Query *root, int relid)
build_base_rel(Query *root, int relid)
{
List *baserels;
List *rels;
RelOptInfo *rel;
foreach(baserels, root->base_rel_list)
/* Already made? */
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(baserels);
rel = (RelOptInfo *) lfirst(rels);
/*
* We know length(rel->relids) == 1 for all members of
* base_rel_list
*/
/* length(rel->relids) == 1 for all members of base_rel_list */
if (lfirsti(rel->relids) == relid)
return rel;
}
/* It should not exist as an "other" rel */
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
if (lfirsti(rel->relids) == relid)
elog(ERROR, "build_base_rel: rel already exists as 'other' rel");
}
/* No existing RelOptInfo for this base rel, so make a new one */
rel = make_base_rel(root, relid);
@ -67,17 +75,53 @@ get_base_rel(Query *root, int relid)
return rel;
}
/*
* build_other_rel
* Returns relation entry corresponding to 'relid', creating a new one
* if necessary. This is for 'other' relations, which are just like
* base relations except that they live in a different list.
*/
RelOptInfo *
build_other_rel(Query *root, int relid)
{
List *rels;
RelOptInfo *rel;
/* Already made? */
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
/* length(rel->relids) == 1 for all members of other_rel_list */
if (lfirsti(rel->relids) == relid)
return rel;
}
/* It should not exist as a base rel */
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
if (lfirsti(rel->relids) == relid)
elog(ERROR, "build_other_rel: rel already exists as base rel");
}
/* No existing RelOptInfo for this other rel, so make a new one */
rel = make_base_rel(root, relid);
/* and add it to the list */
root->other_rel_list = lcons(rel, root->other_rel_list);
return rel;
}
/*
* make_base_rel
* Construct a base-relation RelOptInfo for the specified rangetable index.
*
* This is split out of get_base_rel so that inheritance-tree processing can
* construct baserel nodes for child tables. We need a RelOptInfo so we can
* plan a suitable access path for each child table, but we do NOT want to
* enter the child nodes into base_rel_list. In most contexts, get_base_rel
* should be called instead.
* Common code for build_base_rel and build_other_rel.
*/
RelOptInfo *
static RelOptInfo *
make_base_rel(Query *root, int relid)
{
RelOptInfo *rel = makeNode(RelOptInfo);
@ -92,7 +136,7 @@ make_base_rel(Query *root, int relid)
rel->cheapest_total_path = NULL;
rel->pruneable = true;
rel->issubquery = false;
rel->indexed = false;
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
rel->subplan = NULL;
@ -108,8 +152,12 @@ make_base_rel(Query *root, int relid)
if (relationObjectId != InvalidOid)
{
/* Plain relation --- retrieve statistics from the system catalogs */
relation_info(relationObjectId,
&rel->indexed, &rel->pages, &rel->tuples);
bool indexed;
get_relation_info(relationObjectId,
&indexed, &rel->pages, &rel->tuples);
if (indexed)
rel->indexlist = find_secondary_indexes(relationObjectId);
}
else
{
@ -120,13 +168,46 @@ make_base_rel(Query *root, int relid)
return rel;
}
/*
* find_base_rel
* Find a base or other relation entry, which must already exist
* (since we'd have no idea which list to add it to).
*/
RelOptInfo *
find_base_rel(Query *root, int relid)
{
List *rels;
RelOptInfo *rel;
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
/* length(rel->relids) == 1 for all members of base_rel_list */
if (lfirsti(rel->relids) == relid)
return rel;
}
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
if (lfirsti(rel->relids) == relid)
return rel;
}
elog(ERROR, "find_base_rel: no relation entry for relid %d", relid);
return NULL; /* keep compiler quiet */
}
/*
* find_join_rel
* Returns relation entry corresponding to 'relids' (a list of RT indexes),
* or NULL if none exists. This is for join relations.
*
* Note: there is probably no good reason for this to be called from
* anywhere except get_join_rel, but keep it as a separate routine
* anywhere except build_join_rel, but keep it as a separate routine
* just in case.
*/
static RelOptInfo *
@ -146,7 +227,7 @@ find_join_rel(Query *root, Relids relids)
}
/*
* get_join_rel
* build_join_rel
* Returns relation entry corresponding to the union of two given rels,
* creating a new relation entry if none already exists.
*
@ -161,11 +242,11 @@ find_join_rel(Query *root, Relids relids)
* duplicated calculation of the restrictlist...
*/
RelOptInfo *
get_join_rel(Query *root,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
List **restrictlist_ptr)
build_join_rel(Query *root,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
List **restrictlist_ptr)
{
List *joinrelids;
RelOptInfo *joinrel;
@ -212,7 +293,7 @@ get_join_rel(Query *root,
joinrel->cheapest_total_path = NULL;
joinrel->pruneable = true;
joinrel->issubquery = false;
joinrel->indexed = false;
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->subplan = NULL;