diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 11494b0172a..db54ceede2f 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.57 2001/03/22 06:16:10 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.58 2001/05/20 20:28:17 tgl Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -69,7 +69,7 @@ static void OperatorUpd(Oid baseId, Oid commId, Oid negId); /* ---------------------------------------------------------------- * OperatorGetWithOpenRelation * - * preforms a scan on pg_operator for an operator tuple + * performs a scan on pg_operator for an operator tuple * with given name and left/right type oids. * ---------------------------------------------------------------- * pg_operator_desc -- reldesc for pg_operator @@ -570,26 +570,25 @@ OperatorDef(char *operatorName, ReleaseSysCache(tup); /* - * find restriction + * find restriction estimator */ if (restrictionName) { /* optional */ Oid restOid; MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid)); - typeId[0] = OIDOID; /* operator OID */ - typeId[1] = OIDOID; /* relation OID */ - typeId[2] = INT2OID; /* attribute number */ - typeId[3] = 0; /* value - can be any type */ - typeId[4] = INT4OID; /* flags - left or right selectivity */ + typeId[0] = 0; /* Query (opaque type) */ + typeId[1] = OIDOID; /* operator OID */ + typeId[2] = 0; /* args list (opaque type) */ + typeId[3] = INT4OID; /* varRelid */ restOid = GetSysCacheOid(PROCNAME, PointerGetDatum(restrictionName), - Int32GetDatum(5), + Int32GetDatum(4), PointerGetDatum(typeId), 0); if (!OidIsValid(restOid)) - func_error("OperatorDef", restrictionName, 5, typeId, NULL); + func_error("OperatorDef", restrictionName, 4, typeId, NULL); values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restOid); } @@ -597,26 +596,24 @@ OperatorDef(char *operatorName, values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid); /* - * find join - only valid for binary operators + * find join estimator */ if (joinName) { /* optional */ Oid joinOid; MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid)); - typeId[0] = OIDOID; /* operator OID */ - typeId[1] = OIDOID; /* relation OID 1 */ - typeId[2] = INT2OID; /* attribute number 1 */ - typeId[3] = OIDOID; /* relation OID 2 */ - typeId[4] = INT2OID; /* attribute number 2 */ + typeId[0] = 0; /* Query (opaque type) */ + typeId[1] = OIDOID; /* operator OID */ + typeId[2] = 0; /* args list (opaque type) */ joinOid = GetSysCacheOid(PROCNAME, PointerGetDatum(joinName), - Int32GetDatum(5), + Int32GetDatum(3), PointerGetDatum(typeId), 0); if (!OidIsValid(joinOid)) - func_error("OperatorDef", joinName, 5, typeId, NULL); + func_error("OperatorDef", joinName, 3, typeId, NULL); values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid); } @@ -1044,10 +1041,8 @@ OperatorCreate(char *operatorName, /* If it's not a binary op, these things mustn't be set: */ if (commutatorName) elog(ERROR, "OperatorCreate: only binary operators can have commutators"); - if (negatorName) - elog(ERROR, "OperatorCreate: only binary operators can have negators"); - if (restrictionName || joinName) - elog(ERROR, "OperatorCreate: only binary operators can have selectivity"); + if (joinName) + elog(ERROR, "OperatorCreate: only binary operators can have join selectivity"); if (canHash) elog(ERROR, "OperatorCreate: only binary operators can hash"); if (leftSortName || rightSortName) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index ee5a803b802..a5a968515e6 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.141 2001/05/07 00:43:18 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.142 2001/05/20 20:28:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1071,7 +1071,7 @@ _copyRelOptInfo(RelOptInfo *from) newnode->pruneable = from->pruneable; newnode->issubquery = from->issubquery; - newnode->indexed = from->indexed; + Node_Copy(from, newnode, indexlist); newnode->pages = from->pages; newnode->tuples = from->tuples; Node_Copy(from, newnode, subplan); @@ -1093,47 +1093,44 @@ static IndexOptInfo * _copyIndexOptInfo(IndexOptInfo *from) { IndexOptInfo *newnode = makeNode(IndexOptInfo); - int i, - len; + Size len; newnode->indexoid = from->indexoid; newnode->pages = from->pages; newnode->tuples = from->tuples; + newnode->ncolumns = from->ncolumns; + newnode->nkeys = from->nkeys; + if (from->classlist) { - for (len = 0; from->classlist[len] != 0; len++) - ; - newnode->classlist = (Oid *) palloc(sizeof(Oid) * (len + 1)); - for (i = 0; i < len; i++) - newnode->classlist[i] = from->classlist[i]; - newnode->classlist[len] = 0; + /* copy the trailing zero too */ + len = (from->ncolumns + 1) * sizeof(Oid); + newnode->classlist = (Oid *) palloc(len); + memcpy(newnode->classlist, from->classlist, len); } if (from->indexkeys) { - for (len = 0; from->indexkeys[len] != 0; len++) - ; - newnode->indexkeys = (int *) palloc(sizeof(int) * (len + 1)); - for (i = 0; i < len; i++) - newnode->indexkeys[i] = from->indexkeys[i]; - newnode->indexkeys[len] = 0; + /* copy the trailing zero too */ + len = (from->nkeys + 1) * sizeof(int); + newnode->indexkeys = (int *) palloc(len); + memcpy(newnode->indexkeys, from->indexkeys, len); } if (from->ordering) { - for (len = 0; from->ordering[len] != 0; len++) - ; - newnode->ordering = (Oid *) palloc(sizeof(Oid) * (len + 1)); - for (i = 0; i < len; i++) - newnode->ordering[i] = from->ordering[i]; - newnode->ordering[len] = 0; + /* copy the trailing zero too */ + len = (from->ncolumns + 1) * sizeof(Oid); + newnode->ordering = (Oid *) palloc(len); + memcpy(newnode->ordering, from->ordering, len); } newnode->relam = from->relam; newnode->amcostestimate = from->amcostestimate; newnode->indproc = from->indproc; Node_Copy(from, newnode, indpred); + newnode->unique = from->unique; newnode->lossy = from->lossy; return newnode; @@ -1196,7 +1193,7 @@ _copyIndexPath(IndexPath *from) /* * copy remainder of node */ - newnode->indexid = listCopy(from->indexid); + Node_Copy(from, newnode, indexinfo); Node_Copy(from, newnode, indexqual); newnode->indexscandir = from->indexscandir; newnode->joinrelids = listCopy(from->joinrelids); @@ -1749,8 +1746,8 @@ _copyQuery(Query *from) /* * We do not copy the planner internal fields: base_rel_list, - * join_rel_list, equi_key_list, query_pathkeys. Not entirely clear if - * this is right? + * other_rel_list, join_rel_list, equi_key_list, query_pathkeys. + * Not entirely clear if this is right? */ return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 284a534aa96..a89a8f7f335 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -20,7 +20,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.89 2001/05/07 00:43:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.90 2001/05/20 20:28:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -405,7 +405,7 @@ _equalIndexPath(IndexPath *a, IndexPath *b) { if (!_equalPath((Path *) a, (Path *) b)) return false; - if (!equali(a->indexid, b->indexid)) + if (!equal(a->indexinfo, b->indexinfo)) return false; if (!equal(a->indexqual, b->indexqual)) return false; @@ -623,9 +623,9 @@ _equalQuery(Query *a, Query *b) /* * We do not check the internal-to-the-planner fields: base_rel_list, - * join_rel_list, equi_key_list, query_pathkeys. They might not be set - * yet, and in any case they should be derivable from the other - * fields. + * other_rel_list, join_rel_list, equi_key_list, query_pathkeys. + * They might not be set yet, and in any case they should be derivable + * from the other fields. */ return true; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 2c0cfed7ee4..ebcacd49750 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.140 2001/03/22 03:59:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.141 2001/05/20 20:28:18 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -952,56 +952,6 @@ _outJoinExpr(StringInfo str, JoinExpr *node) _outNode(str, node->colvars); } -/* - * Stuff from relation.h - */ - -static void -_outRelOptInfo(StringInfo str, RelOptInfo *node) -{ - appendStringInfo(str, " RELOPTINFO :relids "); - _outIntList(str, node->relids); - - appendStringInfo(str, " :rows %.0f :width %d :targetlist ", - node->rows, - node->width); - _outNode(str, node->targetlist); - - appendStringInfo(str, " :pathlist "); - _outNode(str, node->pathlist); - appendStringInfo(str, " :cheapest_startup_path "); - _outNode(str, node->cheapest_startup_path); - appendStringInfo(str, " :cheapest_total_path "); - _outNode(str, node->cheapest_total_path); - - appendStringInfo(str, " :pruneable %s :issubquery %s :indexed %s :pages %ld :tuples %.0f :subplan ", - booltostr(node->pruneable), - booltostr(node->issubquery), - booltostr(node->indexed), - node->pages, - node->tuples); - _outNode(str, node->subplan); - - appendStringInfo(str, " :baserestrictinfo "); - _outNode(str, node->baserestrictinfo); - appendStringInfo(str, " :baserestrictcost %.2f :outerjoinset ", - node->baserestrictcost); - _outIntList(str, node->outerjoinset); - appendStringInfo(str, " :joininfo "); - _outNode(str, node->joininfo); - appendStringInfo(str, " :innerjoin "); - _outNode(str, node->innerjoin); -} - -static void -_outIndexOptInfo(StringInfo str, IndexOptInfo *node) -{ - appendStringInfo(str, " INDEXOPTINFO :indexoid %u :pages %ld :tuples %g ", - node->indexoid, - node->pages, - node->tuples); -} - /* * TargetEntry is a subclass of Node. */ @@ -1064,8 +1014,8 @@ _outIndexPath(StringInfo str, IndexPath *node) node->path.total_cost); _outNode(str, node->path.pathkeys); - appendStringInfo(str, " :indexid "); - _outOidList(str, node->indexid); + appendStringInfo(str, " :indexinfo "); + _outNode(str, node->indexinfo); appendStringInfo(str, " :indexqual "); _outNode(str, node->indexqual); @@ -1629,12 +1579,6 @@ _outNode(StringInfo str, void *obj) case T_JoinExpr: _outJoinExpr(str, obj); break; - case T_RelOptInfo: - _outRelOptInfo(str, obj); - break; - case T_IndexOptInfo: - _outIndexOptInfo(str, obj); - break; case T_TargetEntry: _outTargetEntry(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 4c0c1b03ef5..ad832d7ca9e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.108 2001/05/07 00:43:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.109 2001/05/20 20:28:18 tgl Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -1318,88 +1318,6 @@ _readJoinExpr(void) return local_node; } -/* - * Stuff from relation.h - */ - -/* ---------------- - * _readRelOptInfo - * ---------------- - */ -static RelOptInfo * -_readRelOptInfo(void) -{ - RelOptInfo *local_node; - char *token; - int length; - - local_node = makeNode(RelOptInfo); - - token = pg_strtok(&length); /* get :relids */ - local_node->relids = toIntList(nodeRead(true)); /* now read it */ - - token = pg_strtok(&length); /* get :rows */ - token = pg_strtok(&length); /* now read it */ - local_node->rows = atof(token); - - token = pg_strtok(&length); /* get :width */ - token = pg_strtok(&length); /* now read it */ - local_node->width = atoi(token); - - token = pg_strtok(&length); /* get :targetlist */ - local_node->targetlist = nodeRead(true); /* now read it */ - - token = pg_strtok(&length); /* get :pathlist */ - local_node->pathlist = nodeRead(true); /* now read it */ - - token = pg_strtok(&length); /* get :cheapest_startup_path */ - local_node->cheapest_startup_path = nodeRead(true); /* now read it */ - - token = pg_strtok(&length); /* get :cheapest_total_path */ - local_node->cheapest_total_path = nodeRead(true); /* now read it */ - - token = pg_strtok(&length); /* eat :pruneable */ - token = pg_strtok(&length); /* get :pruneable */ - local_node->pruneable = strtobool(token); - - token = pg_strtok(&length); /* get :issubquery */ - token = pg_strtok(&length); /* now read it */ - local_node->issubquery = strtobool(token); - - token = pg_strtok(&length); /* get :indexed */ - token = pg_strtok(&length); /* now read it */ - local_node->indexed = strtobool(token); - - token = pg_strtok(&length); /* get :pages */ - token = pg_strtok(&length); /* now read it */ - local_node->pages = atol(token); - - token = pg_strtok(&length); /* get :tuples */ - token = pg_strtok(&length); /* now read it */ - local_node->tuples = atof(token); - - token = pg_strtok(&length); /* get :subplan */ - local_node->subplan = nodeRead(true); /* now read it */ - - token = pg_strtok(&length); /* get :baserestrictinfo */ - local_node->baserestrictinfo = nodeRead(true); /* now read it */ - - token = pg_strtok(&length); /* get :baserestrictcost */ - token = pg_strtok(&length); /* now read it */ - local_node->baserestrictcost = (Cost) atof(token); - - token = pg_strtok(&length); /* get :outerjoinset */ - local_node->outerjoinset = toIntList(nodeRead(true)); /* now read it */ - - token = pg_strtok(&length); /* get :joininfo */ - local_node->joininfo = nodeRead(true); /* now read it */ - - token = pg_strtok(&length); /* get :innerjoin */ - local_node->innerjoin = nodeRead(true); /* now read it */ - - return local_node; -} - /* ---------------- * _readTargetEntry * ---------------- @@ -1557,8 +1475,8 @@ _readIndexPath(void) token = pg_strtok(&length); /* get :pathkeys */ local_node->path.pathkeys = nodeRead(true); /* now read it */ - token = pg_strtok(&length); /* get :indexid */ - local_node->indexid = toOidList(nodeRead(true)); + token = pg_strtok(&length); /* get :indexinfo */ + local_node->indexinfo = nodeRead(true); /* now read it */ token = pg_strtok(&length); /* get :indexqual */ local_node->indexqual = nodeRead(true); /* now read it */ @@ -2008,8 +1926,6 @@ parsePlanString(void) return_value = _readOper(); else if (length == 5 && strncmp(token, "PARAM", length) == 0) return_value = _readParam(); - else if (length == 10 && strncmp(token, "RELOPTINFO", length) == 0) - return_value = _readRelOptInfo(); else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0) return_value = _readTargetEntry(); else if (length == 3 && strncmp(token, "RTE", length) == 0) 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; + Expr *expr = (Expr *) clause; - get_relattval(clause, varRelid, - &relidx, &attno, &constval, &flag); - if (relidx != 0) + if (length(expr->args) == 2) { - /* if get_relattval succeeded, it must be an opclause */ - Var *other; + bool varonleft = true; - other = (flag & SEL_RIGHT) ? get_rightop((Expr *) clause) : - get_leftop((Expr *) clause); - if (is_pseudo_constant_clause((Node *) other)) + if (is_pseudo_constant_clause(lsecond(expr->args)) || + (varonleft = false, + is_pseudo_constant_clause(lfirst(expr->args)))) { - Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno; + Oid opno = ((Oper *) expr->oper)->opno; RegProcedure oprrest = get_oprrest(opno); - if (!oprrest) - s2 = (Selectivity) 0.5; - else - s2 = restriction_selectivity(oprrest, opno, - getrelid(relidx, - root->rtable), - attno, - constval, flag); + s2 = restriction_selectivity(root, opno, + expr->args, varRelid); /* * If we reach here, we have computed the same result @@ -171,10 +168,12 @@ clauselist_selectivity(Query *root, switch (oprrest) { case F_SCALARLTSEL: - addRangeClause(&rqlist, clause, flag, true, s2); + addRangeClause(&rqlist, clause, + varonleft, true, s2); break; case F_SCALARGTSEL: - addRangeClause(&rqlist, clause, flag, false, s2); + addRangeClause(&rqlist, clause, + varonleft, false, s2); break; default: /* Just merge the selectivity in generically */ @@ -220,7 +219,7 @@ clauselist_selectivity(Query *root, * No data available --- use a default estimate that * is small, but not real small. */ - s2 = 0.01; + s2 = 0.005; } else { @@ -259,14 +258,13 @@ clauselist_selectivity(Query *root, */ static void addRangeClause(RangeQueryClause **rqlist, Node *clause, - int flag, bool isLTsel, Selectivity s2) + bool varonleft, bool isLTsel, Selectivity s2) { RangeQueryClause *rqelem; Node *var; bool is_lobound; - /* get_relattval sets flag&SEL_RIGHT if the var is on the LEFT. */ - if (flag & SEL_RIGHT) + if (varonleft) { var = (Node *) get_leftop((Expr *) clause); is_lobound = !isLTsel; /* x < something is high bound */ @@ -405,12 +403,12 @@ clause_selectivity(Query *root, * is equivalent to the clause reln.attribute = 't', so we * compute the selectivity as if that is what we have. */ - s1 = restriction_selectivity(F_EQSEL, + s1 = restriction_selectivity(root, BooleanEqualOperator, - rte->relid, - var->varattno, - BoolGetDatum(true), - SEL_CONSTANT | SEL_RIGHT); + makeList2(var, + MAKEBOOLCONST(true, + false)), + varRelid); } } } @@ -486,57 +484,14 @@ clause_selectivity(Query *root, if (is_join_clause) { /* Estimate selectivity for a join clause. */ - RegProcedure oprjoin = get_oprjoin(opno); - - /* - * if the oprjoin procedure is missing for whatever reason, - * use a selectivity of 0.5 - */ - if (!oprjoin) - s1 = (Selectivity) 0.5; - else - { - int relid1, - relid2; - AttrNumber attno1, - attno2; - Oid reloid1, - reloid2; - - get_rels_atts(clause, &relid1, &attno1, &relid2, &attno2); - reloid1 = relid1 ? getrelid(relid1, root->rtable) : InvalidOid; - reloid2 = relid2 ? getrelid(relid2, root->rtable) : InvalidOid; - s1 = join_selectivity(oprjoin, opno, - reloid1, attno1, - reloid2, attno2); - } + s1 = join_selectivity(root, opno, + ((Expr *) clause)->args); } else { /* Estimate selectivity for a restriction clause. */ - RegProcedure oprrest = get_oprrest(opno); - - /* - * if the oprrest procedure is missing for whatever reason, - * use a selectivity of 0.5 - */ - if (!oprrest) - s1 = (Selectivity) 0.5; - else - { - int relidx; - AttrNumber attno; - Datum constval; - int flag; - Oid reloid; - - get_relattval(clause, varRelid, - &relidx, &attno, &constval, &flag); - reloid = relidx ? getrelid(relidx, root->rtable) : InvalidOid; - s1 = restriction_selectivity(oprrest, opno, - reloid, attno, - constval, flag); - } + s1 = restriction_selectivity(root, opno, + ((Expr *) clause)->args, varRelid); } } else if (is_funcclause(clause)) @@ -555,7 +510,7 @@ clause_selectivity(Query *root, /* * Just for the moment! FIX ME! - vadim 02/04/98 */ - s1 = 1.0; + s1 = (Selectivity) 0.5; } else if (IsA(clause, RelabelType)) { 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 -#include "catalog/pg_index.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" @@ -27,6 +26,7 @@ #include "optimizer/planmain.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "optimizer/var.h" #include "parser/parse_expr.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -56,11 +56,11 @@ static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist, Plan *outer_plan, List *outer_tlist, Plan *inner_plan, List *inner_tlist); static List *fix_indxqual_references(List *indexquals, IndexPath *index_path); -static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, - Form_pg_index index); +static List *fix_indxqual_sublist(List *indexqual, int baserelid, + IndexOptInfo *index); static Node *fix_indxqual_operand(Node *node, int baserelid, - Form_pg_index index, - Oid *opclass); + IndexOptInfo *index, + Oid *opclass); static List *switch_outer(List *clauses); static void copy_path_costsize(Plan *dest, Path *src); static void copy_plan_costsize(Plan *dest, Plan *src); @@ -365,7 +365,7 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses) * The indexqual of the path contains a sublist of implicitly-ANDed qual * conditions for each scan of the index(es); if there is more than one * scan then the retrieved tuple sets are ORed together. The indexqual - * and indexid lists must have the same length, ie, the number of scans + * and indexinfo lists must have the same length, ie, the number of scans * that will occur. Note it is possible for a qual condition sublist * to be empty --- then no index restrictions will be applied during that * scan. @@ -380,9 +380,10 @@ create_indexscan_plan(Query *root, Index baserelid; List *qpqual; List *fixed_indxqual; - List *ixid; + List *indexids; + List *ixinfo; IndexScan *scan_plan; - bool lossy = false; + bool lossy; /* there should be exactly one base rel involved... */ Assert(length(best_path->path.parent->relids) == 1); @@ -390,25 +391,18 @@ create_indexscan_plan(Query *root, baserelid = lfirsti(best_path->path.parent->relids); - /* check to see if any of the indices are lossy */ - foreach(ixid, best_path->indexid) + /* + * Build list of index OIDs, and check to see if any of the indices + * are lossy. + */ + indexids = NIL; + lossy = false; + foreach(ixinfo, best_path->indexinfo) { - HeapTuple indexTuple; - Form_pg_index index; + IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo); - indexTuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(lfirsti(ixid)), - 0, 0, 0); - if (!HeapTupleIsValid(indexTuple)) - elog(ERROR, "create_plan: index %u not found", lfirsti(ixid)); - index = (Form_pg_index) GETSTRUCT(indexTuple); - if (index->indislossy) - { - lossy = true; - ReleaseSysCache(indexTuple); - break; - } - ReleaseSysCache(indexTuple); + indexids = lappendi(indexids, index->indexoid); + lossy |= index->lossy; } /* @@ -471,7 +465,7 @@ create_indexscan_plan(Query *root, scan_plan = make_indexscan(tlist, qpqual, baserelid, - best_path->indexid, + indexids, fixed_indxqual, indxqual, best_path->indexscandir); @@ -895,45 +889,19 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path) { List *fixed_quals = NIL; int baserelid = lfirsti(index_path->path.parent->relids); - List *indexids = index_path->indexid; + List *ixinfo = index_path->indexinfo; List *i; foreach(i, indexquals) { List *indexqual = lfirst(i); - Oid indexid = lfirsti(indexids); - HeapTuple indexTuple; - Oid relam; - Form_pg_index index; - - /* Get the relam from the index's pg_class entry */ - indexTuple = SearchSysCache(RELOID, - ObjectIdGetDatum(indexid), - 0, 0, 0); - if (!HeapTupleIsValid(indexTuple)) - elog(ERROR, "fix_indxqual_references: index %u not found in pg_class", - indexid); - relam = ((Form_pg_class) GETSTRUCT(indexTuple))->relam; - ReleaseSysCache(indexTuple); - - /* Need the index's pg_index entry for other stuff */ - indexTuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexid), - 0, 0, 0); - if (!HeapTupleIsValid(indexTuple)) - elog(ERROR, "fix_indxqual_references: index %u not found in pg_index", - indexid); - index = (Form_pg_index) GETSTRUCT(indexTuple); + IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo); fixed_quals = lappend(fixed_quals, fix_indxqual_sublist(indexqual, baserelid, - relam, index)); - - ReleaseSysCache(indexTuple); - - indexids = lnext(indexids); + ixinfo = lnext(ixinfo); } return fixed_quals; } @@ -946,8 +914,7 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path) * of the clause.) Also change the operator if necessary. */ static List * -fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, - Form_pg_index index) +fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index) { List *fixed_qual = NIL; List *i; @@ -955,26 +922,14 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, foreach(i, indexqual) { Expr *clause = (Expr *) lfirst(i); - int relid; - AttrNumber attno; - Datum constval; - int flag; Expr *newclause; + List *leftvarnos; Oid opclass, newopno; - if (!is_opclause((Node *) clause) || - length(clause->args) != 2) + if (!is_opclause((Node *) clause) || length(clause->args) != 2) elog(ERROR, "fix_indxqual_sublist: indexqual clause is not binary opclause"); - /* - * Which side is the indexkey on? - * - * get_relattval sets flag&SEL_RIGHT if the indexkey is on the LEFT. - */ - get_relattval((Node *) clause, baserelid, - &relid, &attno, &constval, &flag); - /* * Make a copy that will become the fixed clause. * @@ -984,9 +939,15 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, */ newclause = (Expr *) copyObject((Node *) clause); - /* If the indexkey is on the right, commute the clause. */ - if ((flag & SEL_RIGHT) == 0) + /* + * Check to see if the indexkey is on the right; if so, commute + * the clause. The indexkey should be the side that refers to + * (only) the base relation. + */ + leftvarnos = pull_varnos((Node *) lfirst(newclause->args)); + if (length(leftvarnos) != 1 || lfirsti(leftvarnos) != baserelid) CommuteClause(newclause); + freeList(leftvarnos); /* * Now, determine which index attribute this is, change the @@ -1002,7 +963,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, * is merely binary-compatible with the index. This shouldn't * fail, since indxpath.c found it before... */ - newopno = indexable_operator(newclause, opclass, relam, true); + newopno = indexable_operator(newclause, opclass, index->relam, true); if (newopno == InvalidOid) elog(ERROR, "fix_indxqual_sublist: failed to find substitute op"); ((Oper *) newclause->oper)->opno = newopno; @@ -1013,7 +974,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, } static Node * -fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index, +fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, Oid *opclass) { @@ -1033,27 +994,29 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index, if (IsA(node, Var)) { /* If it's a var, find which index key position it occupies */ + Assert(index->indproc == InvalidOid); + if (((Var *) node)->varno == baserelid) { int varatt = ((Var *) node)->varattno; int pos; - for (pos = 0; pos < INDEX_MAX_KEYS; pos++) + for (pos = 0; pos < index->nkeys; pos++) { - if (index->indkey[pos] == varatt) + if (index->indexkeys[pos] == varatt) { Node *newnode = copyObject(node); ((Var *) newnode)->varattno = pos + 1; /* return the correct opclass, too */ - *opclass = index->indclass[pos]; + *opclass = index->classlist[pos]; return newnode; } } } /* - * Oops, this Var isn't the indexkey! + * Oops, this Var isn't an indexkey! */ elog(ERROR, "fix_indxqual_operand: var is not index attribute"); } @@ -1063,11 +1026,11 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index, * Since we currently only support single-column functional indexes, * the returned varattno must be 1. */ + Assert(index->indproc != InvalidOid); + Assert(is_funcclause(node)); /* not a very thorough check, but easy */ - Assert(is_funcclause(node));/* not a very thorough check, but easy */ - - /* indclass[0] is the only class of a functional index */ - *opclass = index->indclass[0]; + /* classlist[0] is the only class of a functional index */ + *opclass = index->classlist[0]; return (Node *) makeVar(baserelid, 1, exprType(node), -1, 0); } 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); @@ -67,17 +75,53 @@ get_base_rel(Query *root, int relid) return rel; } +/* + * build_other_rel + * Returns relation entry corresponding to 'relid', creating a new one + * if necessary. This is for 'other' relations, which are just like + * base relations except that they live in a different list. + */ +RelOptInfo * +build_other_rel(Query *root, int relid) +{ + List *rels; + RelOptInfo *rel; + + /* Already made? */ + foreach(rels, root->other_rel_list) + { + rel = (RelOptInfo *) lfirst(rels); + + /* length(rel->relids) == 1 for all members of other_rel_list */ + if (lfirsti(rel->relids) == relid) + return rel; + } + + /* It should not exist as a base rel */ + foreach(rels, root->base_rel_list) + { + rel = (RelOptInfo *) lfirst(rels); + + if (lfirsti(rel->relids) == relid) + elog(ERROR, "build_other_rel: rel already exists as base rel"); + } + + /* No existing RelOptInfo for this other rel, so make a new one */ + rel = make_base_rel(root, relid); + + /* and add it to the list */ + root->other_rel_list = lcons(rel, root->other_rel_list); + + return rel; +} + /* * make_base_rel * Construct a base-relation RelOptInfo for the specified rangetable index. * - * This is split out of get_base_rel so that inheritance-tree processing can - * construct baserel nodes for child tables. We need a RelOptInfo so we can - * plan a suitable access path for each child table, but we do NOT want to - * enter the child nodes into base_rel_list. In most contexts, get_base_rel - * should be called instead. + * Common code for build_base_rel and build_other_rel. */ -RelOptInfo * +static RelOptInfo * make_base_rel(Query *root, int relid) { RelOptInfo *rel = makeNode(RelOptInfo); @@ -92,7 +136,7 @@ make_base_rel(Query *root, int relid) rel->cheapest_total_path = NULL; rel->pruneable = true; rel->issubquery = false; - rel->indexed = false; + rel->indexlist = NIL; rel->pages = 0; rel->tuples = 0; rel->subplan = NULL; @@ -108,8 +152,12 @@ make_base_rel(Query *root, int relid) if (relationObjectId != InvalidOid) { /* Plain relation --- retrieve statistics from the system catalogs */ - relation_info(relationObjectId, - &rel->indexed, &rel->pages, &rel->tuples); + bool indexed; + + get_relation_info(relationObjectId, + &indexed, &rel->pages, &rel->tuples); + if (indexed) + rel->indexlist = find_secondary_indexes(relationObjectId); } else { @@ -120,13 +168,46 @@ make_base_rel(Query *root, int relid) return rel; } +/* + * find_base_rel + * Find a base or other relation entry, which must already exist + * (since we'd have no idea which list to add it to). + */ +RelOptInfo * +find_base_rel(Query *root, int relid) +{ + List *rels; + RelOptInfo *rel; + + foreach(rels, root->base_rel_list) + { + rel = (RelOptInfo *) lfirst(rels); + + /* length(rel->relids) == 1 for all members of base_rel_list */ + if (lfirsti(rel->relids) == relid) + return rel; + } + + foreach(rels, root->other_rel_list) + { + rel = (RelOptInfo *) lfirst(rels); + + if (lfirsti(rel->relids) == relid) + return rel; + } + + elog(ERROR, "find_base_rel: no relation entry for relid %d", relid); + + return NULL; /* keep compiler quiet */ +} + /* * find_join_rel * Returns relation entry corresponding to 'relids' (a list of RT indexes), * or NULL if none exists. This is for join relations. * * Note: there is probably no good reason for this to be called from - * anywhere except get_join_rel, but keep it as a separate routine + * anywhere except build_join_rel, but keep it as a separate routine * just in case. */ static RelOptInfo * @@ -146,7 +227,7 @@ find_join_rel(Query *root, Relids relids) } /* - * get_join_rel + * build_join_rel * Returns relation entry corresponding to the union of two given rels, * creating a new relation entry if none already exists. * @@ -161,11 +242,11 @@ find_join_rel(Query *root, Relids relids) * duplicated calculation of the restrictlist... */ RelOptInfo * -get_join_rel(Query *root, - RelOptInfo *outer_rel, - RelOptInfo *inner_rel, - JoinType jointype, - List **restrictlist_ptr) +build_join_rel(Query *root, + RelOptInfo *outer_rel, + RelOptInfo *inner_rel, + JoinType jointype, + List **restrictlist_ptr) { List *joinrelids; RelOptInfo *joinrel; @@ -212,7 +293,7 @@ get_join_rel(Query *root, joinrel->cheapest_total_path = NULL; joinrel->pruneable = true; joinrel->issubquery = false; - joinrel->indexed = false; + joinrel->indexlist = NIL; joinrel->pages = 0; joinrel->tuples = 0; joinrel->subplan = NULL; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d7633dc47dd..07c4da115f5 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,11 +15,57 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.89 2001/05/09 23:13:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.90 2001/05/20 20:28:19 tgl Exp $ * *------------------------------------------------------------------------- */ +/*---------- + * Operator selectivity estimation functions are called to estimate the + * selectivity of WHERE clauses whose top-level operator is their operator. + * We divide the problem into two cases: + * Restriction clause estimation: the clause involves vars of just + * one relation. + * Join clause estimation: the clause involves vars of multiple rels. + * Join selectivity estimation is far more difficult and usually less accurate + * than restriction estimation. + * + * When dealing with the inner scan of a nestloop join, we consider the + * join's joinclauses as restriction clauses for the inner relation, and + * treat vars of the outer relation as parameters (a/k/a constants of unknown + * values). So, restriction estimators need to be able to accept an argument + * telling which relation is to be treated as the variable. + * + * The call convention for a restriction estimator (oprrest function) is + * + * Selectivity oprrest (Query *root, + * Oid operator, + * List *args, + * int varRelid); + * + * root: general information about the query (rtable and RelOptInfo lists + * are particularly important for the estimator). + * operator: OID of the specific operator in question. + * args: argument list from the operator clause. + * varRelid: if not zero, the relid (rtable index) of the relation to + * be treated as the variable relation. May be zero if the args list + * is known to contain vars of only one relation. + * + * This is represented at the SQL level (in pg_proc) as + * + * float8 oprrest (opaque, oid, opaque, int4); + * + * The call convention for a join estimator (oprjoin function) is similar + * except that varRelid is not needed: + * + * Selectivity oprjoin (Query *root, + * Oid operator, + * List *args); + * + * float8 oprjoin (opaque, oid, opaque); + *---------- + */ + #include "postgres.h" #include @@ -35,8 +81,11 @@ #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "mb/pg_wchar.h" +#include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/pathnode.h" +#include "optimizer/plancat.h" #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parsetree.h" @@ -46,17 +95,28 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" -/* N is not a valid var/constant or relation id */ -#define NONVALUE(N) ((N) == 0) +/* + * Note: the default selectivity estimates are not chosen entirely at random. + * We want them to be small enough to ensure that indexscans will be used if + * available, for typical table densities of ~100 tuples/page. Thus, for + * example, 0.01 is not quite small enough, since that makes it appear that + * nearly all pages will be hit anyway. Also, since we sometimes estimate + * eqsel as 1/num_distinct, we probably want DEFAULT_NUM_DISTINCT to equal + * 1/DEFAULT_EQ_SEL. + */ /* default selectivity estimate for equalities such as "A = b" */ -#define DEFAULT_EQ_SEL 0.01 +#define DEFAULT_EQ_SEL 0.005 /* default selectivity estimate for inequalities such as "A < b" */ #define DEFAULT_INEQ_SEL (1.0 / 3.0) /* default selectivity estimate for pattern-match operators such as LIKE */ -#define DEFAULT_MATCH_SEL 0.01 +#define DEFAULT_MATCH_SEL 0.005 + +/* default number of distinct values in a table */ +#define DEFAULT_NUM_DISTINCT 200 + static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, Datum lobound, Datum hibound, Oid boundstypid, @@ -72,19 +132,19 @@ static double convert_one_string_to_scalar(unsigned char *value, int rangelo, int rangehi); static unsigned char *convert_string_datum(Datum value, Oid typid); static double convert_timevalue_to_scalar(Datum value, Oid typid); -static void getattproperties(Oid relid, AttrNumber attnum, - Oid *typid, int32 *typmod); -static double get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid, +static double get_att_numdistinct(Query *root, Var *var, Form_pg_statistic stats); -static Selectivity prefix_selectivity(char *prefix, - Oid relid, - AttrNumber attno, - Oid datatype); +static bool get_restriction_var(List *args, int varRelid, + Var **var, Node **other, + bool *varonleft); +static void get_join_vars(List *args, Var **var1, Var **var2); +static Selectivity prefix_selectivity(Query *root, Var *var, char *prefix); static Selectivity pattern_selectivity(char *patt, Pattern_Type ptype); static bool string_lessthan(const char *str1, const char *str2, Oid datatype); static Oid find_operator(const char *opname, Oid datatype); static Datum string_to_datum(const char *str, Oid datatype); +static Const *string_to_const(const char *str, Oid datatype); /* @@ -93,20 +153,19 @@ static Datum string_to_datum(const char *str, Oid datatype); * Note: this routine is also used to estimate selectivity for some * operators that are not "=" but have comparable selectivity behavior, * such as "~=" (geometric approximate-match). Even for "=", we must - * keep in mind that the left and right datatypes may differ, so the type - * of the given constant "value" may be different from the type of the - * attribute. + * keep in mind that the left and right datatypes may differ. */ Datum eqsel(PG_FUNCTION_ARGS) { - Oid opid = PG_GETARG_OID(0); - Oid relid = PG_GETARG_OID(1); - AttrNumber attno = PG_GETARG_INT16(2); - Datum value = PG_GETARG_DATUM(3); - int32 flag = PG_GETARG_INT32(4); - Oid typid; - int32 typmod; + Query *root = (Query *) PG_GETARG_POINTER(0); + Oid operator = PG_GETARG_OID(1); + List *args = (List *) PG_GETARG_POINTER(2); + int varRelid = PG_GETARG_INT32(3); + Var *var; + Node *other; + bool varonleft; + Oid relid; HeapTuple statsTuple; Datum *values; int nvalues; @@ -114,16 +173,29 @@ eqsel(PG_FUNCTION_ARGS) int nnumbers; double selec; - if (NONVALUE(relid) || NONVALUE(attno)) + /* + * If expression is not var = something or something = var for + * a simple var of a real relation (no subqueries, for now), + * then punt and return a default estimate. + */ + if (!get_restriction_var(args, varRelid, + &var, &other, &varonleft)) + PG_RETURN_FLOAT8(DEFAULT_EQ_SEL); + relid = getrelid(var->varno, root->rtable); + if (relid == InvalidOid) PG_RETURN_FLOAT8(DEFAULT_EQ_SEL); - /* get info about the attribute */ - getattproperties(relid, attno, &typid, &typmod); + /* + * If the something is a NULL constant, assume operator is strict + * and return zero, ie, operator will never return TRUE. + */ + if (IsA(other, Const) && ((Const *) other)->constisnull) + PG_RETURN_FLOAT8(0.0); /* get stats for the attribute, if available */ statsTuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid), - Int16GetDatum(attno), + Int16GetDatum(var->varattno), 0, 0); if (HeapTupleIsValid(statsTuple)) { @@ -131,8 +203,10 @@ eqsel(PG_FUNCTION_ARGS) stats = (Form_pg_statistic) GETSTRUCT(statsTuple); - if (flag & SEL_CONSTANT) + if (IsA(other, Const)) { + /* Var is being compared to a known non-null constant */ + Datum constval = ((Const *) other)->constvalue; bool match = false; int i; @@ -143,25 +217,25 @@ eqsel(PG_FUNCTION_ARGS) * is an appropriate test. If you don't like this, maybe you * shouldn't be using eqsel for your operator...) */ - if (get_attstatsslot(statsTuple, typid, typmod, + if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod, STATISTIC_KIND_MCV, InvalidOid, &values, &nvalues, &numbers, &nnumbers)) { FmgrInfo eqproc; - fmgr_info(get_opcode(opid), &eqproc); + fmgr_info(get_opcode(operator), &eqproc); for (i = 0; i < nvalues; i++) { /* be careful to apply operator right way 'round */ - if (flag & SEL_RIGHT) + if (varonleft) match = DatumGetBool(FunctionCall2(&eqproc, values[i], - value)); + constval)); else match = DatumGetBool(FunctionCall2(&eqproc, - value, + constval, values[i])); if (match) break; @@ -203,8 +277,7 @@ eqsel(PG_FUNCTION_ARGS) * share this remaining fraction equally, so we * divide by the number of other distinct values. */ - otherdistinct = get_att_numdistinct(relid, attno, - typid, stats) + otherdistinct = get_att_numdistinct(root, var, stats) - nnumbers; if (otherdistinct > 1) selec /= otherdistinct; @@ -217,7 +290,8 @@ eqsel(PG_FUNCTION_ARGS) selec = numbers[nnumbers-1]; } - free_attstatsslot(typid, values, nvalues, numbers, nnumbers); + free_attstatsslot(var->vartype, values, nvalues, + numbers, nnumbers); } else { @@ -234,21 +308,21 @@ eqsel(PG_FUNCTION_ARGS) * frequency in the table. Is that a good idea?) */ selec = 1.0 - stats->stanullfrac; - ndistinct = get_att_numdistinct(relid, attno, typid, stats); + ndistinct = get_att_numdistinct(root, var, stats); if (ndistinct > 1) selec /= ndistinct; /* * Cross-check: selectivity should never be * estimated as more than the most common value's. */ - if (get_attstatsslot(statsTuple, typid, typmod, + if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod, STATISTIC_KIND_MCV, InvalidOid, NULL, NULL, &numbers, &nnumbers)) { if (nnumbers > 0 && selec > numbers[0]) selec = numbers[0]; - free_attstatsslot(typid, NULL, 0, numbers, nnumbers); + free_attstatsslot(var->vartype, NULL, 0, numbers, nnumbers); } } @@ -262,7 +336,7 @@ eqsel(PG_FUNCTION_ARGS) * equally common. (The guess is unlikely to be very good, * but we do know a few special cases.) */ - selec = 1.0 / get_att_numdistinct(relid, attno, typid, NULL); + selec = 1.0 / get_att_numdistinct(root, var, NULL); } /* result should be in range, but make sure... */ @@ -284,27 +358,25 @@ eqsel(PG_FUNCTION_ARGS) Datum neqsel(PG_FUNCTION_ARGS) { - Oid opid = PG_GETARG_OID(0); - Oid relid = PG_GETARG_OID(1); - AttrNumber attno = PG_GETARG_INT16(2); - Datum value = PG_GETARG_DATUM(3); - int32 flag = PG_GETARG_INT32(4); - Oid eqopid; + Query *root = (Query *) PG_GETARG_POINTER(0); + Oid operator = PG_GETARG_OID(1); + List *args = (List *) PG_GETARG_POINTER(2); + int varRelid = PG_GETARG_INT32(3); + Oid eqop; float8 result; /* * We want 1 - eqsel() where the equality operator is the one * associated with this != operator, that is, its negator. */ - eqopid = get_negator(opid); - if (eqopid) + eqop = get_negator(operator); + if (eqop) { - result = DatumGetFloat8(DirectFunctionCall5(eqsel, - ObjectIdGetDatum(eqopid), - ObjectIdGetDatum(relid), - Int16GetDatum(attno), - value, - Int32GetDatum(flag))); + result = DatumGetFloat8(DirectFunctionCall4(eqsel, + PointerGetDatum(root), + ObjectIdGetDatum(eqop), + PointerGetDatum(args), + Int32GetDatum(varRelid))); } else { @@ -316,28 +388,26 @@ neqsel(PG_FUNCTION_ARGS) } /* - * scalarltsel - Selectivity of "<" (also "<=") for scalars. + * scalarineqsel - Selectivity of "<", "<=", ">", ">=" for scalars. + * + * This is the guts of both scalarltsel and scalargtsel. The caller has + * commuted the clause, if necessary, so that we can treat the Var as + * being on the left. * * This routine works for any datatype (or pair of datatypes) known to * convert_to_scalar(). If it is applied to some other datatype, * it will return a default estimate. */ -Datum -scalarltsel(PG_FUNCTION_ARGS) +static double +scalarineqsel(Query *root, Oid operator, bool isgt, + Var *var, Node *other) { - Oid opid = PG_GETARG_OID(0); - Oid relid = PG_GETARG_OID(1); - AttrNumber attno = PG_GETARG_INT16(2); - Datum value = PG_GETARG_DATUM(3); - int32 flag = PG_GETARG_INT32(4); - bool isgt; - HeapTuple oprTuple; + Oid relid; + Datum constval; + Oid consttype; HeapTuple statsTuple; Form_pg_statistic stats; - Oid contype; FmgrInfo opproc; - Oid typid; - int32 typmod; Datum *values; int nvalues; float4 *numbers; @@ -348,62 +418,44 @@ scalarltsel(PG_FUNCTION_ARGS) double selec; int i; - if (NONVALUE(relid) || NONVALUE(attno)) - PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); - - /* Can't do anything useful if no constant to compare against, either */ - if (!(flag & SEL_CONSTANT)) - PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + /* + * If expression is not var op something or something op var for + * a simple var of a real relation (no subqueries, for now), + * then punt and return a default estimate. + */ + relid = getrelid(var->varno, root->rtable); + if (relid == InvalidOid) + return DEFAULT_INEQ_SEL; /* - * Force the constant to be on the right to simplify later logic. - * This means that we may be dealing with either "<" or ">" cases. + * Can't do anything useful if the something is not a constant, either. */ - if (flag & SEL_RIGHT) - { - /* we have x < const */ - isgt = false; - } - else - { - /* we have const < x, commute to make x > const */ - opid = get_commutator(opid); - if (!opid) - { - /* Use default selectivity (should we raise an error instead?) */ - PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); - } - isgt = true; - } + if (! IsA(other, Const)) + return DEFAULT_INEQ_SEL; /* - * The constant might not be the same datatype as the column; - * look at the operator's input types to find out what it is. - * Also set up to be able to call the operator's execution proc. + * If the constant is NULL, assume operator is strict + * and return zero, ie, operator will never return TRUE. */ - oprTuple = SearchSysCache(OPEROID, - ObjectIdGetDatum(opid), - 0, 0, 0); - if (!HeapTupleIsValid(oprTuple)) - elog(ERROR, "scalarltsel: no tuple for operator %u", opid); - contype = ((Form_pg_operator) GETSTRUCT(oprTuple))->oprright; - fmgr_info(((Form_pg_operator) GETSTRUCT(oprTuple))->oprcode, &opproc); - ReleaseSysCache(oprTuple); - - /* Now get info and stats about the attribute */ - getattproperties(relid, attno, &typid, &typmod); + if (((Const *) other)->constisnull) + return 0.0; + constval = ((Const *) other)->constvalue; + consttype = ((Const *) other)->consttype; + /* get stats for the attribute */ statsTuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid), - Int16GetDatum(attno), + Int16GetDatum(var->varattno), 0, 0); if (!HeapTupleIsValid(statsTuple)) { /* no stats available, so default result */ - PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + return DEFAULT_INEQ_SEL; } stats = (Form_pg_statistic) GETSTRUCT(statsTuple); + fmgr_info(get_opcode(operator), &opproc); + /* * If we have most-common-values info, add up the fractions of the * MCV entries that satisfy MCV OP CONST. These fractions contribute @@ -413,7 +465,7 @@ scalarltsel(PG_FUNCTION_ARGS) mcv_selec = 0.0; sumcommon = 0.0; - if (get_attstatsslot(statsTuple, typid, typmod, + if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod, STATISTIC_KIND_MCV, InvalidOid, &values, &nvalues, &numbers, &nnumbers)) @@ -422,11 +474,11 @@ scalarltsel(PG_FUNCTION_ARGS) { if (DatumGetBool(FunctionCall2(&opproc, values[i], - value))) + constval))) mcv_selec += numbers[i]; sumcommon += numbers[i]; } - free_attstatsslot(typid, values, nvalues, numbers, nnumbers); + free_attstatsslot(var->vartype, values, nvalues, numbers, nnumbers); } /* @@ -440,11 +492,11 @@ scalarltsel(PG_FUNCTION_ARGS) * have at hand! (For example, we might have a '<=' operator rather * than the '<' operator that will appear in staop.) For now, assume * that whatever appears in pg_statistic is sorted the same way our - * operator sorts. + * operator sorts, or the reverse way if isgt is TRUE. */ hist_selec = 0.0; - if (get_attstatsslot(statsTuple, typid, typmod, + if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod, STATISTIC_KIND_HISTOGRAM, InvalidOid, &values, &nvalues, NULL, NULL)) @@ -456,7 +508,7 @@ scalarltsel(PG_FUNCTION_ARGS) ltcmp = DatumGetBool(FunctionCall2(&opproc, values[0], - value)); + constval)); if (isgt) ltcmp = !ltcmp; if (!ltcmp) @@ -475,7 +527,7 @@ scalarltsel(PG_FUNCTION_ARGS) { ltcmp = DatumGetBool(FunctionCall2(&opproc, values[i], - value)); + constval)); if (isgt) ltcmp = !ltcmp; if (!ltcmp) @@ -500,8 +552,9 @@ scalarltsel(PG_FUNCTION_ARGS) * values to a uniform comparison scale, and do a linear * interpolation within this bin. */ - if (convert_to_scalar(value, contype, &val, - values[i-1], values[i], typid, + if (convert_to_scalar(constval, consttype, &val, + values[i-1], values[i], + var->vartype, &low, &high)) { if (high <= low) @@ -520,10 +573,10 @@ scalarltsel(PG_FUNCTION_ARGS) { /* * Ideally we'd produce an error here, on the grounds - * that the given operator shouldn't have scalarltsel + * that the given operator shouldn't have scalarXXsel * registered as its selectivity func unless we can * deal with its operand types. But currently, all - * manner of stuff is invoking scalarltsel, so give a + * manner of stuff is invoking scalarXXsel, so give a * default estimate until that can be fixed. */ binfrac = 0.5; @@ -549,13 +602,13 @@ scalarltsel(PG_FUNCTION_ARGS) * don't believe extremely small or large selectivity * estimates. */ - if (hist_selec < 0.001) - hist_selec = 0.001; - else if (hist_selec > 0.999) - hist_selec = 0.999; + if (hist_selec < 0.0001) + hist_selec = 0.0001; + else if (hist_selec > 0.9999) + hist_selec = 0.9999; } - free_attstatsslot(typid, values, nvalues, NULL, 0); + free_attstatsslot(var->vartype, values, nvalues, NULL, 0); } /* @@ -586,141 +639,210 @@ scalarltsel(PG_FUNCTION_ARGS) else if (selec > 1.0) selec = 1.0; + return selec; +} + +/* + * scalarltsel - Selectivity of "<" (also "<=") for scalars. + */ +Datum +scalarltsel(PG_FUNCTION_ARGS) +{ + Query *root = (Query *) PG_GETARG_POINTER(0); + Oid operator = PG_GETARG_OID(1); + List *args = (List *) PG_GETARG_POINTER(2); + int varRelid = PG_GETARG_INT32(3); + Var *var; + Node *other; + bool varonleft; + bool isgt; + double selec; + + /* + * If expression is not var op something or something op var for + * a simple var of a real relation (no subqueries, for now), + * then punt and return a default estimate. + */ + if (!get_restriction_var(args, varRelid, + &var, &other, &varonleft)) + PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + + /* + * Force the var to be on the left to simplify logic in scalarineqsel. + */ + if (varonleft) + { + /* we have var < other */ + isgt = false; + } + else + { + /* we have other < var, commute to make var > other */ + operator = get_commutator(operator); + if (!operator) + { + /* Use default selectivity (should we raise an error instead?) */ + PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + } + isgt = true; + } + + selec = scalarineqsel(root, operator, isgt, var, other); + PG_RETURN_FLOAT8((float8) selec); } /* * scalargtsel - Selectivity of ">" (also ">=") for integers. - * - * See above comments for scalarltsel. */ Datum scalargtsel(PG_FUNCTION_ARGS) { - Oid opid = PG_GETARG_OID(0); - Oid relid = PG_GETARG_OID(1); - AttrNumber attno = PG_GETARG_INT16(2); - Datum value = PG_GETARG_DATUM(3); - int32 flag = PG_GETARG_INT32(4); - Oid ltopid; + Query *root = (Query *) PG_GETARG_POINTER(0); + Oid operator = PG_GETARG_OID(1); + List *args = (List *) PG_GETARG_POINTER(2); + int varRelid = PG_GETARG_INT32(3); + Var *var; + Node *other; + bool varonleft; + bool isgt; + double selec; /* - * Commute so that we have a "<" or "<=" operator, then apply - * scalarltsel. + * If expression is not var op something or something op var for + * a simple var of a real relation (no subqueries, for now), + * then punt and return a default estimate. */ - ltopid = get_commutator(opid); - if (!ltopid) - { - /* Use default selectivity (should we raise an error instead?) */ + if (!get_restriction_var(args, varRelid, + &var, &other, &varonleft)) PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + + /* + * Force the var to be on the left to simplify logic in scalarineqsel. + */ + if (varonleft) + { + /* we have var > other */ + isgt = true; + } + else + { + /* we have other > var, commute to make var < other */ + operator = get_commutator(operator); + if (!operator) + { + /* Use default selectivity (should we raise an error instead?) */ + PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + } + isgt = false; } - flag ^= SEL_RIGHT; - return DirectFunctionCall5(scalarltsel, - ObjectIdGetDatum(ltopid), - ObjectIdGetDatum(relid), - Int16GetDatum(attno), - value, - Int32GetDatum(flag)); + selec = scalarineqsel(root, operator, isgt, var, other); + + PG_RETURN_FLOAT8((float8) selec); } /* * patternsel - Generic code for pattern-match selectivity. */ -static Datum +static double patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype) { - Oid opid = PG_GETARG_OID(0); - Oid relid = PG_GETARG_OID(1); - AttrNumber attno = PG_GETARG_INT16(2); - Datum value = PG_GETARG_DATUM(3); - int32 flag = PG_GETARG_INT32(4); - float8 result; + Query *root = (Query *) PG_GETARG_POINTER(0); +#ifdef NOT_USED + Oid operator = PG_GETARG_OID(1); +#endif + List *args = (List *) PG_GETARG_POINTER(2); + int varRelid = PG_GETARG_INT32(3); + Var *var; + Node *other; + bool varonleft; + Oid relid; + Datum constval; + char *patt; + Pattern_Prefix_Status pstatus; + char *prefix; + char *rest; + double result; - /* Must have a constant for the pattern, or cannot learn anything */ - if ((flag & (SEL_CONSTANT | SEL_RIGHT)) != (SEL_CONSTANT | SEL_RIGHT)) - result = DEFAULT_MATCH_SEL; + /* + * If expression is not var op constant for + * a simple var of a real relation (no subqueries, for now), + * then punt and return a default estimate. + */ + if (!get_restriction_var(args, varRelid, + &var, &other, &varonleft)) + return DEFAULT_MATCH_SEL; + if (!varonleft || !IsA(other, Const)) + return DEFAULT_MATCH_SEL; + relid = getrelid(var->varno, root->rtable); + if (relid == InvalidOid) + return DEFAULT_MATCH_SEL; + + /* + * If the constant is NULL, assume operator is strict + * and return zero, ie, operator will never return TRUE. + */ + if (((Const *) other)->constisnull) + return 0.0; + constval = ((Const *) other)->constvalue; + /* the right-hand const is type text for all supported operators */ + Assert(((Const *) other)->consttype == TEXTOID); + patt = DatumGetCString(DirectFunctionCall1(textout, constval)); + + /* divide pattern into fixed prefix and remainder */ + pstatus = pattern_fixed_prefix(patt, ptype, &prefix, &rest); + + if (pstatus == Pattern_Prefix_Exact) + { + /* + * Pattern specifies an exact match, so pretend operator is '=' + */ + Oid eqopr = find_operator("=", var->vartype); + Const *eqcon; + List *eqargs; + + if (eqopr == InvalidOid) + elog(ERROR, "patternsel: no = operator for type %u", + var->vartype); + eqcon = string_to_const(prefix, var->vartype); + eqargs = makeList2(var, eqcon); + result = DatumGetFloat8(DirectFunctionCall4(eqsel, + PointerGetDatum(root), + ObjectIdGetDatum(eqopr), + PointerGetDatum(eqargs), + Int32GetDatum(varRelid))); + } else { - HeapTuple oprTuple; - Oid ltype, - rtype; - char *patt; - Pattern_Prefix_Status pstatus; - char *prefix; - char *rest; - /* - * Get left and right datatypes of the operator so we know what - * type the attribute is. + * Not exact-match pattern. We estimate selectivity of the + * fixed prefix and remainder of pattern separately, then + * combine the two. */ - oprTuple = SearchSysCache(OPEROID, - ObjectIdGetDatum(opid), - 0, 0, 0); - if (!HeapTupleIsValid(oprTuple)) - elog(ERROR, "patternsel: no tuple for operator %u", opid); - ltype = ((Form_pg_operator) GETSTRUCT(oprTuple))->oprleft; - rtype = ((Form_pg_operator) GETSTRUCT(oprTuple))->oprright; - ReleaseSysCache(oprTuple); + Selectivity prefixsel; + Selectivity restsel; + Selectivity selec; - /* the right-hand const is type text for all supported operators */ - Assert(rtype == TEXTOID); - patt = DatumGetCString(DirectFunctionCall1(textout, value)); - - /* divide pattern into fixed prefix and remainder */ - pstatus = pattern_fixed_prefix(patt, ptype, &prefix, &rest); - - if (pstatus == Pattern_Prefix_Exact) - { - - /* - * Pattern specifies an exact match, so pretend operator is - * '=' - */ - Oid eqopr = find_operator("=", ltype); - Datum eqcon; - - if (eqopr == InvalidOid) - elog(ERROR, "patternsel: no = operator for type %u", ltype); - eqcon = string_to_datum(prefix, ltype); - result = DatumGetFloat8(DirectFunctionCall5(eqsel, - ObjectIdGetDatum(eqopr), - ObjectIdGetDatum(relid), - Int16GetDatum(attno), - eqcon, - Int32GetDatum(SEL_CONSTANT | SEL_RIGHT))); - pfree(DatumGetPointer(eqcon)); - } + if (pstatus == Pattern_Prefix_Partial) + prefixsel = prefix_selectivity(root, var, prefix); else - { - - /* - * Not exact-match pattern. We estimate selectivity of the - * fixed prefix and remainder of pattern separately, then - * combine the two. - */ - Selectivity prefixsel; - Selectivity restsel; - Selectivity selec; - - if (pstatus == Pattern_Prefix_Partial) - prefixsel = prefix_selectivity(prefix, relid, attno, ltype); - else - prefixsel = 1.0; - restsel = pattern_selectivity(rest, ptype); - selec = prefixsel * restsel; - /* result should be in range, but make sure... */ - if (selec < 0.0) - selec = 0.0; - else if (selec > 1.0) - selec = 1.0; - result = (float8) selec; - } - if (prefix) - pfree(prefix); - pfree(patt); + prefixsel = 1.0; + restsel = pattern_selectivity(rest, ptype); + selec = prefixsel * restsel; + /* result should be in range, but make sure... */ + if (selec < 0.0) + selec = 0.0; + else if (selec > 1.0) + selec = 1.0; + result = selec; } - PG_RETURN_FLOAT8(result); + + if (prefix) + pfree(prefix); + pfree(patt); + + return result; } /* @@ -729,7 +851,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype) Datum regexeqsel(PG_FUNCTION_ARGS) { - return patternsel(fcinfo, Pattern_Type_Regex); + PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Regex)); } /* @@ -738,7 +860,7 @@ regexeqsel(PG_FUNCTION_ARGS) Datum icregexeqsel(PG_FUNCTION_ARGS) { - return patternsel(fcinfo, Pattern_Type_Regex_IC); + PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Regex_IC)); } /* @@ -747,7 +869,7 @@ icregexeqsel(PG_FUNCTION_ARGS) Datum likesel(PG_FUNCTION_ARGS) { - return patternsel(fcinfo, Pattern_Type_Like); + PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Like)); } /* @@ -756,7 +878,7 @@ likesel(PG_FUNCTION_ARGS) Datum iclikesel(PG_FUNCTION_ARGS) { - return patternsel(fcinfo, Pattern_Type_Like_IC); + PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Like_IC)); } /* @@ -765,9 +887,9 @@ iclikesel(PG_FUNCTION_ARGS) Datum regexnesel(PG_FUNCTION_ARGS) { - float8 result; + double result; - result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Regex)); + result = patternsel(fcinfo, Pattern_Type_Regex); result = 1.0 - result; PG_RETURN_FLOAT8(result); } @@ -778,9 +900,9 @@ regexnesel(PG_FUNCTION_ARGS) Datum icregexnesel(PG_FUNCTION_ARGS) { - float8 result; + double result; - result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Regex_IC)); + result = patternsel(fcinfo, Pattern_Type_Regex_IC); result = 1.0 - result; PG_RETURN_FLOAT8(result); } @@ -791,9 +913,9 @@ icregexnesel(PG_FUNCTION_ARGS) Datum nlikesel(PG_FUNCTION_ARGS) { - float8 result; + double result; - result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Like)); + result = patternsel(fcinfo, Pattern_Type_Like); result = 1.0 - result; PG_RETURN_FLOAT8(result); } @@ -804,9 +926,9 @@ nlikesel(PG_FUNCTION_ARGS) Datum icnlikesel(PG_FUNCTION_ARGS) { - float8 result; + double result; - result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Like_IC)); + result = patternsel(fcinfo, Pattern_Type_Like_IC); result = 1.0 - result; PG_RETURN_FLOAT8(result); } @@ -817,25 +939,21 @@ icnlikesel(PG_FUNCTION_ARGS) Datum eqjoinsel(PG_FUNCTION_ARGS) { + Query *root = (Query *) PG_GETARG_POINTER(0); #ifdef NOT_USED /* see neqjoinsel() before removing me! */ - Oid opid = PG_GETARG_OID(0); + Oid operator = PG_GETARG_OID(1); #endif - Oid relid1 = PG_GETARG_OID(1); - AttrNumber attno1 = PG_GETARG_INT16(2); - Oid relid2 = PG_GETARG_OID(3); - AttrNumber attno2 = PG_GETARG_INT16(4); - bool unknown1 = NONVALUE(relid1) || NONVALUE(attno1); - bool unknown2 = NONVALUE(relid2) || NONVALUE(attno2); + List *args = (List *) PG_GETARG_POINTER(2); + Var *var1; + Var *var2; double selec; - if (unknown1 && unknown2) + get_join_vars(args, &var1, &var2); + + if (var1 == NULL && var2 == NULL) selec = DEFAULT_EQ_SEL; else { - Oid typid1; - Oid typid2; - int32 typmod1; - int32 typmod2; HeapTuple statsTuple1 = NULL; HeapTuple statsTuple2 = NULL; Form_pg_statistic stats1 = NULL; @@ -843,44 +961,52 @@ eqjoinsel(PG_FUNCTION_ARGS) double nd1, nd2; - if (unknown1) + if (var1 == NULL) { - nd1 = 100.0; + nd1 = DEFAULT_NUM_DISTINCT; } else { - /* get info about the attribute */ - getattproperties(relid1, attno1, &typid1, &typmod1); - /* get stats for the attribute, if available */ - statsTuple1 = SearchSysCache(STATRELATT, - ObjectIdGetDatum(relid1), - Int16GetDatum(attno1), - 0, 0); - if (HeapTupleIsValid(statsTuple1)) - stats1 = (Form_pg_statistic) GETSTRUCT(statsTuple1); + Oid relid1 = getrelid(var1->varno, root->rtable); - nd1 = get_att_numdistinct(relid1, attno1, typid1, stats1); + if (relid1 == InvalidOid) + nd1 = DEFAULT_NUM_DISTINCT; + else + { + statsTuple1 = SearchSysCache(STATRELATT, + ObjectIdGetDatum(relid1), + Int16GetDatum(var1->varattno), + 0, 0); + if (HeapTupleIsValid(statsTuple1)) + stats1 = (Form_pg_statistic) GETSTRUCT(statsTuple1); + + nd1 = get_att_numdistinct(root, var1, stats1); + } } - if (unknown2) + if (var2 == NULL) { - nd2 = 100.0; + nd2 = DEFAULT_NUM_DISTINCT; } else { - /* get info about the attribute */ - getattproperties(relid2, attno2, &typid2, &typmod2); - /* get stats for the attribute, if available */ - statsTuple2 = SearchSysCache(STATRELATT, - ObjectIdGetDatum(relid2), - Int16GetDatum(attno2), - 0, 0); - if (HeapTupleIsValid(statsTuple2)) - stats2 = (Form_pg_statistic) GETSTRUCT(statsTuple2); + Oid relid2 = getrelid(var2->varno, root->rtable); - nd2 = get_att_numdistinct(relid2, attno2, typid2, stats2); + if (relid2 == InvalidOid) + nd2 = DEFAULT_NUM_DISTINCT; + else + { + statsTuple2 = SearchSysCache(STATRELATT, + ObjectIdGetDatum(relid2), + Int16GetDatum(var2->varattno), + 0, 0); + if (HeapTupleIsValid(statsTuple2)) + stats2 = (Form_pg_statistic) GETSTRUCT(statsTuple2); + + nd2 = get_att_numdistinct(root, var2, stats2); + } } /* @@ -903,7 +1029,6 @@ eqjoinsel(PG_FUNCTION_ARGS) ReleaseSysCache(statsTuple1); if (HeapTupleIsValid(statsTuple2)) ReleaseSysCache(statsTuple2); - } PG_RETURN_FLOAT8((float8) selec); } @@ -1062,27 +1187,26 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, { switch (valuetypid) { - - /* - * Built-in numeric types - */ - case BOOLOID: - case INT2OID: - case INT4OID: - case INT8OID: - case FLOAT4OID: - case FLOAT8OID: - case NUMERICOID: - case OIDOID: - case REGPROCOID: + /* + * Built-in numeric types + */ + case BOOLOID: + case INT2OID: + case INT4OID: + case INT8OID: + case FLOAT4OID: + case FLOAT8OID: + case NUMERICOID: + case OIDOID: + case REGPROCOID: *scaledvalue = convert_numeric_to_scalar(value, valuetypid); *scaledlobound = convert_numeric_to_scalar(lobound, boundstypid); *scaledhibound = convert_numeric_to_scalar(hibound, boundstypid); return true; - /* - * Built-in string types - */ + /* + * Built-in string types + */ case CHAROID: case BPCHAROID: case VARCHAROID: @@ -1102,9 +1226,9 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, return true; } - /* - * Built-in time types - */ + /* + * Built-in time types + */ case TIMESTAMPOID: case ABSTIMEOID: case DATEOID: @@ -1376,7 +1500,7 @@ convert_timevalue_to_scalar(Datum value, Oid typid) { switch (typid) { - case TIMESTAMPOID: + case TIMESTAMPOID: return DatumGetTimestamp(value); case ABSTIMEOID: return DatumGetTimestamp(DirectFunctionCall1(abstime_timestamp, @@ -1419,51 +1543,17 @@ convert_timevalue_to_scalar(Datum value, Oid typid) } -/* - * getattproperties - * Retrieve pg_attribute properties for an attribute, - * including type OID and typmod. - */ -static void -getattproperties(Oid relid, AttrNumber attnum, - Oid *typid, int32 *typmod) -{ - HeapTuple atp; - Form_pg_attribute att_tup; - - atp = SearchSysCache(ATTNUM, - ObjectIdGetDatum(relid), - Int16GetDatum(attnum), - 0, 0); - if (!HeapTupleIsValid(atp)) - elog(ERROR, "getattproperties: no attribute tuple %u %d", - relid, (int) attnum); - att_tup = (Form_pg_attribute) GETSTRUCT(atp); - - *typid = att_tup->atttypid; - *typmod = att_tup->atttypmod; - - ReleaseSysCache(atp); -} - /* * get_att_numdistinct - * * Estimate the number of distinct values of an attribute. * - * relid, attnum: identify the attribute to examine. - * typid: type of attribute. + * var: identifies the attribute to examine. * stats: pg_statistic tuple for attribute, or NULL if not available. - * - * XXX possible future improvement: look to see if there is a unique - * index on the attribute. If so, we can estimate ndistinct = ntuples. - * This should probably override any info from pg_statistic. */ static double -get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid, - Form_pg_statistic stats) +get_att_numdistinct(Query *root, Var *var, Form_pg_statistic stats) { - HeapTuple reltup; + RelOptInfo *rel; double ntuples; /* @@ -1471,42 +1561,42 @@ get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid, * * Are there any other cases we should wire in special estimates for? */ - if (typid == BOOLOID) + if (var->vartype == BOOLOID) return 2.0; - /* - * If VACUUM ANALYZE determined a fixed estimate, use it. - */ - if (stats && stats->stadistinct > 0.0) - return stats->stadistinct; - /* * Otherwise we need to get the relation size. */ - reltup = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); - if (!HeapTupleIsValid(reltup)) - elog(ERROR, "get_att_numdistinct: no relation tuple %u", relid); - - ntuples = ((Form_pg_class) GETSTRUCT(reltup))->reltuples; - - ReleaseSysCache(reltup); + rel = find_base_rel(root, var->varno); + ntuples = rel->tuples; if (ntuples <= 0.0) - return 100.0; /* no data available; return a default */ + return DEFAULT_NUM_DISTINCT; /* no data available; return a default */ /* - * If VACUUM ANALYZE determined a scaled estimate, use it. + * Look to see if there is a unique index on the attribute. + * If so, we assume it's distinct, ignoring pg_statistic info + * which could be out of date. */ - if (stats && stats->stadistinct < 0.0) - return - stats->stadistinct * ntuples; + if (has_unique_index(rel, var->varattno)) + return ntuples; /* - * VACUUM ANALYZE does not compute stats for system attributes, + * If ANALYZE determined a fixed or scaled estimate, use it. + */ + if (stats) + { + if (stats->stadistinct > 0.0) + return stats->stadistinct; + if (stats->stadistinct < 0.0) + return - stats->stadistinct * ntuples; + } + + /* + * ANALYZE does not compute stats for system attributes, * but some of them can reasonably be assumed unique anyway. */ - switch (attnum) + switch (var->varattno) { case ObjectIdAttributeNumber: case SelfItemPointerAttributeNumber: @@ -1516,12 +1606,116 @@ get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid, } /* - * Estimate ndistinct = ntuples if the table is small, else 100. + * Estimate ndistinct = ntuples if the table is small, else use default. */ - if (ntuples < 100.0) + if (ntuples < DEFAULT_NUM_DISTINCT) return ntuples; - return 100.0; + return DEFAULT_NUM_DISTINCT; +} + +/* + * get_restriction_var + * Examine the args of a restriction clause to see if it's of the + * form (var op something) or (something op var). If so, extract + * and return the var and the other argument. + * + * Inputs: + * args: clause argument list + * varRelid: see specs for restriction selectivity functions + * + * Outputs: (these are set only if TRUE is returned) + * *var: gets Var node + * *other: gets other clause argument + * *varonleft: set TRUE if var is on the left, FALSE if on the right + * + * Returns TRUE if a Var is identified, otherwise FALSE. + */ +static bool +get_restriction_var(List *args, + int varRelid, + Var **var, + Node **other, + bool *varonleft) +{ + Node *left, + *right; + + if (length(args) != 2) + return false; + + left = (Node *) lfirst(args); + right = (Node *) lsecond(args); + + /* Ignore any binary-compatible relabeling */ + + if (IsA(left, RelabelType)) + left = ((RelabelType *) left)->arg; + if (IsA(right, RelabelType)) + right = ((RelabelType *) right)->arg; + + /* Look for the var */ + + if (IsA(left, Var) && + (varRelid == 0 || varRelid == ((Var *) left)->varno)) + { + *var = (Var *) left; + *other = right; + *varonleft = true; + } + else if (IsA(right, Var) && + (varRelid == 0 || varRelid == ((Var *) right)->varno)) + { + *var = (Var *) right; + *other = left; + *varonleft = false; + } + else + { + /* Duh, it's too complicated for me... */ + return false; + } + + return true; +} + +/* + * get_join_vars + * + * Extract the two Vars from a join clause's argument list. Returns + * NULL for arguments that are not simple vars. + */ +static void +get_join_vars(List *args, Var **var1, Var **var2) +{ + Node *left, + *right; + + if (length(args) != 2) + { + *var1 = NULL; + *var2 = NULL; + return; + } + + left = (Node *) lfirst(args); + right = (Node *) lsecond(args); + + /* Ignore any binary-compatible relabeling */ + if (IsA(left, RelabelType)) + left = ((RelabelType *) left)->arg; + if (IsA(right, RelabelType)) + right = ((RelabelType *) right)->arg; + + if (IsA(left, Var)) + *var1 = (Var *) left; + else + *var1 = NULL; + + if (IsA(right, Var)) + *var2 = (Var *) right; + else + *var2 = NULL; } /*------------------------------------------------------------------------- @@ -1755,54 +1949,49 @@ pattern_fixed_prefix(char *patt, Pattern_Type ptype, * more useful to use the upper-bound code than not. */ static Selectivity -prefix_selectivity(char *prefix, - Oid relid, - AttrNumber attno, - Oid datatype) +prefix_selectivity(Query *root, Var *var, char *prefix) { Selectivity prefixsel; Oid cmpopr; - Datum prefixcon; + Const *prefixcon; + List *cmpargs; char *greaterstr; - cmpopr = find_operator(">=", datatype); + cmpopr = find_operator(">=", var->vartype); if (cmpopr == InvalidOid) elog(ERROR, "prefix_selectivity: no >= operator for type %u", - datatype); - prefixcon = string_to_datum(prefix, datatype); + var->vartype); + prefixcon = string_to_const(prefix, var->vartype); + cmpargs = makeList2(var, prefixcon); /* Assume scalargtsel is appropriate for all supported types */ - prefixsel = DatumGetFloat8(DirectFunctionCall5(scalargtsel, - ObjectIdGetDatum(cmpopr), - ObjectIdGetDatum(relid), - Int16GetDatum(attno), - prefixcon, - Int32GetDatum(SEL_CONSTANT | SEL_RIGHT))); - pfree(DatumGetPointer(prefixcon)); + prefixsel = DatumGetFloat8(DirectFunctionCall4(scalargtsel, + PointerGetDatum(root), + ObjectIdGetDatum(cmpopr), + PointerGetDatum(cmpargs), + Int32GetDatum(0))); /*------- * If we can create a string larger than the prefix, say * "x < greaterstr". *------- */ - greaterstr = make_greater_string(prefix, datatype); + greaterstr = make_greater_string(prefix, var->vartype); if (greaterstr) { Selectivity topsel; - cmpopr = find_operator("<", datatype); + cmpopr = find_operator("<", var->vartype); if (cmpopr == InvalidOid) elog(ERROR, "prefix_selectivity: no < operator for type %u", - datatype); - prefixcon = string_to_datum(greaterstr, datatype); + var->vartype); + prefixcon = string_to_const(greaterstr, var->vartype); + cmpargs = makeList2(var, prefixcon); /* Assume scalarltsel is appropriate for all supported types */ - topsel = DatumGetFloat8(DirectFunctionCall5(scalarltsel, - ObjectIdGetDatum(cmpopr), - ObjectIdGetDatum(relid), - Int16GetDatum(attno), - prefixcon, - Int32GetDatum(SEL_CONSTANT | SEL_RIGHT))); - pfree(DatumGetPointer(prefixcon)); - pfree(greaterstr); + topsel = DatumGetFloat8(DirectFunctionCall4(scalarltsel, + PointerGetDatum(root), + ObjectIdGetDatum(cmpopr), + PointerGetDatum(cmpargs), + Int32GetDatum(0))); /* * Merge the two selectivities in the same way as for a range @@ -1828,7 +2017,7 @@ prefix_selectivity(char *prefix, * No data available --- use a default estimate that is * small, but not real small. */ - prefixsel = 0.01; + prefixsel = 0.005; } else { @@ -2074,7 +2263,7 @@ locale_is_like_safe(void) result = false; return (bool) result; #else /* not USE_LOCALE */ - return true; /* We must be in C locale, which is OK */ + return true; /* We must be in C locale, which is OK */ #endif /* USE_LOCALE */ } @@ -2208,7 +2397,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... @@ -2219,6 +2407,18 @@ string_to_datum(const char *str, Oid datatype) return DirectFunctionCall1(textin, CStringGetDatum(str)); } +/* + * Generate a Const node of the appropriate type from a C string. + */ +static Const * +string_to_const(const char *str, Oid datatype) +{ + Datum conval = string_to_datum(str, datatype); + + return makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1), + conval, false, false, false, false); +} + /*------------------------------------------------------------------------- * * Index cost estimation functions diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 44ddb7611a5..e04f97e0ec8 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.78 2001/05/15 03:49:35 momjian Exp $ + * $Id: catversion.h,v 1.79 2001/05/20 20:28:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200105145 +#define CATALOG_VERSION_NO 200105191 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 7d7acf96f73..f905a063c63 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.185 2001/05/09 23:13:35 tgl Exp $ + * $Id: pg_proc.h,v 1.186 2001/05/20 20:28:19 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -219,21 +219,21 @@ DESCR("btree cost estimator"); DATA(insert OID = 100 ( int8fac PGUID 12 f t t t 1 f 20 "20" 100 0 0 100 int8fac - )); DESCR("factorial"); -DATA(insert OID = 101 ( eqsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 eqsel - )); +DATA(insert OID = 101 ( eqsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 eqsel - )); DESCR("restriction selectivity of = and related operators"); -DATA(insert OID = 102 ( neqsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 neqsel - )); +DATA(insert OID = 102 ( neqsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 neqsel - )); DESCR("restriction selectivity of <> and related operators"); -DATA(insert OID = 103 ( scalarltsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 scalarltsel - )); +DATA(insert OID = 103 ( scalarltsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 scalarltsel - )); DESCR("restriction selectivity of < and related operators on scalar datatypes"); -DATA(insert OID = 104 ( scalargtsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 scalargtsel - )); +DATA(insert OID = 104 ( scalargtsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 scalargtsel - )); DESCR("restriction selectivity of > and related operators on scalar datatypes"); -DATA(insert OID = 105 ( eqjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 eqjoinsel - )); +DATA(insert OID = 105 ( eqjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 eqjoinsel - )); DESCR("join selectivity of = and related operators"); -DATA(insert OID = 106 ( neqjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 neqjoinsel - )); +DATA(insert OID = 106 ( neqjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 neqjoinsel - )); DESCR("join selectivity of <> and related operators"); -DATA(insert OID = 107 ( scalarltjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 scalarltjoinsel - )); +DATA(insert OID = 107 ( scalarltjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 scalarltjoinsel - )); DESCR("join selectivity of < and related operators on scalar datatypes"); -DATA(insert OID = 108 ( scalargtjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 scalargtjoinsel - )); +DATA(insert OID = 108 ( scalargtjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 scalargtjoinsel - )); DESCR("join selectivity of > and related operators on scalar datatypes"); DATA(insert OID = 112 ( text PGUID 12 f t t t 1 f 25 "23" 100 0 0 100 int4_text - )); @@ -292,9 +292,9 @@ DATA(insert OID = 137 ( on_ppath PGUID 12 f t t t 2 f 16 "600 602" 100 0 0 DESCR("contained in"); DATA(insert OID = 138 ( box_center PGUID 12 f t t t 1 f 600 "603" 100 0 0 100 box_center - )); DESCR("center of"); -DATA(insert OID = 139 ( areasel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 areasel - )); +DATA(insert OID = 139 ( areasel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 areasel - )); DESCR("restriction selectivity for area-comparison operators"); -DATA(insert OID = 140 ( areajoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 areajoinsel - )); +DATA(insert OID = 140 ( areajoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 areajoinsel - )); DESCR("join selectivity for area-comparison operators"); DATA(insert OID = 141 ( int4mul PGUID 12 f t t t 2 f 23 "23 23" 100 0 0 100 int4mul - )); DESCR("multiply"); @@ -1562,13 +1562,13 @@ DESCR("current transaction time"); /* OIDS 1300 - 1399 */ -DATA(insert OID = 1300 ( positionsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 positionsel - )); +DATA(insert OID = 1300 ( positionsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 positionsel - )); DESCR("restriction selectivity for position-comparison operators"); -DATA(insert OID = 1301 ( positionjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 positionjoinsel - )); +DATA(insert OID = 1301 ( positionjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 positionjoinsel - )); DESCR("join selectivity for position-comparison operators"); -DATA(insert OID = 1302 ( contsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 contsel - )); +DATA(insert OID = 1302 ( contsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 contsel - )); DESCR("restriction selectivity for containment comparison operators"); -DATA(insert OID = 1303 ( contjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 contjoinsel - )); +DATA(insert OID = 1303 ( contjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 contjoinsel - )); DESCR("join selectivity for containment comparison operators"); DATA(insert OID = 1304 ( overlaps PGUID 12 f t t f 4 f 16 "1184 1184 1184 1184" 100 0 0 100 overlaps_timestamp - )); @@ -2465,37 +2465,37 @@ DATA(insert OID = 1799 ( oidout PGUID 12 f t t t 1 f 23 "0" 100 0 0 100 oid DESCR("(internal)"); /* Selectivity estimators for LIKE and related operators */ -DATA(insert OID = 1814 ( iclikesel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 iclikesel - )); +DATA(insert OID = 1814 ( iclikesel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 iclikesel - )); DESCR("restriction selectivity of ILIKE"); -DATA(insert OID = 1815 ( icnlikesel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 icnlikesel - )); +DATA(insert OID = 1815 ( icnlikesel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 icnlikesel - )); DESCR("restriction selectivity of NOT ILIKE"); -DATA(insert OID = 1816 ( iclikejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 iclikejoinsel - )); +DATA(insert OID = 1816 ( iclikejoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 iclikejoinsel - )); DESCR("join selectivity of ILIKE"); -DATA(insert OID = 1817 ( icnlikejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 icnlikejoinsel - )); +DATA(insert OID = 1817 ( icnlikejoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 icnlikejoinsel - )); DESCR("join selectivity of NOT ILIKE"); -DATA(insert OID = 1818 ( regexeqsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 regexeqsel - )); +DATA(insert OID = 1818 ( regexeqsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 regexeqsel - )); DESCR("restriction selectivity of regex match"); -DATA(insert OID = 1819 ( likesel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 likesel - )); +DATA(insert OID = 1819 ( likesel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 likesel - )); DESCR("restriction selectivity of LIKE"); -DATA(insert OID = 1820 ( icregexeqsel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 icregexeqsel - )); +DATA(insert OID = 1820 ( icregexeqsel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 icregexeqsel - )); DESCR("restriction selectivity of case-insensitive regex match"); -DATA(insert OID = 1821 ( regexnesel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 regexnesel - )); +DATA(insert OID = 1821 ( regexnesel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 regexnesel - )); DESCR("restriction selectivity of regex non-match"); -DATA(insert OID = 1822 ( nlikesel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 nlikesel - )); +DATA(insert OID = 1822 ( nlikesel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 nlikesel - )); DESCR("restriction selectivity of NOT LIKE"); -DATA(insert OID = 1823 ( icregexnesel PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100 icregexnesel - )); +DATA(insert OID = 1823 ( icregexnesel PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100 icregexnesel - )); DESCR("restriction selectivity of case-insensitive regex non-match"); -DATA(insert OID = 1824 ( regexeqjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 regexeqjoinsel - )); +DATA(insert OID = 1824 ( regexeqjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 regexeqjoinsel - )); DESCR("join selectivity of regex match"); -DATA(insert OID = 1825 ( likejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 likejoinsel - )); +DATA(insert OID = 1825 ( likejoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 likejoinsel - )); DESCR("join selectivity of LIKE"); -DATA(insert OID = 1826 ( icregexeqjoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 icregexeqjoinsel - )); +DATA(insert OID = 1826 ( icregexeqjoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 icregexeqjoinsel - )); DESCR("join selectivity of case-insensitive regex match"); -DATA(insert OID = 1827 ( regexnejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 regexnejoinsel - )); +DATA(insert OID = 1827 ( regexnejoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 regexnejoinsel - )); DESCR("join selectivity of regex non-match"); -DATA(insert OID = 1828 ( nlikejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 nlikejoinsel - )); +DATA(insert OID = 1828 ( nlikejoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 nlikejoinsel - )); DESCR("join selectivity of NOT LIKE"); -DATA(insert OID = 1829 ( icregexnejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 icregexnejoinsel - )); +DATA(insert OID = 1829 ( icregexnejoinsel PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100 icregexnejoinsel - )); DESCR("join selectivity of case-insensitive regex non-match"); /* Aggregate-related functions */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 63b1b1046a8..cfea5222965 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.127 2001/05/07 00:43:25 tgl Exp $ + * $Id: parsenodes.h,v 1.128 2001/05/20 20:28:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -83,6 +83,7 @@ typedef struct Query /* internal to planner */ List *base_rel_list; /* list of base-relation RelOptInfos */ + List *other_rel_list; /* list of other 1-relation RelOptInfos */ List *join_rel_list; /* list of join-relation RelOptInfos */ List *equi_key_list; /* list of lists of equijoined * PathKeyItems */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index c76d9b4af71..33927edc18d 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: relation.h,v 1.55 2001/05/07 00:43:26 tgl Exp $ + * $Id: relation.h,v 1.56 2001/05/20 20:28:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -72,8 +72,8 @@ typedef enum CostSelector * * If the relation is a base relation it will have these fields set: * * issubquery - true if baserel is a subquery RTE rather than a table - * indexed - true if the relation has secondary indices (always false - * if it's a subquery) + * indexlist - list of IndexOptInfo nodes for relation's indexes + * (always NIL if it's a subquery) * pages - number of disk pages in relation (zero if a subquery) * tuples - number of tuples in relation (not considering restrictions) * subplan - plan for subquery (NULL if it's a plain table) @@ -150,7 +150,7 @@ typedef struct RelOptInfo /* information about a base rel (not set for join rels!) */ bool issubquery; - bool indexed; + List *indexlist; long pages; double tuples; struct Plan *subplan; @@ -178,20 +178,30 @@ typedef struct RelOptInfo * and indexes, but that created confusion without actually doing anything * useful. So now we have a separate IndexOptInfo struct for indexes. * - * indexoid - OID of the index relation itself - * pages - number of disk pages in index - * tuples - number of index tuples in index + * indexoid - OID of the index relation itself + * pages - number of disk pages in index + * tuples - number of index tuples in index + * ncolumns - number of columns in index + * nkeys - number of keys used by index (input columns) * classlist - List of PG_AMOPCLASS OIDs for the index * indexkeys - List of base-relation attribute numbers that are index keys - * ordering - List of PG_OPERATOR OIDs which order the indexscan result - * relam - the OID of the pg_am of the index + * ordering - List of PG_OPERATOR OIDs which order the indexscan result + * relam - the OID of the pg_am of the index * amcostestimate - OID of the relam's cost estimator * indproc - OID of the function if a functional index, else 0 * indpred - index predicate if a partial index, else NULL + * unique - true if index is unique * lossy - true if index is lossy (may return non-matching tuples) * - * NB. the last element of the arrays classlist, indexkeys and ordering - * is always 0. + * ncolumns and nkeys are the same except for a functional index, + * wherein ncolumns is 1 (the single function output) while nkeys + * is the number of table columns passed to the function. classlist[] + * and ordering[] have ncolumns entries, while indexkeys[] has nkeys + * entries. + * + * Note: for historical reasons, the arrays classlist, indexkeys and + * ordering have an extra entry that is always zero. Some code scans + * until it sees a zero rather than looking at ncolumns or nkeys. */ typedef struct IndexOptInfo @@ -205,15 +215,18 @@ typedef struct IndexOptInfo double tuples; /* index descriptor information */ - Oid *classlist; /* classes of AM operators */ - int *indexkeys; /* keys over which we're indexing */ - Oid *ordering; /* OIDs of sort operators for each key */ + int ncolumns; /* number of columns in index */ + int nkeys; /* number of keys used by index */ + Oid *classlist; /* AM operator classes for columns */ + int *indexkeys; /* column numbers of index's keys */ + Oid *ordering; /* OIDs of sort operators for each column */ Oid relam; /* OID of the access method (in pg_am) */ RegProcedure amcostestimate;/* OID of the access method's cost fcn */ Oid indproc; /* if a functional index */ List *indpred; /* if a partial index */ + bool unique; /* if a unique index */ bool lossy; /* if a lossy index */ } IndexOptInfo; @@ -275,7 +288,7 @@ typedef struct Path * tuples matched during any scan. (The executor is smart enough not to return * the same tuple more than once, even if it is matched in multiple scans.) * - * 'indexid' is a list of index relation OIDs, one per scan to be performed. + * 'indexinfo' is a list of IndexOptInfo nodes, one per scan to be performed. * * 'indexqual' is a list of index qualifications, also one per scan. * Each entry in 'indexqual' is a sublist of qualification expressions with @@ -313,7 +326,7 @@ typedef struct Path typedef struct IndexPath { Path path; - List *indexid; + List *indexinfo; List *indexqual; ScanDirection indexscandir; Relids joinrelids; /* other rels mentioned in indexqual */ @@ -533,7 +546,7 @@ typedef struct RestrictInfo typedef struct JoinInfo { NodeTag type; - Relids unjoined_relids;/* some rels not yet part of my RelOptInfo */ + Relids unjoined_relids; /* some rels not yet part of my RelOptInfo */ List *jinfo_restrictinfo; /* relevant RestrictInfos */ } JoinInfo; diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index a02ef9c77c3..79bb7a13593 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.43 2001/03/22 04:00:53 momjian Exp $ + * $Id: clauses.h,v 1.44 2001/05/20 20:28:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,14 +16,6 @@ #include "nodes/relation.h" -/* - * Flag bits returned by get_relattval(). - * These are used in selectivity-estimation routines, too. - */ -#define SEL_CONSTANT 1 /* operator's non-var arg is a constant */ -#define SEL_RIGHT 2 /* operator's non-var arg is on the right */ - - extern Expr *make_clause(int type, Node *oper, List *args); extern bool is_opclause(Node *clause); @@ -61,11 +53,6 @@ extern List *pull_constant_clauses(List *quals, List **constantQual); extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars); extern int NumRelids(Node *clause); -extern void get_relattval(Node *clause, int targetrelid, - int *relid, AttrNumber *attno, - Datum *constval, int *flag); -extern void get_rels_atts(Node *clause, int *relid1, - AttrNumber *attno1, int *relid2, AttrNumber *attno2); extern void CommuteClause(Expr *clause); extern Node *eval_const_expressions(Node *node); diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 0839feb4b2f..85ba6936f48 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pathnode.h,v 1.36 2001/05/07 00:43:26 tgl Exp $ + * $Id: pathnode.h,v 1.37 2001/05/20 20:28:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -64,9 +64,10 @@ extern HashPath *create_hashjoin_path(RelOptInfo *joinrel, /* * prototypes for relnode.c */ -extern RelOptInfo *get_base_rel(Query *root, int relid); -extern RelOptInfo *make_base_rel(Query *root, int relid); -extern RelOptInfo *get_join_rel(Query *root, +extern RelOptInfo *build_base_rel(Query *root, int relid); +extern RelOptInfo *build_other_rel(Query *root, int relid); +extern RelOptInfo *find_base_rel(Query *root, int relid); +extern RelOptInfo *build_join_rel(Query *root, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinType jointype, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index ac67f6a6551..39afe74d2ad 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: paths.h,v 1.52 2001/03/22 04:00:54 momjian Exp $ + * $Id: paths.h,v 1.53 2001/05/20 20:28:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,7 +35,7 @@ extern RelOptInfo *make_fromexpr_rel(Query *root, FromExpr *from); * indxpath.c * routines to generate index paths */ -extern void create_index_paths(Query *root, RelOptInfo *rel, List *indices); +extern void create_index_paths(Query *root, RelOptInfo *rel); extern Oid indexable_operator(Expr *clause, Oid opclass, Oid relam, bool indexkey_on_left); extern List *extract_or_indexqual_conditions(RelOptInfo *rel, diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index 5d3062d981c..a103f51e3f1 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: plancat.h,v 1.22 2001/03/22 04:00:55 momjian Exp $ + * $Id: plancat.h,v 1.23 2001/05/20 20:28:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ #include "nodes/relation.h" -extern void relation_info(Oid relationObjectId, +extern void get_relation_info(Oid relationObjectId, bool *hasindex, long *pages, double *tuples); extern List *find_secondary_indexes(Oid relationObjectId); @@ -26,15 +26,15 @@ extern List *find_inheritance_children(Oid inhparent); extern bool has_subclass(Oid relationId); -extern Selectivity restriction_selectivity(Oid functionObjectId, - Oid operatorObjectId, - Oid relationObjectId, - AttrNumber attributeNumber, - Datum constValue, - int constFlag); +extern bool has_unique_index(RelOptInfo *rel, AttrNumber attno); -extern Selectivity join_selectivity(Oid functionObjectId, Oid operatorObjectId, - Oid relationObjectId1, AttrNumber attributeNumber1, - Oid relationObjectId2, AttrNumber attributeNumber2); +extern Selectivity restriction_selectivity(Query *root, + Oid operator, + List *args, + int varRelid); + +extern Selectivity join_selectivity(Query *root, + Oid operator, + List *args); #endif /* PLANCAT_H */ diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index b1ca99fed52..1888248ed53 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: prep.h,v 1.27 2001/03/22 04:00:55 momjian Exp $ + * $Id: prep.h,v 1.28 2001/05/20 20:28:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,7 +35,9 @@ extern List *preprocess_targetlist(List *tlist, int command_type, extern Plan *plan_set_operations(Query *parse); extern List *find_all_inheritors(Oid parentrel); -extern List *expand_inherted_rtentry(Query *parse, Index rti); + +extern List *expand_inherted_rtentry(Query *parse, Index rti, + bool dup_parent); extern Node *adjust_inherited_attrs(Node *node, Index old_rt_index, Oid old_relid, diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 33deafdd94a..cc71f44d1d7 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -418,16 +418,15 @@ WHERE p1.oprcode = p2.oid AND -- If oprrest is set, the operator must return boolean, -- and it must link to a proc with the right signature -- to be a restriction selectivity estimator. --- The proc signature we want is: float8 proc(oid, oid, int2, , int4) +-- The proc signature we want is: float8 proc(opaque, oid, opaque, int4) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprrest = p2.oid AND (p1.oprresult != 16 OR p2.prorettype != 701 OR p2.proretset OR - p2.pronargs != 5 OR - p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR - p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 0 OR - p2.proargtypes[4] != 23); + p2.pronargs != 4 OR + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR + p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23); oid | oprname | oid | proname -----+---------+-----+--------- (0 rows) @@ -435,16 +434,15 @@ WHERE p1.oprrest = p2.oid AND -- If oprjoin is set, the operator must be a binary boolean op, -- and it must link to a proc with the right signature -- to be a join selectivity estimator. --- The proc signature we want is: float8 proc(oid, oid, int2, oid, int2) +-- The proc signature we want is: float8 proc(opaque, oid, opaque) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprjoin = p2.oid AND (p1.oprkind != 'b' OR p1.oprresult != 16 OR p2.prorettype != 701 OR p2.proretset OR - p2.pronargs != 5 OR - p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR - p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 26 OR - p2.proargtypes[4] != 21); + p2.pronargs != 3 OR + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR + p2.proargtypes[2] != 0); oid | oprname | oid | proname -----+---------+-----+--------- (0 rows) diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index a79d0eae754..1cb5cd18c92 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -349,32 +349,30 @@ WHERE p1.oprcode = p2.oid AND -- If oprrest is set, the operator must return boolean, -- and it must link to a proc with the right signature -- to be a restriction selectivity estimator. --- The proc signature we want is: float8 proc(oid, oid, int2, , int4) +-- The proc signature we want is: float8 proc(opaque, oid, opaque, int4) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprrest = p2.oid AND (p1.oprresult != 16 OR p2.prorettype != 701 OR p2.proretset OR - p2.pronargs != 5 OR - p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR - p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 0 OR - p2.proargtypes[4] != 23); + p2.pronargs != 4 OR + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR + p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23); -- If oprjoin is set, the operator must be a binary boolean op, -- and it must link to a proc with the right signature -- to be a join selectivity estimator. --- The proc signature we want is: float8 proc(oid, oid, int2, oid, int2) +-- The proc signature we want is: float8 proc(opaque, oid, opaque) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprjoin = p2.oid AND (p1.oprkind != 'b' OR p1.oprresult != 16 OR p2.prorettype != 701 OR p2.proretset OR - p2.pronargs != 5 OR - p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR - p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 26 OR - p2.proargtypes[4] != 21); + p2.pronargs != 3 OR + p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR + p2.proargtypes[2] != 0); -- **************** pg_aggregate ****************