diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 69 | ||||
-rw-r--r-- | src/backend/optimizer/path/clausesel.c | 123 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 14 | ||||
-rw-r--r-- | src/backend/optimizer/path/joinrels.c | 8 | ||||
-rw-r--r-- | src/backend/optimizer/path/orindxpath.c | 40 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 133 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 26 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 3 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 5 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 21 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 199 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 6 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 151 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 135 |
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; |