*
*
* 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.
/* ----------------------------------------------------------------
* 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
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);
}
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);
}
/* 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)
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
_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;
/*
* 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);
/*
* 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;
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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;
/*
* 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;
}
* 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
}
/*
- * 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.
*/
static void
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);
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;
*
*
* 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
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
* ----------------
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 */
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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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));
/* 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
{
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);
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 */
* 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;
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;
/*
* 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,
*
*
* 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"
#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.
} 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);
* match what clause_selectivity() would do in the cases it
* handles.
*/
- if (varRelid != 0 || NumRelids(clause) == 1)
+ if (is_opclause(clause) &&
+ (varRelid != 0 || NumRelids(clause) == 1))
{
- int relidx;
- AttrNumber attno;
- Datum constval;
- int flag;
-
- get_relattval(clause, varRelid,
- &relidx, &attno, &constval, &flag);
- if (relidx != 0)
+ Expr *expr = (Expr *) clause;
+
+ if (length(expr->args) == 2)
{
- /* if get_relattval succeeded, it must be an opclause */
- Var *other;
+ bool varonleft = true;
- other = (flag & SEL_RIGHT) ? get_rightop((Expr *) clause) :
- get_leftop((Expr *) clause);
- if (is_pseudo_constant_clause((Node *) other))
+ if (is_pseudo_constant_clause(lsecond(expr->args)) ||
+ (varonleft = false,
+ is_pseudo_constant_clause(lfirst(expr->args))))
{
- Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
+ Oid opno = ((Oper *) expr->oper)->opno;
RegProcedure oprrest = get_oprrest(opno);
- if (!oprrest)
- s2 = (Selectivity) 0.5;
- else
- s2 = restriction_selectivity(oprrest, opno,
- getrelid(relidx,
- root->rtable),
- attno,
- constval, flag);
+ s2 = restriction_selectivity(root, opno,
+ expr->args, varRelid);
/*
* If we reach here, we have computed the same result
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 */
* No data available --- use a default estimate that
* is small, but not real small.
*/
- s2 = 0.01;
+ s2 = 0.005;
}
else
{
*/
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 */
* 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);
}
}
}
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))
/*
* Just for the moment! FIX ME! - vadim 02/04/98
*/
- s1 = 1.0;
+ s1 = (Selectivity) 0.5;
}
else if (IsA(clause, RelabelType))
{
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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;
/*
* 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 ... */
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...
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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.
*/
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- return get_base_rel(root, varno);
+ return build_base_rel(root, varno);
}
else if (IsA(jtnode, FromExpr))
{
* 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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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
* '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;
{
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;
* '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
*/
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 */
{
List *ilist;
/* if we don't match anything, return zeros */
+ *retIndexInfo = NULL;
*retIndexQual = NIL;
- *retIndexid = InvalidOid;
*retStartupCost = 0;
*retTotalCost = 0;
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
-#include "catalog/pg_index.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#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"
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);
* 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.
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);
baserelid = lfirsti(best_path->path.parent->relids);
- /* check to see if any of the indices are lossy */
- foreach(ixid, best_path->indexid)
+ /*
+ * Build list of index OIDs, and check to see if any of the indices
+ * are lossy.
+ */
+ indexids = NIL;
+ lossy = false;
+ foreach(ixinfo, best_path->indexinfo)
{
- HeapTuple indexTuple;
- Form_pg_index index;
-
- indexTuple = SearchSysCache(INDEXRELID,
- ObjectIdGetDatum(lfirsti(ixid)),
- 0, 0, 0);
- if (!HeapTupleIsValid(indexTuple))
- elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
- index = (Form_pg_index) GETSTRUCT(indexTuple);
- if (index->indislossy)
- {
- lossy = true;
- ReleaseSysCache(indexTuple);
- break;
- }
- ReleaseSysCache(indexTuple);
+ IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
+
+ indexids = lappendi(indexids, index->indexoid);
+ lossy |= index->lossy;
}
/*
scan_plan = make_indexscan(tlist,
qpqual,
baserelid,
- best_path->indexid,
+ indexids,
fixed_indxqual,
indxqual,
best_path->indexscandir);
{
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;
}
* 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;
foreach(i, indexqual)
{
Expr *clause = (Expr *) lfirst(i);
- int relid;
- AttrNumber attno;
- Datum constval;
- int flag;
Expr *newclause;
+ List *leftvarnos;
Oid opclass,
newopno;
- if (!is_opclause((Node *) clause) ||
- length(clause->args) != 2)
+ if (!is_opclause((Node *) clause) || length(clause->args) != 2)
elog(ERROR, "fix_indxqual_sublist: indexqual clause is not binary opclause");
/*
- * Which side is the indexkey on?
- *
- * get_relattval sets flag&SEL_RIGHT if the indexkey is on the LEFT.
- */
- get_relattval((Node *) clause, baserelid,
- &relid, &attno, &constval, &flag);
-
- /*
* Make a copy that will become the fixed clause.
*
* We used to try to do a shallow copy here, but that fails if there
*/
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
* 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;
}
static Node *
-fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
+fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index,
Oid *opclass)
{
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");
}
* 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);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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)
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);
}
{
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);
}
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
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))
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);
* 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);
* 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* construction.
*/
root->base_rel_list = NIL;
+ root->other_rel_list = NIL;
root->join_rel_list = NIL;
root->equi_key_list = NIL;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
* 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;
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
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;
/*
inhRTIs = lappendi(inhRTIs, childRTindex);
}
+
return inhRTIs;
}
*
*
* 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
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);
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
*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
/*
- * 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
* 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;
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);
}
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));
}
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);
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;
/*
* 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);
/*
* 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);
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;
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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,
/*
- * 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);
}
/*
+ * 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);
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;
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
{
}
/*
+ * 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 *
}
/*
- * 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.
*
* 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;
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;
*
*
* 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 <ctype.h>
#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"
#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,
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);
/*
* 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;
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))
{
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;
* 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;
* 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;
selec = numbers[nnumbers-1];
}
- free_attstatsslot(typid, values, nvalues, numbers, nnumbers);
+ free_attstatsslot(var->vartype, values, nvalues,
+ numbers, nnumbers);
}
else
{
* 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);
}
}
* 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... */
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
{
}
/*
- * 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;
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
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))
{
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);
}
/*
* 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))
ltcmp = DatumGetBool(FunctionCall2(&opproc,
values[0],
- value));
+ constval));
if (isgt)
ltcmp = !ltcmp;
if (!ltcmp)
{
ltcmp = DatumGetBool(FunctionCall2(&opproc,
values[i],
- value));
+ constval));
if (isgt)
ltcmp = !ltcmp;
if (!ltcmp)
* 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)
{
/*
* 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;
* 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);
}
/*
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;
+
+ /*
+ * 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));
- /* 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;
+ /* 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);
-
- /* 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)
- {
+ Selectivity prefixsel;
+ Selectivity restsel;
+ Selectivity selec;
- /*
- * 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
- {
+ 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;
+ }
- /*
- * 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 (prefix)
+ pfree(prefix);
+ pfree(patt);
- 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);
- }
- PG_RETURN_FLOAT8(result);
+ return result;
}
/*
Datum
regexeqsel(PG_FUNCTION_ARGS)
{
- return patternsel(fcinfo, Pattern_Type_Regex);
+ PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Regex));
}
/*
Datum
icregexeqsel(PG_FUNCTION_ARGS)
{
- return patternsel(fcinfo, Pattern_Type_Regex_IC);
+ PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Regex_IC));
}
/*
Datum
likesel(PG_FUNCTION_ARGS)
{
- return patternsel(fcinfo, Pattern_Type_Like);
+ PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Like));
}
/*
Datum
iclikesel(PG_FUNCTION_ARGS)
{
- return patternsel(fcinfo, Pattern_Type_Like_IC);
+ PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Like_IC));
}
/*
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);
}
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);
}
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);
}
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);
}
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;
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);
-
- nd1 = get_att_numdistinct(relid1, attno1, typid1, stats1);
+ Oid relid1 = getrelid(var1->varno, root->rtable);
+
+ 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);
-
- nd2 = get_att_numdistinct(relid2, attno2, typid2, stats2);
+ Oid relid2 = getrelid(var2->varno, root->rtable);
+
+ 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);
+ }
}
/*
ReleaseSysCache(statsTuple1);
if (HeapTupleIsValid(statsTuple2))
ReleaseSysCache(statsTuple2);
-
}
PG_RETURN_FLOAT8((float8) selec);
}
{
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:
return true;
}
- /*
- * Built-in time types
- */
+ /*
+ * Built-in time types
+ */
case TIMESTAMPOID:
case ABSTIMEOID:
case DATEOID:
{
switch (typid)
{
- case TIMESTAMPOID:
+ case TIMESTAMPOID:
return DatumGetTimestamp(value);
case ABSTIMEOID:
return DatumGetTimestamp(DirectFunctionCall1(abstime_timestamp,
/*
- * 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;
/*
*
* 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:
}
/*
- * 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;
}
/*-------------------------------------------------------------------------
* 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
* No data available --- use a default estimate that is
* small, but not real small.
*/
- prefixsel = 0.01;
+ prefixsel = 0.005;
}
else
{
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 */
}
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...
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
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200105145
+#define CATALOG_VERSION_NO 200105191
#endif
* 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
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 - ));
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");
/* 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 - ));
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
* * 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)
/* information about a base rel (not set for join rels!) */
bool issubquery;
- bool indexed;
+ List *indexlist;
long pages;
double tuples;
struct Plan *subplan;
* 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
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;
* 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
typedef struct IndexPath
{
Path path;
- List *indexid;
+ List *indexinfo;
List *indexqual;
ScanDirection indexscandir;
Relids joinrelids; /* other rels mentioned in indexqual */
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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,
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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,
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
-- 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, <any>, 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)
-- 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)
-- 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, <any>, 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 ****************