diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/executor/nodeHash.c | 3 | ||||
-rw-r--r-- | src/backend/tsearch/ts_selfuncs.c | 3 | ||||
-rw-r--r-- | src/backend/utils/adt/selfuncs.c | 334 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 7 | ||||
-rw-r--r-- | src/include/utils/lsyscache.h | 3 |
5 files changed, 322 insertions, 28 deletions
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 34e8f4149ee..558a780229c 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.125 2010/01/02 16:57:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.126 2010/01/04 02:44:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1006,6 +1006,7 @@ ExecHashBuildSkewHash(HashJoinTable hashtable, Hash *node, int mcvsToUse) if (get_attstatsslot(statsTuple, node->skewColType, node->skewColTypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values, &nvalues, &numbers, &nnumbers)) { diff --git a/src/backend/tsearch/ts_selfuncs.c b/src/backend/tsearch/ts_selfuncs.c index c000433325e..a22dba55e5e 100644 --- a/src/backend/tsearch/ts_selfuncs.c +++ b/src/backend/tsearch/ts_selfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tsearch/ts_selfuncs.c,v 1.6 2010/01/02 16:57:53 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tsearch/ts_selfuncs.c,v 1.7 2010/01/04 02:44:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -170,6 +170,7 @@ tsquerysel(VariableStatData *vardata, Datum constval) if (get_attstatsslot(vardata->statsTuple, TEXTOID, -1, STATISTIC_KIND_MCELEM, InvalidOid, + NULL, &values, &nvalues, &numbers, &nnumbers)) { diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 35cc1ae870e..2506eaaf82d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.266 2010/01/02 16:57:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.267 2010/01/04 02:44:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,9 +92,11 @@ #include <math.h> #include "access/sysattr.h" +#include "catalog/index.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" +#include "executor/executor.h" #include "mb/pg_wchar.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -118,6 +120,7 @@ #include "utils/pg_locale.h" #include "utils/selfuncs.h" #include "utils/syscache.h" +#include "utils/tqual.h" /* Hooks for plugins to get control when we ask for stats */ @@ -130,7 +133,8 @@ static double var_eq_const(VariableStatData *vardata, Oid operator, static double var_eq_non_const(VariableStatData *vardata, Oid operator, Node *other, bool varonleft); -static double ineq_histogram_selectivity(VariableStatData *vardata, +static double ineq_histogram_selectivity(PlannerInfo *root, + VariableStatData *vardata, FmgrInfo *opproc, bool isgt, Datum constval, Oid consttype); static double eqjoinsel_inner(Oid operator, @@ -161,7 +165,12 @@ static char *convert_string_datum(Datum value, Oid typid); static double convert_timevalue_to_scalar(Datum value, Oid typid); static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Datum *min, Datum *max); -static Selectivity prefix_selectivity(VariableStatData *vardata, +static bool get_actual_variable_range(PlannerInfo *root, + VariableStatData *vardata, + Oid sortop, + Datum *min, Datum *max); +static Selectivity prefix_selectivity(PlannerInfo *root, + VariableStatData *vardata, Oid vartype, Oid opfamily, Const *prefixcon); static Selectivity pattern_selectivity(Const *patt, Pattern_Type ptype); static Datum string_to_datum(const char *str, Oid datatype); @@ -266,6 +275,7 @@ var_eq_const(VariableStatData *vardata, Oid operator, if (get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values, &nvalues, &numbers, &nnumbers)) { @@ -405,6 +415,7 @@ var_eq_non_const(VariableStatData *vardata, Oid operator, if (get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, NULL, NULL, &numbers, &nnumbers)) { @@ -514,7 +525,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt, * If there is a histogram, determine which bin the constant falls in, and * compute the resulting contribution to selectivity. */ - hist_selec = ineq_histogram_selectivity(vardata, &opproc, isgt, + hist_selec = ineq_histogram_selectivity(root, vardata, &opproc, isgt, constval, consttype); /* @@ -524,7 +535,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt, */ selec = 1.0 - stats->stanullfrac - sumcommon; - if (hist_selec > 0.0) + if (hist_selec >= 0.0) selec *= hist_selec; else { @@ -575,6 +586,7 @@ mcv_selectivity(VariableStatData *vardata, FmgrInfo *opproc, get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values, &nvalues, &numbers, &nnumbers)) { @@ -648,6 +660,7 @@ histogram_selectivity(VariableStatData *vardata, FmgrInfo *opproc, get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_HISTOGRAM, InvalidOid, + NULL, &values, &nvalues, NULL, NULL)) { @@ -689,23 +702,24 @@ histogram_selectivity(VariableStatData *vardata, FmgrInfo *opproc, * Determine the fraction of the variable's histogram population that * satisfies the inequality condition, ie, VAR < CONST or VAR > CONST. * - * Returns zero if there is no histogram (valid results will always be - * greater than zero). + * Returns -1 if there is no histogram (valid results will always be >= 0). * * Note that the result disregards both the most-common-values (if any) and * null entries. The caller is expected to combine this result with * statistics for those portions of the column population. */ static double -ineq_histogram_selectivity(VariableStatData *vardata, +ineq_histogram_selectivity(PlannerInfo *root, + VariableStatData *vardata, FmgrInfo *opproc, bool isgt, Datum constval, Oid consttype) { double hist_selec; + Oid hist_op; Datum *values; int nvalues; - hist_selec = 0.0; + hist_selec = -1.0; /* * Someday, ANALYZE might store more than one histogram per rel/att, @@ -721,6 +735,7 @@ ineq_histogram_selectivity(VariableStatData *vardata, get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_HISTOGRAM, InvalidOid, + &hist_op, &values, &nvalues, NULL, NULL)) { @@ -732,16 +747,56 @@ ineq_histogram_selectivity(VariableStatData *vardata, * actually sort-compatible with the histogram, you'll get garbage * results ... but probably not any more garbage-y than you would * from the old linear search.) + * + * If the binary search accesses the first or last histogram entry, + * we try to replace that endpoint with the true column min or max + * as found by get_actual_variable_range(). This ameliorates + * misestimates when the min or max is moving as a result of + * changes since the last ANALYZE. Note that this could result + * in effectively including MCVs into the histogram that weren't + * there before, but we don't try to correct for that. */ double histfrac; int lobound = 0; /* first possible slot to search */ int hibound = nvalues; /* last+1 slot to search */ + bool have_end = false; + + /* + * If there are only two histogram entries, we'll want up-to-date + * values for both. (If there are more than two, we need at most + * one of them to be updated, so we deal with that within the + * loop.) + */ + if (nvalues == 2) + have_end = get_actual_variable_range(root, + vardata, + hist_op, + &values[0], + &values[1]); while (lobound < hibound) { int probe = (lobound + hibound) / 2; bool ltcmp; + /* + * If we find ourselves about to compare to the first or last + * histogram entry, first try to replace it with the actual + * current min or max (unless we already did so above). + */ + if (probe == 0 && nvalues > 2) + have_end = get_actual_variable_range(root, + vardata, + hist_op, + &values[0], + NULL); + else if (probe == nvalues - 1 && nvalues > 2) + have_end = get_actual_variable_range(root, + vardata, + hist_op, + NULL, + &values[probe]); + ltcmp = DatumGetBool(FunctionCall2(opproc, values[probe], constval)); @@ -772,7 +827,7 @@ ineq_histogram_selectivity(VariableStatData *vardata, double binfrac; /* - * We have values[i-1] < constant < values[i]. + * We have values[i-1] <= constant <= values[i]. * * Convert the constant and the two nearest bin boundary * values to a uniform comparison scale, and do a linear @@ -840,12 +895,18 @@ ineq_histogram_selectivity(VariableStatData *vardata, /* * The histogram boundaries are only approximate to begin with, * and may well be out of date anyway. Therefore, don't believe - * extremely small or large selectivity estimates. + * extremely small or large selectivity estimates --- unless we + * got actual current endpoint values from the table. */ - if (hist_selec < 0.0001) - hist_selec = 0.0001; - else if (hist_selec > 0.9999) - hist_selec = 0.9999; + if (have_end) + CLAMP_PROBABILITY(hist_selec); + else + { + if (hist_selec < 0.0001) + hist_selec = 0.0001; + else if (hist_selec > 0.9999) + hist_selec = 0.9999; + } } free_attstatsslot(vardata->atttype, values, nvalues, NULL, 0); @@ -1198,7 +1259,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype, bool negate) Selectivity restsel; if (pstatus == Pattern_Prefix_Partial) - prefixsel = prefix_selectivity(&vardata, vartype, + prefixsel = prefix_selectivity(root, &vardata, vartype, opfamily, prefix); else prefixsel = 1.0; @@ -1363,6 +1424,7 @@ booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg, if (get_attstatsslot(vardata.statsTuple, vardata.atttype, vardata.atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values, &nvalues, &numbers, &nnumbers) && nnumbers > 0) @@ -1999,6 +2061,7 @@ eqjoinsel_inner(Oid operator, vardata1->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values1, &nvalues1, &numbers1, &nnumbers1); } @@ -2011,6 +2074,7 @@ eqjoinsel_inner(Oid operator, vardata2->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values2, &nvalues2, &numbers2, &nnumbers2); } @@ -2232,6 +2296,7 @@ eqjoinsel_semi(Oid operator, vardata1->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values1, &nvalues1, &numbers1, &nnumbers1); } @@ -2244,6 +2309,7 @@ eqjoinsel_semi(Oid operator, vardata2->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values2, &nvalues2, &numbers2, &nnumbers2); } @@ -3226,7 +3292,9 @@ estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets) if (get_attstatsslot(vardata.statsTuple, vardata.atttype, vardata.atttypmod, STATISTIC_KIND_MCV, InvalidOid, - NULL, NULL, &numbers, &nnumbers)) + NULL, + NULL, NULL, + &numbers, &nnumbers)) { /* * The first MCV stat is for the most common value. @@ -4339,6 +4407,18 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, int nvalues; int i; + /* + * XXX It's very tempting to try to use the actual column min and max, + * if we can get them relatively-cheaply with an index probe. However, + * since this function is called many times during join planning, + * that could have unpleasant effects on planning speed. Need more + * investigation before enabling this. + */ +#ifdef NOT_USED + if (get_actual_variable_range(root, vardata, sortop, min, max)) + return true; +#endif + if (!HeapTupleIsValid(vardata->statsTuple)) { /* no stats available, so default result */ @@ -4358,6 +4438,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, if (get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_HISTOGRAM, sortop, + NULL, &values, &nvalues, NULL, NULL)) { @@ -4372,6 +4453,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, else if (get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_HISTOGRAM, InvalidOid, + NULL, &values, &nvalues, NULL, NULL)) { @@ -4388,6 +4470,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, if (get_attstatsslot(vardata->statsTuple, vardata->atttype, vardata->atttypmod, STATISTIC_KIND_MCV, InvalidOid, + NULL, &values, &nvalues, NULL, NULL)) { @@ -4429,6 +4512,205 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, } +/* + * get_actual_variable_range + * Attempt to identify the current *actual* minimum and/or maximum + * of the specified variable, by looking for a suitable btree index + * and fetching its low and/or high values. + * If successful, store values in *min and *max, and return TRUE. + * (Either pointer can be NULL if that endpoint isn't needed.) + * If no data available, return FALSE. + * + * sortop is the "<" comparison operator to use. + */ +static bool +get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, + Oid sortop, + Datum *min, Datum *max) +{ + bool have_data = false; + RelOptInfo *rel = vardata->rel; + RangeTblEntry *rte; + ListCell *lc; + + /* No hope if no relation or it doesn't have indexes */ + if (rel == NULL || rel->indexlist == NIL) + return false; + /* If it has indexes it must be a plain relation */ + rte = root->simple_rte_array[rel->relid]; + Assert(rte->rtekind == RTE_RELATION); + + /* Search through the indexes to see if any match our problem */ + foreach(lc, rel->indexlist) + { + IndexOptInfo *index = (IndexOptInfo *) lfirst(lc); + ScanDirection indexscandir; + + /* Ignore non-btree indexes */ + if (index->relam != BTREE_AM_OID) + continue; + + /* + * Ignore partial indexes --- we only want stats that cover the + * entire relation. + */ + if (index->indpred != NIL) + continue; + + /* + * The index list might include fictitious indexes inserted by a + * get_relation_info hook --- don't try to access them. + */ + if (!OidIsValid(index->indexoid)) + continue; + + /* + * The first index column must match the desired variable and sort + * operator --- but we can use a descending-order index. + */ + if (sortop == index->fwdsortop[0]) + indexscandir = ForwardScanDirection; + else if (sortop == index->revsortop[0]) + indexscandir = BackwardScanDirection; + else + continue; + if (!match_index_to_operand(vardata->var, 0, index)) + continue; + + /* + * Found a suitable index to extract data from. We'll need an + * EState and a bunch of other infrastructure. + */ + { + EState *estate; + ExprContext *econtext; + MemoryContext tmpcontext; + MemoryContext oldcontext; + Relation heapRel; + Relation indexRel; + IndexInfo *indexInfo; + TupleTableSlot *slot; + int16 typLen; + bool typByVal; + ScanKeyData scankeys[1]; + IndexScanDesc index_scan; + HeapTuple tup; + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + + estate = CreateExecutorState(); + econtext = GetPerTupleExprContext(estate); + /* Make sure any cruft is generated in the econtext's memory */ + tmpcontext = econtext->ecxt_per_tuple_memory; + oldcontext = MemoryContextSwitchTo(tmpcontext); + + /* + * Open the table and index so we can read from them. We should + * already have at least AccessShareLock on the table, but not + * necessarily on the index. + */ + heapRel = heap_open(rte->relid, NoLock); + indexRel = index_open(index->indexoid, AccessShareLock); + + /* extract index key information from the index's pg_index info */ + indexInfo = BuildIndexInfo(indexRel); + + /* some other stuff */ + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel)); + econtext->ecxt_scantuple = slot; + get_typlenbyval(vardata->atttype, &typLen, &typByVal); + + /* set up an IS NOT NULL scan key so that we ignore nulls */ + ScanKeyEntryInitialize(&scankeys[0], + SK_ISNULL | SK_SEARCHNOTNULL, + 1, /* index col to scan */ + InvalidStrategy, /* no strategy */ + InvalidOid, /* no strategy subtype */ + InvalidOid, /* no reg proc for this */ + (Datum) 0); /* constant */ + + have_data = true; + + /* If min is requested ... */ + if (min) + { + index_scan = index_beginscan(heapRel, indexRel, SnapshotNow, + 1, scankeys); + + /* Fetch first tuple in sortop's direction */ + if ((tup = index_getnext(index_scan, + indexscandir)) != NULL) + { + /* Extract the index column values from the heap tuple */ + ExecStoreTuple(tup, slot, InvalidBuffer, false); + FormIndexDatum(indexInfo, slot, estate, + values, isnull); + + /* Shouldn't have got a null, but be careful */ + if (isnull[0]) + elog(ERROR, "found unexpected null value in index \"%s\"", + RelationGetRelationName(indexRel)); + + /* Copy the index column value out to caller's context */ + MemoryContextSwitchTo(oldcontext); + *min = datumCopy(values[0], typByVal, typLen); + MemoryContextSwitchTo(tmpcontext); + } + else + have_data = false; + + index_endscan(index_scan); + } + + /* If max is requested, and we didn't find the index is empty */ + if (max && have_data) + { + index_scan = index_beginscan(heapRel, indexRel, SnapshotNow, + 1, scankeys); + + /* Fetch first tuple in reverse direction */ + if ((tup = index_getnext(index_scan, + -indexscandir)) != NULL) + { + /* Extract the index column values from the heap tuple */ + ExecStoreTuple(tup, slot, InvalidBuffer, false); + FormIndexDatum(indexInfo, slot, estate, + values, isnull); + + /* Shouldn't have got a null, but be careful */ + if (isnull[0]) + elog(ERROR, "found unexpected null value in index \"%s\"", + RelationGetRelationName(indexRel)); + + /* Copy the index column value out to caller's context */ + MemoryContextSwitchTo(oldcontext); + *max = datumCopy(values[0], typByVal, typLen); + MemoryContextSwitchTo(tmpcontext); + } + else + have_data = false; + + index_endscan(index_scan); + } + + /* Clean everything up */ + ExecDropSingleTupleTableSlot(slot); + + index_close(indexRel, AccessShareLock); + heap_close(heapRel, NoLock); + + MemoryContextSwitchTo(oldcontext); + FreeExecutorState(estate); + + /* And we're done */ + break; + } + } + + return have_data; +} + + /*------------------------------------------------------------------------- * * Pattern analysis functions @@ -4795,7 +5077,7 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype, * more useful to use the upper-bound code than not. */ static Selectivity -prefix_selectivity(VariableStatData *vardata, +prefix_selectivity(PlannerInfo *root, VariableStatData *vardata, Oid vartype, Oid opfamily, Const *prefixcon) { Selectivity prefixsel; @@ -4810,11 +5092,11 @@ prefix_selectivity(VariableStatData *vardata, elog(ERROR, "no >= operator for opfamily %u", opfamily); fmgr_info(get_opcode(cmpopr), &opproc); - prefixsel = ineq_histogram_selectivity(vardata, &opproc, true, + prefixsel = ineq_histogram_selectivity(root, vardata, &opproc, true, prefixcon->constvalue, prefixcon->consttype); - if (prefixsel <= 0.0) + if (prefixsel < 0.0) { /* No histogram is present ... return a suitable default estimate */ return DEFAULT_MATCH_SEL; @@ -4836,12 +5118,12 @@ prefix_selectivity(VariableStatData *vardata, { Selectivity topsel; - topsel = ineq_histogram_selectivity(vardata, &opproc, false, + topsel = ineq_histogram_selectivity(root, vardata, &opproc, false, greaterstrcon->constvalue, greaterstrcon->consttype); /* ineq_histogram_selectivity worked before, it shouldn't fail now */ - Assert(topsel > 0.0); + Assert(topsel >= 0.0); /* * Merge the two selectivities in the same way as for a range query @@ -5870,7 +6152,9 @@ btcostestimate(PG_FUNCTION_ARGS) if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0, STATISTIC_KIND_CORRELATION, index->fwdsortop[0], - NULL, NULL, &numbers, &nnumbers)) + NULL, + NULL, NULL, + &numbers, &nnumbers)) { double varCorrelation; @@ -5887,7 +6171,9 @@ btcostestimate(PG_FUNCTION_ARGS) else if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0, STATISTIC_KIND_CORRELATION, index->revsortop[0], - NULL, NULL, &numbers, &nnumbers)) + NULL, + NULL, NULL, + &numbers, &nnumbers)) { double varCorrelation; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index cd138c312f0..e68a16c49f9 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 - * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.165 2010/01/02 16:57:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.166 2010/01/04 02:44:40 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -2577,6 +2577,7 @@ get_attavgwidth(Oid relid, AttrNumber attnum) * atttypmod: typmod of attribute (can be 0 if values == NULL). * reqkind: STAKIND code for desired statistics slot kind. * reqop: STAOP value wanted, or InvalidOid if don't care. + * actualop: if not NULL, *actualop receives the actual STAOP value. * values, nvalues: if not NULL, the slot's stavalues are extracted. * numbers, nnumbers: if not NULL, the slot's stanumbers are extracted. * @@ -2589,6 +2590,7 @@ bool get_attstatsslot(HeapTuple statstuple, Oid atttype, int32 atttypmod, int reqkind, Oid reqop, + Oid *actualop, Datum **values, int *nvalues, float4 **numbers, int *nnumbers) { @@ -2611,6 +2613,9 @@ get_attstatsslot(HeapTuple statstuple, if (i >= STATISTIC_NUM_SLOTS) return false; /* not there */ + if (actualop) + *actualop = (&stats->staop1)[i]; + if (values) { val = SysCacheGetAttr(STATRELATTINH, statstuple, diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 35252af9af6..429bcaa1477 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.130 2010/01/02 16:58:10 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.131 2010/01/04 02:44:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -129,6 +129,7 @@ extern int32 get_attavgwidth(Oid relid, AttrNumber attnum); extern bool get_attstatsslot(HeapTuple statstuple, Oid atttype, int32 atttypmod, int reqkind, Oid reqop, + Oid *actualop, Datum **values, int *nvalues, float4 **numbers, int *nnumbers); extern void free_attstatsslot(Oid atttype, |