Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7b7df9f

Browse files
committed
Add hooks to let plugins override the planner's lookups in pg_statistic.
Simon Riggs, with some editorialization by me.
1 parent bc965e8 commit 7b7df9f

File tree

4 files changed

+109
-23
lines changed

4 files changed

+109
-23
lines changed

src/backend/utils/adt/selfuncs.c

+69-16
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.253 2008/08/25 22:42:34 tgl Exp $
18+
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.254 2008/09/28 19:51:40 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -119,6 +119,10 @@
119119
#include "utils/syscache.h"
120120

121121

122+
/* Hooks for plugins to get control when we ask for stats */
123+
get_relation_stats_hook_type get_relation_stats_hook = NULL;
124+
get_index_stats_hook_type get_index_stats_hook = NULL;
125+
122126
static double var_eq_const(VariableStatData *vardata, Oid operator,
123127
Datum constval, bool constisnull,
124128
bool varonleft);
@@ -2935,7 +2939,7 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows)
29352939
* complicated.
29362940
*/
29372941
examine_variable(root, groupexpr, 0, &vardata);
2938-
if (vardata.statsTuple != NULL || vardata.isunique)
2942+
if (HeapTupleIsValid(vardata.statsTuple) || vardata.isunique)
29392943
{
29402944
varinfos = add_unique_group_var(root, varinfos,
29412945
groupexpr, &vardata);
@@ -3942,6 +3946,7 @@ get_join_variables(PlannerInfo *root, List *args, SpecialJoinInfo *sjinfo,
39423946
* subquery, not one in the current query).
39433947
* statsTuple: the pg_statistic entry for the variable, if one exists;
39443948
* otherwise NULL.
3949+
* freefunc: pointer to a function to release statsTuple with.
39453950
* vartype: exposed type of the expression; this should always match
39463951
* the declared input type of the operator we are estimating for.
39473952
* atttype, atttypmod: type data to pass to get_attstatsslot(). This is
@@ -3986,7 +3991,18 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
39863991

39873992
rte = root->simple_rte_array[var->varno];
39883993

3989-
if (rte->inh)
3994+
if (get_relation_stats_hook &&
3995+
(*get_relation_stats_hook) (root, rte, var->varattno, vardata))
3996+
{
3997+
/*
3998+
* The hook took control of acquiring a stats tuple. If it
3999+
* did supply a tuple, it'd better have supplied a freefunc.
4000+
*/
4001+
if (HeapTupleIsValid(vardata->statsTuple) &&
4002+
!vardata->freefunc)
4003+
elog(ERROR, "no function provided to release variable stats with");
4004+
}
4005+
else if (rte->inh)
39904006
{
39914007
/*
39924008
* XXX This means the Var represents a column of an append
@@ -4000,6 +4016,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
40004016
ObjectIdGetDatum(rte->relid),
40014017
Int16GetDatum(var->varattno),
40024018
0, 0);
4019+
vardata->freefunc = ReleaseSysCache;
40034020
}
40044021
else
40054022
{
@@ -4116,10 +4133,28 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
41164133
index->indpred == NIL)
41174134
vardata->isunique = true;
41184135
/* Has it got stats? */
4119-
vardata->statsTuple = SearchSysCache(STATRELATT,
4120-
ObjectIdGetDatum(index->indexoid),
4121-
Int16GetDatum(pos + 1),
4122-
0, 0);
4136+
if (get_index_stats_hook &&
4137+
(*get_index_stats_hook) (root, index->indexoid,
4138+
pos + 1, vardata))
4139+
{
4140+
/*
4141+
* The hook took control of acquiring a stats
4142+
* tuple. If it did supply a tuple, it'd better
4143+
* have supplied a freefunc.
4144+
*/
4145+
if (HeapTupleIsValid(vardata->statsTuple) &&
4146+
!vardata->freefunc)
4147+
elog(ERROR, "no function provided to release variable stats with");
4148+
}
4149+
else
4150+
{
4151+
vardata->statsTuple =
4152+
SearchSysCache(STATRELATT,
4153+
ObjectIdGetDatum(index->indexoid),
4154+
Int16GetDatum(pos + 1),
4155+
0, 0);
4156+
vardata->freefunc = ReleaseSysCache;
4157+
}
41234158
if (vardata->statsTuple)
41244159
break;
41254160
}
@@ -5551,7 +5586,7 @@ btcostestimate(PG_FUNCTION_ARGS)
55515586
double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
55525587
Oid relid;
55535588
AttrNumber colnum;
5554-
HeapTuple tuple;
5589+
VariableStatData vardata;
55555590
double numIndexTuples;
55565591
List *indexBoundQuals;
55575592
int indexcol;
@@ -5756,17 +5791,34 @@ btcostestimate(PG_FUNCTION_ARGS)
57565791
colnum = 1;
57575792
}
57585793

5759-
tuple = SearchSysCache(STATRELATT,
5760-
ObjectIdGetDatum(relid),
5761-
Int16GetDatum(colnum),
5762-
0, 0);
5794+
MemSet(&vardata, 0, sizeof(vardata));
57635795

5764-
if (HeapTupleIsValid(tuple))
5796+
if (get_index_stats_hook &&
5797+
(*get_index_stats_hook) (root, relid, colnum, &vardata))
5798+
{
5799+
/*
5800+
* The hook took control of acquiring a stats tuple. If it did supply
5801+
* a tuple, it'd better have supplied a freefunc.
5802+
*/
5803+
if (HeapTupleIsValid(vardata.statsTuple) &&
5804+
!vardata.freefunc)
5805+
elog(ERROR, "no function provided to release variable stats with");
5806+
}
5807+
else
5808+
{
5809+
vardata.statsTuple = SearchSysCache(STATRELATT,
5810+
ObjectIdGetDatum(relid),
5811+
Int16GetDatum(colnum),
5812+
0, 0);
5813+
vardata.freefunc = ReleaseSysCache;
5814+
}
5815+
5816+
if (HeapTupleIsValid(vardata.statsTuple))
57655817
{
57665818
float4 *numbers;
57675819
int nnumbers;
57685820

5769-
if (get_attstatsslot(tuple, InvalidOid, 0,
5821+
if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
57705822
STATISTIC_KIND_CORRELATION,
57715823
index->fwdsortop[0],
57725824
NULL, NULL, &numbers, &nnumbers))
@@ -5783,7 +5835,7 @@ btcostestimate(PG_FUNCTION_ARGS)
57835835

57845836
free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
57855837
}
5786-
else if (get_attstatsslot(tuple, InvalidOid, 0,
5838+
else if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
57875839
STATISTIC_KIND_CORRELATION,
57885840
index->revsortop[0],
57895841
NULL, NULL, &numbers, &nnumbers))
@@ -5800,9 +5852,10 @@ btcostestimate(PG_FUNCTION_ARGS)
58005852

58015853
free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
58025854
}
5803-
ReleaseSysCache(tuple);
58045855
}
58055856

5857+
ReleaseVariableStats(vardata);
5858+
58065859
PG_RETURN_VOID();
58075860
}
58085861

src/backend/utils/cache/lsyscache.c

+19-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.159 2008/08/02 21:32:00 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.160 2008/09/28 19:51:40 tgl Exp $
1111
*
1212
* NOTES
1313
* Eventually, the index information should go through here, too.
@@ -35,6 +35,9 @@
3535
#include "utils/lsyscache.h"
3636
#include "utils/syscache.h"
3737

38+
/* Hook for plugins to get control in get_attavgwidth() */
39+
get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
40+
3841

3942
/* ---------- AMOP CACHES ---------- */
4043

@@ -2492,20 +2495,30 @@ get_typmodout(Oid typid)
24922495
*
24932496
* Given the table and attribute number of a column, get the average
24942497
* width of entries in the column. Return zero if no data available.
2498+
*
2499+
* Calling a hook at this point looks somewhat strange, but is required
2500+
* because the optimizer calls this function without any other way for
2501+
* plug-ins to control the result.
24952502
*/
24962503
int32
24972504
get_attavgwidth(Oid relid, AttrNumber attnum)
24982505
{
24992506
HeapTuple tp;
2507+
int32 stawidth;
25002508

2509+
if (get_attavgwidth_hook)
2510+
{
2511+
stawidth = (*get_attavgwidth_hook) (relid, attnum);
2512+
if (stawidth > 0)
2513+
return stawidth;
2514+
}
25012515
tp = SearchSysCache(STATRELATT,
25022516
ObjectIdGetDatum(relid),
25032517
Int16GetDatum(attnum),
25042518
0, 0);
25052519
if (HeapTupleIsValid(tp))
25062520
{
2507-
int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
2508-
2521+
stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
25092522
ReleaseSysCache(tp);
25102523
if (stawidth > 0)
25112524
return stawidth;
@@ -2523,6 +2536,9 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
25232536
* already-looked-up tuple in the pg_statistic cache. We do this since
25242537
* most callers will want to extract more than one value from the cache
25252538
* entry, and we don't want to repeat the cache lookup unnecessarily.
2539+
* Also, this API allows this routine to be used with statistics tuples
2540+
* that have been provided by a stats hook and didn't really come from
2541+
* pg_statistic.
25262542
*
25272543
* statstuple: pg_statistics tuple to be examined.
25282544
* atttype: type OID of attribute (can be InvalidOid if values == NULL).

src/include/utils/lsyscache.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
9-
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.125 2008/08/02 21:32:01 tgl Exp $
9+
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.126 2008/09/28 19:51:40 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -26,6 +26,10 @@ typedef enum IOFuncSelector
2626
IOFunc_send
2727
} IOFuncSelector;
2828

29+
/* Hook for plugins to get control in get_attavgwidth() */
30+
typedef int32 (*get_attavgwidth_hook_type) (Oid relid, AttrNumber attnum);
31+
extern PGDLLIMPORT get_attavgwidth_hook_type get_attavgwidth_hook;
32+
2933
extern bool op_in_opfamily(Oid opno, Oid opfamily);
3034
extern int get_op_opfamily_strategy(Oid opno, Oid opfamily);
3135
extern void get_op_opfamily_properties(Oid opno, Oid opfamily,

src/include/utils/selfuncs.h

+16-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.46 2008/08/16 00:01:38 tgl Exp $
11+
* $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.47 2008/09/28 19:51:40 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -64,12 +64,13 @@
6464

6565

6666
/* Return data from examine_variable and friends */
67-
typedef struct
67+
typedef struct VariableStatData
6868
{
6969
Node *var; /* the Var or expression tree */
7070
RelOptInfo *rel; /* Relation, or NULL if not identifiable */
7171
HeapTuple statsTuple; /* pg_statistic tuple, or NULL if none */
7272
/* NB: if statsTuple!=NULL, it must be freed when caller is done */
73+
void (*freefunc) (HeapTuple tuple); /* how to free statsTuple */
7374
Oid vartype; /* exposed type of expression */
7475
Oid atttype; /* type to pass to get_attstatsslot */
7576
int32 atttypmod; /* typmod to pass to get_attstatsslot */
@@ -79,7 +80,7 @@ typedef struct
7980
#define ReleaseVariableStats(vardata) \
8081
do { \
8182
if (HeapTupleIsValid((vardata).statsTuple)) \
82-
ReleaseSysCache((vardata).statsTuple); \
83+
(* (vardata).freefunc) ((vardata).statsTuple); \
8384
} while(0)
8485

8586

@@ -97,6 +98,18 @@ typedef enum
9798

9899
/* selfuncs.c */
99100

101+
/* Hooks for plugins to get control when we ask for stats */
102+
typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
103+
RangeTblEntry *rte,
104+
AttrNumber attnum,
105+
VariableStatData *vardata);
106+
extern PGDLLIMPORT get_relation_stats_hook_type get_relation_stats_hook;
107+
typedef bool (*get_index_stats_hook_type) (PlannerInfo *root,
108+
Oid indexOid,
109+
AttrNumber indexattnum,
110+
VariableStatData *vardata);
111+
extern PGDLLIMPORT get_index_stats_hook_type get_index_stats_hook;
112+
100113
extern void examine_variable(PlannerInfo *root, Node *node, int varRelid,
101114
VariableStatData *vardata);
102115
extern bool get_restriction_variable(PlannerInfo *root, List *args,

0 commit comments

Comments
 (0)