aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/indexcost.sgml36
-rw-r--r--src/backend/optimizer/path/costsize.c121
-rw-r--r--src/backend/optimizer/util/var.c57
-rw-r--r--src/backend/utils/adt/selfuncs.c137
-rw-r--r--src/backend/utils/cache/lsyscache.c32
-rw-r--r--src/include/catalog/pg_proc.h10
-rw-r--r--src/include/optimizer/var.h4
-rw-r--r--src/include/utils/lsyscache.h4
8 files changed, 315 insertions, 86 deletions
diff --git a/doc/src/sgml/indexcost.sgml b/doc/src/sgml/indexcost.sgml
index 9c781f97fc8..482a2e199e6 100644
--- a/doc/src/sgml/indexcost.sgml
+++ b/doc/src/sgml/indexcost.sgml
@@ -1,5 +1,5 @@
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.6 2000/12/22 21:51:57 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.7 2001/05/09 23:13:34 tgl Exp $
-->
<chapter id="indexcost">
@@ -57,7 +57,8 @@ amcostestimate (Query *root,
List *indexQuals,
Cost *indexStartupCost,
Cost *indexTotalCost,
- Selectivity *indexSelectivity);
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
</programlisting>
The first four parameters are inputs:
@@ -103,7 +104,7 @@ amcostestimate (Query *root,
</para>
<para>
- The last three parameters are pass-by-reference outputs:
+ The last four parameters are pass-by-reference outputs:
<variablelist>
<varlistentry>
@@ -132,6 +133,16 @@ amcostestimate (Query *root,
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>*indexCorrelation</term>
+ <listitem>
+ <para>
+ Set to correlation coefficient between index scan order and
+ underlying table's order
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
@@ -172,6 +183,13 @@ amcostestimate (Query *root,
tuples that actually pass the given qual conditions.
</para>
+ <para>
+ The indexCorrelation should be set to the correlation (ranging between
+ -1.0 and 1.0) between the index order and the table order. This is used
+ to adjust the estimate for the cost of fetching tuples from the main
+ table.
+ </para>
+
<procedure>
<title>Cost Estimation</title>
<para>
@@ -224,6 +242,14 @@ amcostestimate (Query *root,
</programlisting>
</para>
</step>
+
+ <step>
+ <para>
+ Estimate the index correlation. For a simple ordered index on a single
+ field, this can be retrieved from pg_statistic. If the correlation
+ is not known, the conservative estimate is zero (no correlation).
+ </para>
+ </step>
</procedure>
<para>
@@ -237,8 +263,8 @@ amcostestimate (Query *root,
<programlisting>
prorettype = 0
-pronargs = 7
-proargtypes = 0 0 0 0 0 0 0
+pronargs = 8
+proargtypes = 0 0 0 0 0 0 0 0
</programlisting>
We use zero ("opaque") for all the arguments since none of them have types
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 7dfe834b779..dddca240e95 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -31,17 +31,18 @@
* result by interpolating between startup_cost and total_cost. In detail:
* actual_cost = startup_cost +
* (total_cost - startup_cost) * tuples_to_fetch / path->parent->rows;
- * Note that a relation's rows count (and, by extension, a Plan's plan_rows)
- * are set without regard to any LIMIT, so that this equation works properly.
- * (Also, these routines guarantee not to set the rows count to zero, so there
- * will be no zero divide.) The LIMIT is applied as a separate Plan node.
+ * Note that a base relation's rows count (and, by extension, plan_rows for
+ * plan nodes below the LIMIT node) are set without regard to any LIMIT, so
+ * that this equation works properly. (Also, these routines guarantee not to
+ * set the rows count to zero, so there will be no zero divide.) The LIMIT is
+ * applied as a top-level plan node.
*
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.72 2001/05/09 00:35:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.73 2001/05/09 23:13:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -205,12 +206,18 @@ cost_index(Path *path, Query *root,
{
Cost startup_cost = 0;
Cost run_cost = 0;
- Cost cpu_per_tuple;
Cost indexStartupCost;
Cost indexTotalCost;
Selectivity indexSelectivity;
+ double indexCorrelation,
+ csquared;
+ Cost min_IO_cost,
+ max_IO_cost;
+ Cost cpu_per_tuple;
double tuples_fetched;
double pages_fetched;
+ double T,
+ b;
/* Should only be applied to base relations */
Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
@@ -224,38 +231,52 @@ cost_index(Path *path, Query *root,
* Call index-access-method-specific code to estimate the processing
* cost for scanning the index, as well as the selectivity of the
* index (ie, the fraction of main-table tuples we will have to
- * retrieve).
+ * retrieve) and its correlation to the main-table tuple order.
*/
- OidFunctionCall7(index->amcostestimate,
+ OidFunctionCall8(index->amcostestimate,
PointerGetDatum(root),
PointerGetDatum(baserel),
PointerGetDatum(index),
PointerGetDatum(indexQuals),
PointerGetDatum(&indexStartupCost),
PointerGetDatum(&indexTotalCost),
- PointerGetDatum(&indexSelectivity));
+ PointerGetDatum(&indexSelectivity),
+ PointerGetDatum(&indexCorrelation));
/* all costs for touching index itself included here */
startup_cost += indexStartupCost;
run_cost += indexTotalCost - indexStartupCost;
- /*
+ /*----------
* Estimate number of main-table tuples and pages fetched.
*
- * If the number of tuples is much smaller than the number of pages in
- * the relation, each tuple will cost a separate nonsequential fetch.
- * If it is comparable or larger, then probably we will be able to
- * avoid some fetches. We use a growth rate of log(#tuples/#pages +
- * 1) --- probably totally bogus, but intuitively it gives the right
- * shape of curve at least.
+ * When the index ordering is uncorrelated with the table ordering,
+ * we use an approximation proposed by Mackert and Lohman, "Index Scans
+ * Using a Finite LRU Buffer: A Validated I/O Model", ACM Transactions
+ * on Database Systems, Vol. 14, No. 3, September 1989, Pages 401-424.
+ * The Mackert and Lohman approximation is that the number of pages
+ * fetched is
+ * PF =
+ * min(2TNs/(2T+Ns), T) when T <= b
+ * 2TNs/(2T+Ns) when T > b and Ns <= 2Tb/(2T-b)
+ * b + (Ns - 2Tb/(2T-b))*(T-b)/T when T > b and Ns > 2Tb/(2T-b)
+ * where
+ * T = # pages in table
+ * N = # tuples in table
+ * s = selectivity = fraction of table to be scanned
+ * b = # buffer pages available (we include kernel space here)
*
- * XXX if the relation has recently been "clustered" using this index,
- * then in fact the target tuples will be highly nonuniformly
- * distributed, and we will be seriously overestimating the scan cost!
- * Currently we have no way to know whether the relation has been
- * clustered, nor how much it's been modified since the last
- * clustering, so we ignore this effect. Would be nice to do better
- * someday.
+ * When the index ordering is exactly correlated with the table ordering
+ * (just after a CLUSTER, for example), the number of pages fetched should
+ * be just sT. What's more, these will be sequential fetches, not the
+ * random fetches that occur in the uncorrelated case. So, depending on
+ * the extent of correlation, we should estimate the actual I/O cost
+ * somewhere between s * T * 1.0 and PF * random_cost. We currently
+ * interpolate linearly between these two endpoints based on the
+ * correlation squared (XXX is that appropriate?).
+ *
+ * In any case the number of tuples fetched is Ns.
+ *----------
*/
tuples_fetched = indexSelectivity * baserel->tuples;
@@ -263,24 +284,56 @@ cost_index(Path *path, Query *root,
if (tuples_fetched < 1.0)
tuples_fetched = 1.0;
- if (baserel->pages > 0)
- pages_fetched = ceil(baserel->pages *
- log(tuples_fetched / baserel->pages + 1.0));
+ /* This part is the Mackert and Lohman formula */
+
+ T = (baserel->pages > 1) ? (double) baserel->pages : 1.0;
+ b = (effective_cache_size > 1) ? effective_cache_size : 1.0;
+
+ if (T <= b)
+ {
+ pages_fetched =
+ (2.0 * T * tuples_fetched) / (2.0 * T + tuples_fetched);
+ if (pages_fetched > T)
+ pages_fetched = T;
+ }
else
- pages_fetched = tuples_fetched;
+ {
+ double lim;
+
+ lim = (2.0 * T * b) / (2.0 * T - b);
+ if (tuples_fetched <= lim)
+ {
+ pages_fetched =
+ (2.0 * T * tuples_fetched) / (2.0 * T + tuples_fetched);
+ }
+ else
+ {
+ pages_fetched =
+ b + (tuples_fetched - lim) * (T - b) / T;
+ }
+ }
/*
- * Now estimate one nonsequential access per page fetched, plus
- * appropriate CPU costs per tuple.
+ * min_IO_cost corresponds to the perfectly correlated case (csquared=1),
+ * max_IO_cost to the perfectly uncorrelated case (csquared=0). Note
+ * that we just charge random_page_cost per page in the uncorrelated
+ * case, rather than using cost_nonsequential_access, since we've already
+ * accounted for caching effects by using the Mackert model.
*/
+ min_IO_cost = ceil(indexSelectivity * T);
+ max_IO_cost = pages_fetched * random_page_cost;
- /* disk costs for main table */
- run_cost += pages_fetched * cost_nonsequential_access(baserel->pages);
+ /*
+ * Now interpolate based on estimated index order correlation
+ * to get total disk I/O cost for main table accesses.
+ */
+ csquared = indexCorrelation * indexCorrelation;
- /* CPU costs */
- cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
+ run_cost += max_IO_cost + csquared * (min_IO_cost - max_IO_cost);
/*
+ * Estimate CPU costs per tuple.
+ *
* Normally the indexquals will be removed from the list of
* restriction clauses that we have to evaluate as qpquals, so we
* should subtract their costs from baserestrictcost. For a lossy
@@ -290,6 +343,8 @@ cost_index(Path *path, Query *root,
* Rather than work out exactly how much to subtract, we don't
* subtract anything in that case either.
*/
+ cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
+
if (!index->lossy && !is_injoin)
cpu_per_tuple -= cost_qual_eval(indexQuals);
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 30f02de5c72..9b620c80f5a 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.31 2001/04/18 20:42:55 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.32 2001/05/09 23:13:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,8 +28,9 @@ typedef struct
typedef struct
{
int varno;
+ int varattno;
int sublevels_up;
-} contain_whole_tuple_var_context;
+} contain_var_reference_context;
typedef struct
{
@@ -39,8 +40,8 @@ typedef struct
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
-static bool contain_whole_tuple_var_walker(Node *node,
- contain_whole_tuple_var_context *context);
+static bool contain_var_reference_walker(Node *node,
+ contain_var_reference_context *context);
static bool contain_var_clause_walker(Node *node, void *context);
static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context);
@@ -129,10 +130,10 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
/*
- * contain_whole_tuple_var
+ * contain_var_reference
*
- * Detect whether a parsetree contains any references to the whole
- * tuple of a given rtable entry (ie, a Var with varattno = 0).
+ * Detect whether a parsetree contains any references to a specified
+ * attribute of a specified rtable entry.
*
* NOTE: this is used on not-yet-planned expressions. It may therefore find
* bare SubLinks, and if so it needs to recurse into them to look for uplevel
@@ -140,11 +141,12 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
* SubPlan, we only need to look at the parameters passed to the subplan.
*/
bool
-contain_whole_tuple_var(Node *node, int varno, int levelsup)
+contain_var_reference(Node *node, int varno, int varattno, int levelsup)
{
- contain_whole_tuple_var_context context;
+ contain_var_reference_context context;
context.varno = varno;
+ context.varattno = varattno;
context.sublevels_up = levelsup;
/*
@@ -154,15 +156,15 @@ contain_whole_tuple_var(Node *node, int varno, int levelsup)
*/
if (node && IsA(node, Query))
return query_tree_walker((Query *) node,
- contain_whole_tuple_var_walker,
+ contain_var_reference_walker,
(void *) &context, true);
else
- return contain_whole_tuple_var_walker(node, &context);
+ return contain_var_reference_walker(node, &context);
}
static bool
-contain_whole_tuple_var_walker(Node *node,
- contain_whole_tuple_var_context *context)
+contain_var_reference_walker(Node *node,
+ contain_var_reference_context *context)
{
if (node == NULL)
return false;
@@ -171,8 +173,8 @@ contain_whole_tuple_var_walker(Node *node,
Var *var = (Var *) node;
if (var->varno == context->varno &&
- var->varlevelsup == context->sublevels_up &&
- var->varattno == InvalidAttrNumber)
+ var->varattno == context->varattno &&
+ var->varlevelsup == context->sublevels_up)
return true;
return false;
}
@@ -187,11 +189,11 @@ contain_whole_tuple_var_walker(Node *node,
*/
Expr *expr = (Expr *) node;
- if (contain_whole_tuple_var_walker((Node *) ((SubPlan *) expr->oper)->sublink->oper,
- context))
+ if (contain_var_reference_walker((Node *) ((SubPlan *) expr->oper)->sublink->oper,
+ context))
return true;
- if (contain_whole_tuple_var_walker((Node *) expr->args,
- context))
+ if (contain_var_reference_walker((Node *) expr->args,
+ context))
return true;
return false;
}
@@ -202,17 +204,30 @@ contain_whole_tuple_var_walker(Node *node,
context->sublevels_up++;
result = query_tree_walker((Query *) node,
- contain_whole_tuple_var_walker,
+ contain_var_reference_walker,
(void *) context, true);
context->sublevels_up--;
return result;
}
- return expression_tree_walker(node, contain_whole_tuple_var_walker,
+ return expression_tree_walker(node, contain_var_reference_walker,
(void *) context);
}
/*
+ * contain_whole_tuple_var
+ *
+ * Detect whether a parsetree contains any references to the whole
+ * tuple of a given rtable entry (ie, a Var with varattno = 0).
+ */
+bool
+contain_whole_tuple_var(Node *node, int varno, int levelsup)
+{
+ return contain_var_reference(node, varno, InvalidAttrNumber, levelsup);
+}
+
+
+/*
* contain_var_clause
* Recursively scan a clause to discover whether it contains any Var nodes
* (of the current query level).
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 41ba82db7b5..d7633dc47dd 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.88 2001/05/07 00:43:23 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.89 2001/05/09 23:13:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -39,6 +39,7 @@
#include "optimizer/cost.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
+#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/int8.h"
@@ -818,7 +819,6 @@ eqjoinsel(PG_FUNCTION_ARGS)
{
#ifdef NOT_USED /* see neqjoinsel() before removing me! */
Oid opid = PG_GETARG_OID(0);
-
#endif
Oid relid1 = PG_GETARG_OID(1);
AttrNumber attno1 = PG_GETARG_INT16(2);
@@ -2230,16 +2230,14 @@ string_to_datum(const char *str, Oid datatype)
*-------------------------------------------------------------------------
*/
-static Datum
-genericcostestimate(PG_FUNCTION_ARGS)
+static void
+genericcostestimate(Query *root, RelOptInfo *rel,
+ IndexOptInfo *index, List *indexQuals,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation)
{
- Query *root = (Query *) PG_GETARG_POINTER(0);
- RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
- IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
- List *indexQuals = (List *) PG_GETARG_POINTER(3);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
double numIndexTuples;
double numIndexPages;
@@ -2275,33 +2273,134 @@ genericcostestimate(PG_FUNCTION_ARGS)
*indexTotalCost = numIndexPages +
(cpu_index_tuple_cost + cost_qual_eval(indexQuals)) * numIndexTuples;
- PG_RETURN_VOID();
+ /*
+ * Generic assumption about index correlation: there isn't any.
+ */
+ *indexCorrelation = 0.0;
}
-/*
- * For first cut, just use generic function for all index types.
- */
Datum
btcostestimate(PG_FUNCTION_ARGS)
{
- return genericcostestimate(fcinfo);
+ Query *root = (Query *) PG_GETARG_POINTER(0);
+ RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
+ IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
+ List *indexQuals = (List *) PG_GETARG_POINTER(3);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
+
+ genericcostestimate(root, rel, index, indexQuals,
+ indexStartupCost, indexTotalCost,
+ indexSelectivity, indexCorrelation);
+
+ /*
+ * If it's a functional index, leave the default zero-correlation
+ * estimate in place. If not, and if we can get an estimate for
+ * the first variable's ordering correlation C from pg_statistic,
+ * estimate the index correlation as C / number-of-columns.
+ * (The idea here is that multiple columns dilute the importance
+ * of the first column's ordering, but don't negate it entirely.)
+ */
+ if (index->indproc == InvalidOid)
+ {
+ Oid relid;
+ HeapTuple tuple;
+
+ relid = getrelid(lfirsti(rel->relids), root->rtable);
+ Assert(relid != InvalidOid);
+ tuple = SearchSysCache(STATRELATT,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(index->indexkeys[0]),
+ 0, 0);
+ if (HeapTupleIsValid(tuple))
+ {
+ Oid typid;
+ int32 typmod;
+ float4 *numbers;
+ int nnumbers;
+
+ get_atttypetypmod(relid, index->indexkeys[0],
+ &typid, &typmod);
+ if (get_attstatsslot(tuple, typid, typmod,
+ STATISTIC_KIND_CORRELATION,
+ index->ordering[0],
+ NULL, NULL, &numbers, &nnumbers))
+ {
+ double varCorrelation;
+ int nKeys;
+
+ Assert(nnumbers == 1);
+ varCorrelation = numbers[0];
+ for (nKeys = 1; index->indexkeys[nKeys] != 0; nKeys++)
+ /*skip*/;
+
+ *indexCorrelation = varCorrelation / nKeys;
+
+ free_attstatsslot(typid, NULL, 0, numbers, nnumbers);
+ }
+ ReleaseSysCache(tuple);
+ }
+ }
+
+ PG_RETURN_VOID();
}
Datum
rtcostestimate(PG_FUNCTION_ARGS)
{
- return genericcostestimate(fcinfo);
+ Query *root = (Query *) PG_GETARG_POINTER(0);
+ RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
+ IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
+ List *indexQuals = (List *) PG_GETARG_POINTER(3);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
+
+ genericcostestimate(root, rel, index, indexQuals,
+ indexStartupCost, indexTotalCost,
+ indexSelectivity, indexCorrelation);
+
+ PG_RETURN_VOID();
}
Datum
hashcostestimate(PG_FUNCTION_ARGS)
{
- return genericcostestimate(fcinfo);
+ Query *root = (Query *) PG_GETARG_POINTER(0);
+ RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
+ IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
+ List *indexQuals = (List *) PG_GETARG_POINTER(3);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
+
+ genericcostestimate(root, rel, index, indexQuals,
+ indexStartupCost, indexTotalCost,
+ indexSelectivity, indexCorrelation);
+
+ PG_RETURN_VOID();
}
Datum
gistcostestimate(PG_FUNCTION_ARGS)
{
- return genericcostestimate(fcinfo);
+ Query *root = (Query *) PG_GETARG_POINTER(0);
+ RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
+ IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
+ List *indexQuals = (List *) PG_GETARG_POINTER(3);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
+
+ genericcostestimate(root, rel, index, indexQuals,
+ indexStartupCost, indexTotalCost,
+ indexSelectivity, indexCorrelation);
+
+ PG_RETURN_VOID();
}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index ee15a940cc5..573c21afd8d 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.54 2001/05/09 00:35:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.55 2001/05/09 23:13:35 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -185,6 +185,36 @@ get_atttypmod(Oid relid, AttrNumber attnum)
return -1;
}
+/*
+ * get_atttypetypmod
+ *
+ * A two-fer: given the relation id and the attribute number,
+ * fetch both type OID and atttypmod in a single cache lookup.
+ *
+ * Unlike the otherwise-similar get_atttype/get_atttypmod, this routine
+ * raises an error if it can't obtain the information.
+ */
+void
+get_atttypetypmod(Oid relid, AttrNumber attnum,
+ Oid *typid, int32 *typmod)
+{
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+
+ tp = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for relation %u attribute %d",
+ relid, attnum);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+
+ *typid = att_tup->atttypid;
+ *typmod = att_tup->atttypmod;
+ ReleaseSysCache(tp);
+}
+
/* ---------- INDEX CACHE ---------- */
/* watch this space...
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d03d472043f..7d7acf96f73 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_proc.h,v 1.184 2001/03/22 04:00:39 momjian Exp $
+ * $Id: pg_proc.h,v 1.185 2001/05/09 23:13:35 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -210,9 +210,9 @@ DESCR("not equal");
DATA(insert OID = 89 ( version PGUID 12 f t f t 0 f 25 "" 100 0 0 100 pgsql_version - ));
DESCR("PostgreSQL version string");
-DATA(insert OID = 1265 ( rtcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 rtcostestimate - ));
+DATA(insert OID = 1265 ( rtcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 rtcostestimate - ));
DESCR("r-tree cost estimator");
-DATA(insert OID = 1268 ( btcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 btcostestimate - ));
+DATA(insert OID = 1268 ( btcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 btcostestimate - ));
DESCR("btree cost estimator");
/* OIDS 100 - 199 */
@@ -789,7 +789,7 @@ DESCR("convert name to char()");
DATA(insert OID = 409 ( name PGUID 12 f t t t 1 f 19 "1042" 100 0 0 100 bpchar_name - ));
DESCR("convert char() to name");
-DATA(insert OID = 438 ( hashcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 hashcostestimate - ));
+DATA(insert OID = 438 ( hashcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 hashcostestimate - ));
DESCR("hash index cost estimator");
DATA(insert OID = 440 ( hashgettuple PGUID 12 f t f t 2 f 23 "0 0" 100 0 0 100 hashgettuple - ));
@@ -1014,7 +1014,7 @@ DESCR("larger of two");
DATA(insert OID = 771 ( int2smaller PGUID 12 f t t t 2 f 21 "21 21" 100 0 0 100 int2smaller - ));
DESCR("smaller of two");
-DATA(insert OID = 772 ( gistcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 gistcostestimate - ));
+DATA(insert OID = 772 ( gistcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 gistcostestimate - ));
DESCR("gist cost estimator");
DATA(insert OID = 774 ( gistgettuple PGUID 12 f t f t 2 f 23 "0 0" 100 0 0 100 gistgettuple - ));
DESCR("gist(internal)");
diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h
index 45048133eb0..4cad677c7ce 100644
--- a/src/include/optimizer/var.h
+++ b/src/include/optimizer/var.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: var.h,v 1.13 2001/04/18 20:42:55 tgl Exp $
+ * $Id: var.h,v 1.14 2001/05/09 23:13:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,6 +17,8 @@
#include "nodes/primnodes.h"
extern List *pull_varnos(Node *node);
+extern bool contain_var_reference(Node *node, int varno, int varattno,
+ int levelsup);
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 3f18a4aea63..719f68a873f 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lsyscache.h,v 1.32 2001/05/09 00:35:09 tgl Exp $
+ * $Id: lsyscache.h,v 1.33 2001/05/09 23:13:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,8 @@ extern AttrNumber get_attnum(Oid relid, char *attname);
extern Oid get_atttype(Oid relid, AttrNumber attnum);
extern bool get_attisset(Oid relid, char *attname);
extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
+extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
+ Oid *typid, int32 *typmod);
extern RegProcedure get_opcode(Oid opno);
extern char *get_opname(Oid opno);
extern bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype,