mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
Add COST and ROWS options to CREATE/ALTER FUNCTION, plus underlying pg_proc
columns procost and prorows, to allow simple user adjustment of the estimated cost of a function call, as well as control of the estimated number of rows returned by a set-returning function. We might eventually wish to extend this to allow function-specific estimation routines, but there seems to be consensus that we should try a simple constant estimate first. In particular this provides a relatively simple way to control the order in which different WHERE clauses are applied in a plan node, which is a Good Thing in view of the fact that the recent EquivalenceClass planner rewrite made that much less predictable than before.
This commit is contained in:
@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.81 2007/01/05 22:19:26 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.82 2007/01/22 01:35:20 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* These routines take the parse tree and pick out the
|
||||
@ -273,7 +273,9 @@ static bool
|
||||
compute_common_attribute(DefElem *defel,
|
||||
DefElem **volatility_item,
|
||||
DefElem **strict_item,
|
||||
DefElem **security_item)
|
||||
DefElem **security_item,
|
||||
DefElem **cost_item,
|
||||
DefElem **rows_item)
|
||||
{
|
||||
if (strcmp(defel->defname, "volatility") == 0)
|
||||
{
|
||||
@ -296,6 +298,20 @@ compute_common_attribute(DefElem *defel,
|
||||
|
||||
*security_item = defel;
|
||||
}
|
||||
else if (strcmp(defel->defname, "cost") == 0)
|
||||
{
|
||||
if (*cost_item)
|
||||
goto duplicate_error;
|
||||
|
||||
*cost_item = defel;
|
||||
}
|
||||
else if (strcmp(defel->defname, "rows") == 0)
|
||||
{
|
||||
if (*rows_item)
|
||||
goto duplicate_error;
|
||||
|
||||
*rows_item = defel;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
@ -337,7 +353,9 @@ compute_attributes_sql_style(List *options,
|
||||
char **language,
|
||||
char *volatility_p,
|
||||
bool *strict_p,
|
||||
bool *security_definer)
|
||||
bool *security_definer,
|
||||
float4 *procost,
|
||||
float4 *prorows)
|
||||
{
|
||||
ListCell *option;
|
||||
DefElem *as_item = NULL;
|
||||
@ -345,6 +363,8 @@ compute_attributes_sql_style(List *options,
|
||||
DefElem *volatility_item = NULL;
|
||||
DefElem *strict_item = NULL;
|
||||
DefElem *security_item = NULL;
|
||||
DefElem *cost_item = NULL;
|
||||
DefElem *rows_item = NULL;
|
||||
|
||||
foreach(option, options)
|
||||
{
|
||||
@ -369,7 +389,9 @@ compute_attributes_sql_style(List *options,
|
||||
else if (compute_common_attribute(defel,
|
||||
&volatility_item,
|
||||
&strict_item,
|
||||
&security_item))
|
||||
&security_item,
|
||||
&cost_item,
|
||||
&rows_item))
|
||||
{
|
||||
/* recognized common option */
|
||||
continue;
|
||||
@ -407,6 +429,22 @@ compute_attributes_sql_style(List *options,
|
||||
*strict_p = intVal(strict_item->arg);
|
||||
if (security_item)
|
||||
*security_definer = intVal(security_item->arg);
|
||||
if (cost_item)
|
||||
{
|
||||
*procost = defGetNumeric(cost_item);
|
||||
if (*procost <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("COST must be positive")));
|
||||
}
|
||||
if (rows_item)
|
||||
{
|
||||
*prorows = defGetNumeric(rows_item);
|
||||
if (*prorows <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("ROWS must be positive")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -519,6 +557,8 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
bool isStrict,
|
||||
security;
|
||||
char volatility;
|
||||
float4 procost;
|
||||
float4 prorows;
|
||||
HeapTuple languageTuple;
|
||||
Form_pg_language languageStruct;
|
||||
List *as_clause;
|
||||
@ -537,10 +577,14 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
isStrict = false;
|
||||
security = false;
|
||||
volatility = PROVOLATILE_VOLATILE;
|
||||
procost = -1; /* indicates not set */
|
||||
prorows = -1; /* indicates not set */
|
||||
|
||||
/* override attributes from explicit list */
|
||||
compute_attributes_sql_style(stmt->options,
|
||||
&as_clause, &language, &volatility, &isStrict, &security);
|
||||
&as_clause, &language,
|
||||
&volatility, &isStrict, &security,
|
||||
&procost, &prorows);
|
||||
|
||||
/* Convert language name to canonical case */
|
||||
languageName = case_translate_language_name(language);
|
||||
@ -645,6 +689,32 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
prosrc_str = funcname;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set default values for COST and ROWS depending on other parameters;
|
||||
* reject ROWS if it's not returnsSet. NB: pg_dump knows these default
|
||||
* values, keep it in sync if you change them.
|
||||
*/
|
||||
if (procost < 0)
|
||||
{
|
||||
/* SQL and PL-language functions are assumed more expensive */
|
||||
if (languageOid == INTERNALlanguageId ||
|
||||
languageOid == ClanguageId)
|
||||
procost = 1;
|
||||
else
|
||||
procost = 100;
|
||||
}
|
||||
if (prorows < 0)
|
||||
{
|
||||
if (returnsSet)
|
||||
prorows = 1000;
|
||||
else
|
||||
prorows = 0; /* dummy value if not returnsSet */
|
||||
}
|
||||
else if (!returnsSet)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("ROWS is not applicable when function does not return a set")));
|
||||
|
||||
/*
|
||||
* And now that we have all the parameters, and know we're permitted to do
|
||||
* so, go ahead and create the function.
|
||||
@ -665,7 +735,9 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
parameterTypes,
|
||||
PointerGetDatum(allParameterTypes),
|
||||
PointerGetDatum(parameterModes),
|
||||
PointerGetDatum(parameterNames));
|
||||
PointerGetDatum(parameterNames),
|
||||
procost,
|
||||
prorows);
|
||||
}
|
||||
|
||||
|
||||
@ -1012,6 +1084,8 @@ AlterFunction(AlterFunctionStmt *stmt)
|
||||
DefElem *volatility_item = NULL;
|
||||
DefElem *strict_item = NULL;
|
||||
DefElem *security_def_item = NULL;
|
||||
DefElem *cost_item = NULL;
|
||||
DefElem *rows_item = NULL;
|
||||
|
||||
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
|
||||
|
||||
@ -1046,7 +1120,9 @@ AlterFunction(AlterFunctionStmt *stmt)
|
||||
if (compute_common_attribute(defel,
|
||||
&volatility_item,
|
||||
&strict_item,
|
||||
&security_def_item) == false)
|
||||
&security_def_item,
|
||||
&cost_item,
|
||||
&rows_item) == false)
|
||||
elog(ERROR, "option \"%s\" not recognized", defel->defname);
|
||||
}
|
||||
|
||||
@ -1056,6 +1132,26 @@ AlterFunction(AlterFunctionStmt *stmt)
|
||||
procForm->proisstrict = intVal(strict_item->arg);
|
||||
if (security_def_item)
|
||||
procForm->prosecdef = intVal(security_def_item->arg);
|
||||
if (cost_item)
|
||||
{
|
||||
procForm->procost = defGetNumeric(cost_item);
|
||||
if (procForm->procost <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("COST must be positive")));
|
||||
}
|
||||
if (rows_item)
|
||||
{
|
||||
procForm->prorows = defGetNumeric(rows_item);
|
||||
if (procForm->prorows <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("ROWS must be positive")));
|
||||
if (!procForm->proretset)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("ROWS is not applicable when function does not return a set")));
|
||||
}
|
||||
|
||||
/* Do the update */
|
||||
simple_heap_update(rel, &tup->t_self, tup);
|
||||
|
Reference in New Issue
Block a user