1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-14 18:42:34 +03:00

Create the infrastructure for planner support functions.

Rename/repurpose pg_proc.protransform as "prosupport".  The idea is
still that it names an internal function that provides knowledge to
the planner about the behavior of the function it's attached to;
but redesign the API specification so that it's not limited to doing
just one thing, but can support an extensible set of requests.

The original purpose of simplifying a function call is handled by
the first request type to be invented, SupportRequestSimplify.
Adjust all the existing transform functions to handle this API,
and rename them fron "xxx_transform" to "xxx_support" to reflect
the potential generalization of what they do.  (Since we never
previously provided any way for extensions to add transform functions,
this change doesn't create an API break for them.)

Also add DDL and pg_dump support for attaching a support function to a
user-defined function.  Unfortunately, DDL access has to be restricted
to superusers, at least for now; but seeing that support functions
will pretty much have to be written in C, that limitation is just
theoretical.  (This support is untested in this patch, but a follow-on
patch will add cases that exercise it.)

Discussion: https://postgr.es/m/15193.1548028093@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2019-02-09 18:08:48 -05:00
parent 1a8d5afb0d
commit 1fb57af920
41 changed files with 698 additions and 294 deletions

View File

@ -479,6 +479,7 @@ compute_common_attribute(ParseState *pstate,
List **set_items,
DefElem **cost_item,
DefElem **rows_item,
DefElem **support_item,
DefElem **parallel_item)
{
if (strcmp(defel->defname, "volatility") == 0)
@ -537,6 +538,15 @@ compute_common_attribute(ParseState *pstate,
*rows_item = defel;
}
else if (strcmp(defel->defname, "support") == 0)
{
if (is_procedure)
goto procedure_error;
if (*support_item)
goto duplicate_error;
*support_item = defel;
}
else if (strcmp(defel->defname, "parallel") == 0)
{
if (is_procedure)
@ -635,6 +645,45 @@ update_proconfig_value(ArrayType *a, List *set_items)
return a;
}
static Oid
interpret_func_support(DefElem *defel)
{
List *procName = defGetQualifiedName(defel);
Oid procOid;
Oid argList[1];
/*
* Support functions always take one INTERNAL argument and return
* INTERNAL.
*/
argList[0] = INTERNALOID;
procOid = LookupFuncName(procName, 1, argList, true);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procName, 1, NIL, argList))));
if (get_func_rettype(procOid) != INTERNALOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("support function %s must return type %s",
NameListToString(procName), "internal")));
/*
* Someday we might want an ACL check here; but for now, we insist that
* you be superuser to specify a support function, so privilege on the
* support function is moot.
*/
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to specify a support function")));
return procOid;
}
/*
* Dissect the list of options assembled in gram.y into function
@ -655,6 +704,7 @@ compute_function_attributes(ParseState *pstate,
ArrayType **proconfig,
float4 *procost,
float4 *prorows,
Oid *prosupport,
char *parallel_p)
{
ListCell *option;
@ -669,6 +719,7 @@ compute_function_attributes(ParseState *pstate,
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
DefElem *support_item = NULL;
DefElem *parallel_item = NULL;
foreach(option, options)
@ -726,6 +777,7 @@ compute_function_attributes(ParseState *pstate,
&set_items,
&cost_item,
&rows_item,
&support_item,
&parallel_item))
{
/* recognized common option */
@ -788,6 +840,8 @@ compute_function_attributes(ParseState *pstate,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS must be positive")));
}
if (support_item)
*prosupport = interpret_func_support(support_item);
if (parallel_item)
*parallel_p = interpret_func_parallel(parallel_item);
}
@ -893,6 +947,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
ArrayType *proconfig;
float4 procost;
float4 prorows;
Oid prosupport;
HeapTuple languageTuple;
Form_pg_language languageStruct;
List *as_clause;
@ -917,6 +972,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
proconfig = NULL;
procost = -1; /* indicates not set */
prorows = -1; /* indicates not set */
prosupport = InvalidOid;
parallel = PROPARALLEL_UNSAFE;
/* Extract non-default attributes from stmt->options list */
@ -926,7 +982,8 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
&as_clause, &language, &transformDefElem,
&isWindowFunc, &volatility,
&isStrict, &security, &isLeakProof,
&proconfig, &procost, &prorows, &parallel);
&proconfig, &procost, &prorows,
&prosupport, &parallel);
/* Look up the language and validate permissions */
languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
@ -1113,6 +1170,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
parameterDefaults,
PointerGetDatum(trftypes),
PointerGetDatum(proconfig),
prosupport,
procost,
prorows);
}
@ -1187,6 +1245,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
DefElem *support_item = NULL;
DefElem *parallel_item = NULL;
ObjectAddress address;
@ -1194,6 +1253,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
ObjectAddressSet(address, ProcedureRelationId, funcOid);
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
@ -1228,6 +1289,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
&set_items,
&cost_item,
&rows_item,
&support_item,
&parallel_item) == false)
elog(ERROR, "option \"%s\" not recognized", defel->defname);
}
@ -1266,6 +1328,28 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS is not applicable when function does not return a set")));
}
if (support_item)
{
/* interpret_func_support handles the privilege check */
Oid newsupport = interpret_func_support(support_item);
/* Add or replace dependency on support function */
if (OidIsValid(procForm->prosupport))
changeDependencyFor(ProcedureRelationId, funcOid,
ProcedureRelationId, procForm->prosupport,
newsupport);
else
{
ObjectAddress referenced;
referenced.classId = ProcedureRelationId;
referenced.objectId = newsupport;
referenced.objectSubId = 0;
recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL);
}
procForm->prosupport = newsupport;
}
if (set_items)
{
Datum datum;
@ -1308,8 +1392,6 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0);
ObjectAddressSet(address, ProcedureRelationId, funcOid);
table_close(rel, NoLock);
heap_freetuple(tup);