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

Commit 4434f89

Browse files
author
Nikita Glukhov
committed
Add pg_operator.oprstat for derived operator statistics estimation
1 parent adb5c28 commit 4434f89

File tree

7 files changed

+147
-0
lines changed

7 files changed

+147
-0
lines changed

src/backend/catalog/pg_operator.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ OperatorShellMake(const char *operatorName,
256256
values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid);
257257
values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
258258
values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
259+
values[Anum_pg_operator_oprstat - 1] = ObjectIdGetDatum(InvalidOid);
259260

260261
/*
261262
* create a new operator tuple
@@ -301,6 +302,7 @@ OperatorShellMake(const char *operatorName,
301302
* negatorName X negator operator
302303
* restrictionId X restriction selectivity procedure ID
303304
* joinId X join selectivity procedure ID
305+
* statsId X statistics derivation procedure ID
304306
* canMerge merge join can be used with this operator
305307
* canHash hash join can be used with this operator
306308
*
@@ -333,6 +335,7 @@ OperatorCreate(const char *operatorName,
333335
List *negatorName,
334336
Oid restrictionId,
335337
Oid joinId,
338+
Oid statsId,
336339
bool canMerge,
337340
bool canHash)
338341
{
@@ -505,6 +508,7 @@ OperatorCreate(const char *operatorName,
505508
values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId);
506509
values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId);
507510
values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId);
511+
values[Anum_pg_operator_oprstat - 1] = ObjectIdGetDatum(statsId);
508512

509513
pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
510514

@@ -855,6 +859,13 @@ makeOperatorDependencies(HeapTuple tuple,
855859
add_exact_object_address(&referenced, addrs);
856860
}
857861

862+
/* Dependency on statistics derivation function */
863+
if (OidIsValid(oper->oprstat))
864+
{
865+
ObjectAddressSet(referenced, ProcedureRelationId, oper->oprstat);
866+
add_exact_object_address(&referenced, addrs);
867+
}
868+
858869
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
859870
free_object_addresses(addrs);
860871

src/backend/commands/operatorcmds.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@
5353
static Oid ValidateRestrictionEstimator(List *restrictionName);
5454
static Oid ValidateJoinEstimator(List *joinName);
5555

56+
/*
57+
* XXX Maybe not the right name, because "estimator" implies we're calculating
58+
* selectivity. But we're actually deriving statistics for an expression.
59+
*/
60+
static Oid ValidateStatisticsEstimator(List *joinName);
61+
5662
/*
5763
* DefineOperator
5864
* this function extracts all the information from the
@@ -78,10 +84,12 @@ DefineOperator(List *names, List *parameters)
7884
List *commutatorName = NIL; /* optional commutator operator name */
7985
List *negatorName = NIL; /* optional negator operator name */
8086
List *restrictionName = NIL; /* optional restrict. sel. function */
87+
List *statisticsName = NIL; /* optional stats estimat. procedure */
8188
List *joinName = NIL; /* optional join sel. function */
8289
Oid functionOid; /* functions converted to OID */
8390
Oid restrictionOid;
8491
Oid joinOid;
92+
Oid statisticsOid;
8593
Oid typeId[2]; /* to hold left and right arg */
8694
int nargs;
8795
ListCell *pl;
@@ -131,6 +139,9 @@ DefineOperator(List *names, List *parameters)
131139
restrictionName = defGetQualifiedName(defel);
132140
else if (strcmp(defel->defname, "join") == 0)
133141
joinName = defGetQualifiedName(defel);
142+
/* XXX perhaps full "statistics" wording would be better */
143+
else if (strcmp(defel->defname, "stats") == 0)
144+
statisticsName = defGetQualifiedName(defel);
134145
else if (strcmp(defel->defname, "hashes") == 0)
135146
canHash = defGetBoolean(defel);
136147
else if (strcmp(defel->defname, "merges") == 0)
@@ -246,6 +257,10 @@ DefineOperator(List *names, List *parameters)
246257
joinOid = ValidateJoinEstimator(joinName);
247258
else
248259
joinOid = InvalidOid;
260+
if (statisticsName)
261+
statisticsOid = ValidateStatisticsEstimator(statisticsName);
262+
else
263+
statisticsOid = InvalidOid;
249264

250265
/*
251266
* now have OperatorCreate do all the work..
@@ -260,6 +275,7 @@ DefineOperator(List *names, List *parameters)
260275
negatorName, /* optional negator operator name */
261276
restrictionOid, /* optional restrict. sel. function */
262277
joinOid, /* optional join sel. function name */
278+
statisticsOid, /* optional stats estimation procedure */
263279
canMerge, /* operator merges */
264280
canHash); /* operator hashes */
265281
}
@@ -357,6 +373,40 @@ ValidateJoinEstimator(List *joinName)
357373
return joinOid;
358374
}
359375

376+
/*
377+
* Look up a statistics estimator function by name, and verify that it has the
378+
* correct signature and we have the permissions to attach it to an operator.
379+
*/
380+
static Oid
381+
ValidateStatisticsEstimator(List *statName)
382+
{
383+
Oid typeId[4];
384+
Oid statOid;
385+
AclResult aclresult;
386+
387+
typeId[0] = INTERNALOID; /* PlannerInfo */
388+
typeId[1] = INTERNALOID; /* OpExpr */
389+
typeId[2] = INT4OID; /* varRelid */
390+
typeId[3] = INTERNALOID; /* VariableStatData */
391+
392+
statOid = LookupFuncName(statName, 4, typeId, false);
393+
394+
/* statistics estimators must return void */
395+
if (get_func_rettype(statOid) != BOOLOID)
396+
ereport(ERROR,
397+
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
398+
errmsg("statistics estimator function %s must return type %s",
399+
NameListToString(statName), "boolean")));
400+
401+
/* Require EXECUTE rights for the estimator */
402+
aclresult = pg_proc_aclcheck(statOid, GetUserId(), ACL_EXECUTE);
403+
if (aclresult != ACLCHECK_OK)
404+
aclcheck_error(aclresult, OBJECT_FUNCTION,
405+
NameListToString(statName));
406+
407+
return statOid;
408+
}
409+
360410
/*
361411
* Guts of operator deletion.
362412
*/
@@ -424,6 +474,9 @@ AlterOperator(AlterOperatorStmt *stmt)
424474
List *joinName = NIL; /* optional join sel. function */
425475
bool updateJoin = false;
426476
Oid joinOid;
477+
List *statName = NIL; /* optional statistics estimation procedure */
478+
bool updateStat = false;
479+
Oid statOid;
427480

428481
/* Look up the operator */
429482
oprId = LookupOperWithArgs(stmt->opername, false);
@@ -454,6 +507,11 @@ AlterOperator(AlterOperatorStmt *stmt)
454507
joinName = param;
455508
updateJoin = true;
456509
}
510+
else if (pg_strcasecmp(defel->defname, "stats") == 0)
511+
{
512+
statName = param;
513+
updateStat = true;
514+
}
457515

458516
/*
459517
* The rest of the options that CREATE accepts cannot be changed.
@@ -496,6 +554,10 @@ AlterOperator(AlterOperatorStmt *stmt)
496554
joinOid = ValidateJoinEstimator(joinName);
497555
else
498556
joinOid = InvalidOid;
557+
if (statName)
558+
statOid = ValidateStatisticsEstimator(statName);
559+
else
560+
statOid = InvalidOid;
499561

500562
/* Perform additional checks, like OperatorCreate does */
501563
if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
@@ -536,6 +598,11 @@ AlterOperator(AlterOperatorStmt *stmt)
536598
replaces[Anum_pg_operator_oprjoin - 1] = true;
537599
values[Anum_pg_operator_oprjoin - 1] = joinOid;
538600
}
601+
if (updateStat)
602+
{
603+
replaces[Anum_pg_operator_oprstat - 1] = true;
604+
values[Anum_pg_operator_oprstat - 1] = statOid;
605+
}
539606

540607
tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
541608
values, nulls, replaces);

src/backend/utils/adt/selfuncs.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4917,6 +4917,32 @@ ReleaseDummy(HeapTuple tuple)
49174917
pfree(tuple);
49184918
}
49194919

4920+
/*
4921+
* examine_operator_expression
4922+
* Try to derive optimizer statistics for the operator expression using
4923+
* operator's oprstat function.
4924+
*
4925+
* XXX Not sure why this returns bool - we ignore the return value anyway. We
4926+
* might as well return the calculated vardata (or NULL).
4927+
*
4928+
* XXX Not sure what to do about recursion - there can be another OpExpr in
4929+
* one of the arguments, and we should call this recursively from the oprstat
4930+
* procedure. But that's not possible, because it's marked as static.
4931+
*/
4932+
static bool
4933+
examine_operator_expression(PlannerInfo *root, OpExpr *opexpr, int varRelid,
4934+
VariableStatData *vardata)
4935+
{
4936+
RegProcedure oprstat = get_oprstat(opexpr->opno);
4937+
4938+
return OidIsValid(oprstat) &&
4939+
DatumGetBool(OidFunctionCall4(oprstat,
4940+
PointerGetDatum(root),
4941+
PointerGetDatum(opexpr),
4942+
Int32GetDatum(varRelid),
4943+
PointerGetDatum(vardata)));
4944+
}
4945+
49204946
/*
49214947
* examine_variable
49224948
* Try to look up statistical data about an expression.
@@ -5332,6 +5358,19 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
53325358
pos++;
53335359
}
53345360
}
5361+
5362+
/*
5363+
* If there's no index or extended statistics matching the expression,
5364+
* try deriving the statistics from statistics on arguments of the
5365+
* operator expression (OpExpr). We do this last because it may be quite
5366+
* expensive, and it's unclear how accurate the statistics will be.
5367+
*
5368+
* XXX Shouldn't this put more restrictions on the OpExpr? E.g. that
5369+
* one of the arguments has to be a Const or something?
5370+
*/
5371+
if (!vardata->statsTuple && IsA(basenode, OpExpr))
5372+
examine_operator_expression(root, (OpExpr *) basenode, varRelid,
5373+
vardata);
53355374
}
53365375
}
53375376

src/backend/utils/cache/lsyscache.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,30 @@ get_oprjoin(Oid opno)
15671567
return (RegProcedure) InvalidOid;
15681568
}
15691569

1570+
/*
1571+
* get_oprstat
1572+
*
1573+
* Returns procedure id for estimating statistics for an operator.
1574+
*/
1575+
RegProcedure
1576+
get_oprstat(Oid opno)
1577+
{
1578+
HeapTuple tp;
1579+
1580+
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
1581+
if (HeapTupleIsValid(tp))
1582+
{
1583+
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
1584+
RegProcedure result;
1585+
1586+
result = optup->oprstat;
1587+
ReleaseSysCache(tp);
1588+
return result;
1589+
}
1590+
else
1591+
return (RegProcedure) InvalidOid;
1592+
}
1593+
15701594
/* ---------- FUNCTION CACHE ---------- */
15711595

15721596
/*

src/include/catalog/pg_operator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ CATALOG(pg_operator,2617,OperatorRelationId)
7373

7474
/* OID of join estimator, or 0 */
7575
regproc oprjoin BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
76+
77+
/* OID of statistics estimator, or 0 */
78+
regproc oprstat BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
7679
} FormData_pg_operator;
7780

7881
/* ----------------
@@ -95,6 +98,7 @@ extern ObjectAddress OperatorCreate(const char *operatorName,
9598
List *negatorName,
9699
Oid restrictionId,
97100
Oid joinId,
101+
Oid statisticsId,
98102
bool canMerge,
99103
bool canHash);
100104

src/include/utils/lsyscache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ extern Oid get_commutator(Oid opno);
118118
extern Oid get_negator(Oid opno);
119119
extern RegProcedure get_oprrest(Oid opno);
120120
extern RegProcedure get_oprjoin(Oid opno);
121+
extern RegProcedure get_oprstat(Oid opno);
121122
extern char *get_func_name(Oid funcid);
122123
extern Oid get_func_namespace(Oid funcid);
123124
extern Oid get_func_rettype(Oid funcid);

src/test/regress/expected/oidjoins.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ NOTICE: checking pg_operator {oprnegate} => pg_operator {oid}
113113
NOTICE: checking pg_operator {oprcode} => pg_proc {oid}
114114
NOTICE: checking pg_operator {oprrest} => pg_proc {oid}
115115
NOTICE: checking pg_operator {oprjoin} => pg_proc {oid}
116+
NOTICE: checking pg_operator {oprstat} => pg_proc {oid}
116117
NOTICE: checking pg_opfamily {opfmethod} => pg_am {oid}
117118
NOTICE: checking pg_opfamily {opfnamespace} => pg_namespace {oid}
118119
NOTICE: checking pg_opfamily {opfowner} => pg_authid {oid}

0 commit comments

Comments
 (0)