Commit 7b7df9f0 authored by Tom Lane's avatar Tom Lane

Add hooks to let plugins override the planner's lookups in pg_statistic.

Simon Riggs, with some editorialization by me.
parent bc965e84
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.253 2008/08/25 22:42:34 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.254 2008/09/28 19:51:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -119,6 +119,10 @@ ...@@ -119,6 +119,10 @@
#include "utils/syscache.h" #include "utils/syscache.h"
/* Hooks for plugins to get control when we ask for stats */
get_relation_stats_hook_type get_relation_stats_hook = NULL;
get_index_stats_hook_type get_index_stats_hook = NULL;
static double var_eq_const(VariableStatData *vardata, Oid operator, static double var_eq_const(VariableStatData *vardata, Oid operator,
Datum constval, bool constisnull, Datum constval, bool constisnull,
bool varonleft); bool varonleft);
...@@ -2935,7 +2939,7 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows) ...@@ -2935,7 +2939,7 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows)
* complicated. * complicated.
*/ */
examine_variable(root, groupexpr, 0, &vardata); examine_variable(root, groupexpr, 0, &vardata);
if (vardata.statsTuple != NULL || vardata.isunique) if (HeapTupleIsValid(vardata.statsTuple) || vardata.isunique)
{ {
varinfos = add_unique_group_var(root, varinfos, varinfos = add_unique_group_var(root, varinfos,
groupexpr, &vardata); groupexpr, &vardata);
...@@ -3942,6 +3946,7 @@ get_join_variables(PlannerInfo *root, List *args, SpecialJoinInfo *sjinfo, ...@@ -3942,6 +3946,7 @@ get_join_variables(PlannerInfo *root, List *args, SpecialJoinInfo *sjinfo,
* subquery, not one in the current query). * subquery, not one in the current query).
* statsTuple: the pg_statistic entry for the variable, if one exists; * statsTuple: the pg_statistic entry for the variable, if one exists;
* otherwise NULL. * otherwise NULL.
* freefunc: pointer to a function to release statsTuple with.
* vartype: exposed type of the expression; this should always match * vartype: exposed type of the expression; this should always match
* the declared input type of the operator we are estimating for. * the declared input type of the operator we are estimating for.
* atttype, atttypmod: type data to pass to get_attstatsslot(). This is * atttype, atttypmod: type data to pass to get_attstatsslot(). This is
...@@ -3986,7 +3991,18 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, ...@@ -3986,7 +3991,18 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
rte = root->simple_rte_array[var->varno]; rte = root->simple_rte_array[var->varno];
if (rte->inh) if (get_relation_stats_hook &&
(*get_relation_stats_hook) (root, rte, var->varattno, vardata))
{
/*
* The hook took control of acquiring a stats tuple. If it
* did supply a tuple, it'd better have supplied a freefunc.
*/
if (HeapTupleIsValid(vardata->statsTuple) &&
!vardata->freefunc)
elog(ERROR, "no function provided to release variable stats with");
}
else if (rte->inh)
{ {
/* /*
* XXX This means the Var represents a column of an append * XXX This means the Var represents a column of an append
...@@ -4000,6 +4016,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, ...@@ -4000,6 +4016,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
ObjectIdGetDatum(rte->relid), ObjectIdGetDatum(rte->relid),
Int16GetDatum(var->varattno), Int16GetDatum(var->varattno),
0, 0); 0, 0);
vardata->freefunc = ReleaseSysCache;
} }
else else
{ {
...@@ -4116,10 +4133,28 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, ...@@ -4116,10 +4133,28 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
index->indpred == NIL) index->indpred == NIL)
vardata->isunique = true; vardata->isunique = true;
/* Has it got stats? */ /* Has it got stats? */
vardata->statsTuple = SearchSysCache(STATRELATT, if (get_index_stats_hook &&
ObjectIdGetDatum(index->indexoid), (*get_index_stats_hook) (root, index->indexoid,
Int16GetDatum(pos + 1), pos + 1, vardata))
0, 0); {
/*
* The hook took control of acquiring a stats
* tuple. If it did supply a tuple, it'd better
* have supplied a freefunc.
*/
if (HeapTupleIsValid(vardata->statsTuple) &&
!vardata->freefunc)
elog(ERROR, "no function provided to release variable stats with");
}
else
{
vardata->statsTuple =
SearchSysCache(STATRELATT,
ObjectIdGetDatum(index->indexoid),
Int16GetDatum(pos + 1),
0, 0);
vardata->freefunc = ReleaseSysCache;
}
if (vardata->statsTuple) if (vardata->statsTuple)
break; break;
} }
...@@ -5551,7 +5586,7 @@ btcostestimate(PG_FUNCTION_ARGS) ...@@ -5551,7 +5586,7 @@ btcostestimate(PG_FUNCTION_ARGS)
double *indexCorrelation = (double *) PG_GETARG_POINTER(7); double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
Oid relid; Oid relid;
AttrNumber colnum; AttrNumber colnum;
HeapTuple tuple; VariableStatData vardata;
double numIndexTuples; double numIndexTuples;
List *indexBoundQuals; List *indexBoundQuals;
int indexcol; int indexcol;
...@@ -5756,17 +5791,34 @@ btcostestimate(PG_FUNCTION_ARGS) ...@@ -5756,17 +5791,34 @@ btcostestimate(PG_FUNCTION_ARGS)
colnum = 1; colnum = 1;
} }
tuple = SearchSysCache(STATRELATT, MemSet(&vardata, 0, sizeof(vardata));
ObjectIdGetDatum(relid),
Int16GetDatum(colnum),
0, 0);
if (HeapTupleIsValid(tuple)) if (get_index_stats_hook &&
(*get_index_stats_hook) (root, relid, colnum, &vardata))
{
/*
* The hook took control of acquiring a stats tuple. If it did supply
* a tuple, it'd better have supplied a freefunc.
*/
if (HeapTupleIsValid(vardata.statsTuple) &&
!vardata.freefunc)
elog(ERROR, "no function provided to release variable stats with");
}
else
{
vardata.statsTuple = SearchSysCache(STATRELATT,
ObjectIdGetDatum(relid),
Int16GetDatum(colnum),
0, 0);
vardata.freefunc = ReleaseSysCache;
}
if (HeapTupleIsValid(vardata.statsTuple))
{ {
float4 *numbers; float4 *numbers;
int nnumbers; int nnumbers;
if (get_attstatsslot(tuple, InvalidOid, 0, if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
STATISTIC_KIND_CORRELATION, STATISTIC_KIND_CORRELATION,
index->fwdsortop[0], index->fwdsortop[0],
NULL, NULL, &numbers, &nnumbers)) NULL, NULL, &numbers, &nnumbers))
...@@ -5783,7 +5835,7 @@ btcostestimate(PG_FUNCTION_ARGS) ...@@ -5783,7 +5835,7 @@ btcostestimate(PG_FUNCTION_ARGS)
free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers); free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
} }
else if (get_attstatsslot(tuple, InvalidOid, 0, else if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
STATISTIC_KIND_CORRELATION, STATISTIC_KIND_CORRELATION,
index->revsortop[0], index->revsortop[0],
NULL, NULL, &numbers, &nnumbers)) NULL, NULL, &numbers, &nnumbers))
...@@ -5800,9 +5852,10 @@ btcostestimate(PG_FUNCTION_ARGS) ...@@ -5800,9 +5852,10 @@ btcostestimate(PG_FUNCTION_ARGS)
free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers); free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
} }
ReleaseSysCache(tuple);
} }
ReleaseVariableStats(vardata);
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.159 2008/08/02 21:32:00 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.160 2008/09/28 19:51:40 tgl Exp $
* *
* NOTES * NOTES
* Eventually, the index information should go through here, too. * Eventually, the index information should go through here, too.
...@@ -35,6 +35,9 @@ ...@@ -35,6 +35,9 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
/* Hook for plugins to get control in get_attavgwidth() */
get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
/* ---------- AMOP CACHES ---------- */ /* ---------- AMOP CACHES ---------- */
...@@ -2492,20 +2495,30 @@ get_typmodout(Oid typid) ...@@ -2492,20 +2495,30 @@ get_typmodout(Oid typid)
* *
* Given the table and attribute number of a column, get the average * Given the table and attribute number of a column, get the average
* width of entries in the column. Return zero if no data available. * width of entries in the column. Return zero if no data available.
*
* Calling a hook at this point looks somewhat strange, but is required
* because the optimizer calls this function without any other way for
* plug-ins to control the result.
*/ */
int32 int32
get_attavgwidth(Oid relid, AttrNumber attnum) get_attavgwidth(Oid relid, AttrNumber attnum)
{ {
HeapTuple tp; HeapTuple tp;
int32 stawidth;
if (get_attavgwidth_hook)
{
stawidth = (*get_attavgwidth_hook) (relid, attnum);
if (stawidth > 0)
return stawidth;
}
tp = SearchSysCache(STATRELATT, tp = SearchSysCache(STATRELATT,
ObjectIdGetDatum(relid), ObjectIdGetDatum(relid),
Int16GetDatum(attnum), Int16GetDatum(attnum),
0, 0); 0, 0);
if (HeapTupleIsValid(tp)) if (HeapTupleIsValid(tp))
{ {
int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth; stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
ReleaseSysCache(tp); ReleaseSysCache(tp);
if (stawidth > 0) if (stawidth > 0)
return stawidth; return stawidth;
...@@ -2523,6 +2536,9 @@ get_attavgwidth(Oid relid, AttrNumber attnum) ...@@ -2523,6 +2536,9 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
* already-looked-up tuple in the pg_statistic cache. We do this since * already-looked-up tuple in the pg_statistic cache. We do this since
* most callers will want to extract more than one value from the cache * most callers will want to extract more than one value from the cache
* entry, and we don't want to repeat the cache lookup unnecessarily. * entry, and we don't want to repeat the cache lookup unnecessarily.
* Also, this API allows this routine to be used with statistics tuples
* that have been provided by a stats hook and didn't really come from
* pg_statistic.
* *
* statstuple: pg_statistics tuple to be examined. * statstuple: pg_statistics tuple to be examined.
* atttype: type OID of attribute (can be InvalidOid if values == NULL). * atttype: type OID of attribute (can be InvalidOid if values == NULL).
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.125 2008/08/02 21:32:01 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.126 2008/09/28 19:51:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,10 @@ typedef enum IOFuncSelector ...@@ -26,6 +26,10 @@ typedef enum IOFuncSelector
IOFunc_send IOFunc_send
} IOFuncSelector; } IOFuncSelector;
/* Hook for plugins to get control in get_attavgwidth() */
typedef int32 (*get_attavgwidth_hook_type) (Oid relid, AttrNumber attnum);
extern PGDLLIMPORT get_attavgwidth_hook_type get_attavgwidth_hook;
extern bool op_in_opfamily(Oid opno, Oid opfamily); extern bool op_in_opfamily(Oid opno, Oid opfamily);
extern int get_op_opfamily_strategy(Oid opno, Oid opfamily); extern int get_op_opfamily_strategy(Oid opno, Oid opfamily);
extern void get_op_opfamily_properties(Oid opno, Oid opfamily, extern void get_op_opfamily_properties(Oid opno, Oid opfamily,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.46 2008/08/16 00:01:38 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.47 2008/09/28 19:51:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -64,12 +64,13 @@ ...@@ -64,12 +64,13 @@
/* Return data from examine_variable and friends */ /* Return data from examine_variable and friends */
typedef struct typedef struct VariableStatData
{ {
Node *var; /* the Var or expression tree */ Node *var; /* the Var or expression tree */
RelOptInfo *rel; /* Relation, or NULL if not identifiable */ RelOptInfo *rel; /* Relation, or NULL if not identifiable */
HeapTuple statsTuple; /* pg_statistic tuple, or NULL if none */ HeapTuple statsTuple; /* pg_statistic tuple, or NULL if none */
/* NB: if statsTuple!=NULL, it must be freed when caller is done */ /* NB: if statsTuple!=NULL, it must be freed when caller is done */
void (*freefunc) (HeapTuple tuple); /* how to free statsTuple */
Oid vartype; /* exposed type of expression */ Oid vartype; /* exposed type of expression */
Oid atttype; /* type to pass to get_attstatsslot */ Oid atttype; /* type to pass to get_attstatsslot */
int32 atttypmod; /* typmod to pass to get_attstatsslot */ int32 atttypmod; /* typmod to pass to get_attstatsslot */
...@@ -79,7 +80,7 @@ typedef struct ...@@ -79,7 +80,7 @@ typedef struct
#define ReleaseVariableStats(vardata) \ #define ReleaseVariableStats(vardata) \
do { \ do { \
if (HeapTupleIsValid((vardata).statsTuple)) \ if (HeapTupleIsValid((vardata).statsTuple)) \
ReleaseSysCache((vardata).statsTuple); \ (* (vardata).freefunc) ((vardata).statsTuple); \
} while(0) } while(0)
...@@ -97,6 +98,18 @@ typedef enum ...@@ -97,6 +98,18 @@ typedef enum
/* selfuncs.c */ /* selfuncs.c */
/* Hooks for plugins to get control when we ask for stats */
typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
RangeTblEntry *rte,
AttrNumber attnum,
VariableStatData *vardata);
extern PGDLLIMPORT get_relation_stats_hook_type get_relation_stats_hook;
typedef bool (*get_index_stats_hook_type) (PlannerInfo *root,
Oid indexOid,
AttrNumber indexattnum,
VariableStatData *vardata);
extern PGDLLIMPORT get_index_stats_hook_type get_index_stats_hook;
extern void examine_variable(PlannerInfo *root, Node *node, int varRelid, extern void examine_variable(PlannerInfo *root, Node *node, int varRelid,
VariableStatData *vardata); VariableStatData *vardata);
extern bool get_restriction_variable(PlannerInfo *root, List *args, extern bool get_restriction_variable(PlannerInfo *root, List *args,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment