summaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/allpaths.c69
-rw-r--r--src/backend/optimizer/path/clausesel.c123
-rw-r--r--src/backend/optimizer/path/costsize.c4
-rw-r--r--src/backend/optimizer/path/indxpath.c14
-rw-r--r--src/backend/optimizer/path/joinrels.c8
-rw-r--r--src/backend/optimizer/path/orindxpath.c40
-rw-r--r--src/backend/optimizer/plan/createplan.c133
-rw-r--r--src/backend/optimizer/plan/initsplan.c26
-rw-r--r--src/backend/optimizer/plan/planmain.c3
-rw-r--r--src/backend/optimizer/plan/planner.c5
-rw-r--r--src/backend/optimizer/prep/prepunion.c21
-rw-r--r--src/backend/optimizer/util/clauses.c199
-rw-r--r--src/backend/optimizer/util/pathnode.c6
-rw-r--r--src/backend/optimizer/util/plancat.c151
-rw-r--r--src/backend/optimizer/util/relnode.c135
15 files changed, 407 insertions, 530 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 243ff7c5a72..afb3259e736 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -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,
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 2699b56cb37..78407fb833a 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -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;
-
- get_relattval(clause, varRelid,
- &relidx, &attno, &constval, &flag);
- if (relidx != 0)
+ Expr *expr = (Expr *) clause;
+
+ 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))
{
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index dddca240e95..b4379e4b39b 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -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 */
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index ca19465c897..a5f5bb151da 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -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...
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 929a977112d..3bde257a37e 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -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.
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
index d4e467c3e04..25cbc3e4fa2 100644
--- a/src/backend/optimizer/path/orindxpath.c
+++ b/src/backend/optimizer/path/orindxpath.c
@@ -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;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 2d264c46881..81e7fec0427 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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;
-
- 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);
+ IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
+
+ 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,27 +922,15 @@ 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.
*
* We used to try to do a shallow copy here, but that fails if there
@@ -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);
}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index c62fd5ecd7d..3b3c761bca6 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -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
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index b2b362e84a5..2f52e694d13 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -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;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0aba4808c16..fbed3d6d092 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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);
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ede4159d970..42cc47fa4ac 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -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;
}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8bd6ef6f68b..e0cc97e3a1d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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
*
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 407c132b4f7..801b328d817 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -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;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index ee3523553e8..749390a4d2d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -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;
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index b4764ab6f8c..86d923116a4 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -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);
@@ -68,16 +76,52 @@ get_base_rel(Query *root, int relid)
}
/*
+ * 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
{
@@ -121,12 +169,45 @@ make_base_rel(Query *root, int relid)
}
/*
+ * 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;