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

Lines changed: 69 additions & 16 deletions
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

Lines changed: 19 additions & 3 deletions
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

Lines changed: 5 additions & 1 deletion
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

Lines changed: 16 additions & 3 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy