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:
Tom Lane 2007-01-22 01:35:23 +00:00
parent a85e9c61e5
commit 5a7471c307
27 changed files with 2550 additions and 2090 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.142 2007/01/20 23:13:01 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.143 2007/01/22 01:35:19 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
@ -3375,6 +3375,22 @@
<entry>Implementation language or call interface of this function</entry>
</row>
<row>
<entry><structfield>procost</structfield></entry>
<entry><type>float4</type></entry>
<entry></entry>
<entry>Estimated execution cost (in units of
<xref linkend="guc-cpu-operator-cost">); if <structfield>proretset</>,
this is cost per row returned</entry>
</row>
<row>
<entry><structfield>prorows</structfield></entry>
<entry><type>float4</type></entry>
<entry></entry>
<entry>Estimated number of result rows (zero if not <structfield>proretset</>)</entry>
</row>
<row>
<entry><structfield>proisagg</structfield></entry>
<entry><type>bool</type></entry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.12 2006/09/16 00:30:16 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.13 2007/01/22 01:35:19 tgl Exp $
PostgreSQL documentation
-->
@ -34,6 +34,8 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
IMMUTABLE | STABLE | VOLATILE
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
COST <replaceable class="parameter">execution_cost</replaceable>
ROWS <replaceable class="parameter">result_rows</replaceable>
</synopsis>
</refsynopsisdiv>
@ -186,6 +188,30 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</listitem>
</varlistentry>
<varlistentry>
<term><literal>COST</literal> <replaceable class="parameter">execution_cost</replaceable></term>
<listitem>
<para>
Change the estimated execution cost of the function.
See <xref linkend="sql-createfunction"
endterm="sql-createfunction-title"> for more information.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ROWS</literal> <replaceable class="parameter">result_rows</replaceable></term>
<listitem>
<para>
Change the estimated number of rows returned by a set-returning
function. See <xref linkend="sql-createfunction"
endterm="sql-createfunction-title"> for more information.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>RESTRICT</literal></term>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.70 2006/11/10 20:52:18 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.71 2007/01/22 01:35:19 tgl Exp $
-->
<refentry id="SQL-CREATEFUNCTION">
@ -26,6 +26,8 @@ CREATE [ OR REPLACE ] FUNCTION
| IMMUTABLE | STABLE | VOLATILE
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
| [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
| COST <replaceable class="parameter">execution_cost</replaceable>
| ROWS <replaceable class="parameter">result_rows</replaceable>
| AS '<replaceable class="parameter">definition</replaceable>'
| AS '<replaceable class="parameter">obj_file</replaceable>', '<replaceable class="parameter">link_symbol</replaceable>'
} ...
@ -52,7 +54,7 @@ CREATE [ OR REPLACE ] FUNCTION
</para>
<para>
To update the definition of an existing function, use
To replace the current definition of an existing function, use
<command>CREATE OR REPLACE FUNCTION</command>. It is not possible
to change the name or argument types of a function this way (if you
tried, you would actually be creating a new, distinct function).
@ -289,6 +291,35 @@ CREATE [ OR REPLACE ] FUNCTION
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">execution_cost</replaceable></term>
<listitem>
<para>
A positive number giving the estimated execution cost for the function,
in units of <xref linkend="guc-cpu-operator-cost">. If the function
returns a set, this is the cost per returned row. If the cost is
not specified, 1 unit is assumed for C-language and internal functions,
and 100 units for functions in all other languages. Larger values
cause the planner to try to avoid evaluating the function more often
than necessary.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">result_rows</replaceable></term>
<listitem>
<para>
A positive number giving the estimated number of rows that the planner
should expect the function to return. This is only allowed when the
function is declared to return a set. The default assumption is
1000 rows.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">definition</replaceable></term>
@ -400,7 +431,8 @@ CREATE FUNCTION foo(int, out text) ...
<para>
When repeated <command>CREATE FUNCTION</command> calls refer to
the same object file, the file is only loaded once. To unload and
the same object file, the file is only loaded once per session.
To unload and
reload the file (perhaps during development), use the <xref
linkend="sql-load" endterm="sql-load-title"> command.
</para>

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.228 2007/01/05 22:19:24 momjian Exp $
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.229 2007/01/22 01:35:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -115,16 +115,18 @@ static const struct typinfo TypInfo[] = {
F_BYTEAIN, F_BYTEAOUT},
{"char", CHAROID, 0, 1, true, 'c', 'p',
F_CHARIN, F_CHAROUT},
{"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'i', 'p',
F_NAMEIN, F_NAMEOUT},
{"int2", INT2OID, 0, 2, true, 's', 'p',
F_INT2IN, F_INT2OUT},
{"int4", INT4OID, 0, 4, true, 'i', 'p',
F_INT4IN, F_INT4OUT},
{"regproc", REGPROCOID, 0, 4, true, 'i', 'p',
F_REGPROCIN, F_REGPROCOUT},
{"float4", FLOAT4OID, 0, 4, false, 'i', 'p',
F_FLOAT4IN, F_FLOAT4OUT},
{"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'i', 'p',
F_NAMEIN, F_NAMEOUT},
{"regclass", REGCLASSOID, 0, 4, true, 'i', 'p',
F_REGCLASSIN, F_REGCLASSOUT},
{"regproc", REGPROCOID, 0, 4, true, 'i', 'p',
F_REGPROCIN, F_REGPROCOUT},
{"regtype", REGTYPEOID, 0, 4, true, 'i', 'p',
F_REGTYPEIN, F_REGTYPEOUT},
{"text", TEXTOID, 0, -1, false, 'i', 'x',

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.84 2007/01/05 22:19:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.85 2007/01/22 01:35:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -214,7 +214,9 @@ AggregateCreate(const char *aggName,
numArgs), /* paramTypes */
PointerGetDatum(NULL), /* allParamTypes */
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL)); /* parameterNames */
PointerGetDatum(NULL), /* parameterNames */
1, /* procost */
0); /* prorows */
/*
* Okay to create the pg_aggregate entry.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.142 2007/01/05 22:19:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.143 2007/01/22 01:35:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -71,7 +71,9 @@ ProcedureCreate(const char *procedureName,
oidvector *parameterTypes,
Datum allParameterTypes,
Datum parameterModes,
Datum parameterNames)
Datum parameterNames,
float4 procost,
float4 prorows)
{
Oid retval;
int parameterCount;
@ -219,6 +221,8 @@ ProcedureCreate(const char *procedureName,
values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);
values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(GetUserId());
values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);
values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);

View File

@ -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);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.70 2007/01/05 22:19:26 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.71 2007/01/22 01:35:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -130,7 +130,9 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
buildoidvector(funcargtypes, 0),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL));
PointerGetDatum(NULL),
1,
0);
}
/*
@ -160,7 +162,9 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
buildoidvector(funcargtypes, 1),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL));
PointerGetDatum(NULL),
1,
0);
}
}
else

View File

@ -54,7 +54,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.175 2007/01/20 20:45:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.176 2007/01/22 01:35:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -68,6 +68,7 @@
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
@ -842,17 +843,19 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
Cost startup_cost = 0;
Cost run_cost = 0;
Cost cpu_per_tuple;
RangeTblEntry *rte;
QualCost exprcost;
/* Should only be applied to base relations that are functions */
Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_FUNCTION);
rte = rt_fetch(baserel->relid, root->parse->rtable);
Assert(rte->rtekind == RTE_FUNCTION);
/*
* For now, estimate function's cost at one operator eval per function
* call. Someday we should revive the function cost estimate columns in
* pg_proc...
*/
cpu_per_tuple = cpu_operator_cost;
/* Estimate costs of executing the function expression */
cost_qual_eval_node(&exprcost, rte->funcexpr);
startup_cost += exprcost.startup;
cpu_per_tuple = exprcost.per_tuple;
/* Add scanning CPU costs */
startup_cost += baserel->baserestrictcost.startup;
@ -1068,6 +1071,10 @@ cost_agg(Path *path, PlannerInfo *root,
* there's roundoff error we might do the wrong thing. So be sure that
* the computations below form the same intermediate values in the same
* order.
*
* Note: ideally we should use the pg_proc.procost costs of each
* aggregate's component functions, but for now that seems like an
* excessive amount of work.
*/
if (aggstrategy == AGG_PLAIN)
{
@ -1694,7 +1701,8 @@ cost_hashjoin(HashPath *path, PlannerInfo *root)
* cost_qual_eval
* Estimate the CPU costs of evaluating a WHERE clause.
* The input can be either an implicitly-ANDed list of boolean
* expressions, or a list of RestrictInfo nodes.
* expressions, or a list of RestrictInfo nodes. (The latter is
* preferred since it allows caching of the results.)
* The result includes both a one-time (startup) component,
* and a per-evaluation component.
*/
@ -1712,43 +1720,22 @@ cost_qual_eval(QualCost *cost, List *quals)
{
Node *qual = (Node *) lfirst(l);
/*
* RestrictInfo nodes contain an eval_cost field reserved for this
* routine's use, so that it's not necessary to evaluate the qual
* clause's cost more than once. If the clause's cost hasn't been
* computed yet, the field's startup value will contain -1.
*
* If the RestrictInfo is marked pseudoconstant, it will be tested
* only once, so treat its cost as all startup cost.
*/
if (qual && IsA(qual, RestrictInfo))
{
RestrictInfo *rinfo = (RestrictInfo *) qual;
if (rinfo->eval_cost.startup < 0)
{
rinfo->eval_cost.startup = 0;
rinfo->eval_cost.per_tuple = 0;
cost_qual_eval_walker((Node *) rinfo->clause,
&rinfo->eval_cost);
if (rinfo->pseudoconstant)
{
/* count one execution during startup */
rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple;
rinfo->eval_cost.per_tuple = 0;
}
}
cost->startup += rinfo->eval_cost.startup;
cost->per_tuple += rinfo->eval_cost.per_tuple;
}
else
{
/* If it's a bare expression, must always do it the hard way */
cost_qual_eval_walker(qual, cost);
}
cost_qual_eval_walker(qual, cost);
}
}
/*
* cost_qual_eval_node
* As above, for a single RestrictInfo or expression.
*/
void
cost_qual_eval_node(QualCost *cost, Node *qual)
{
cost->startup = 0;
cost->per_tuple = 0;
cost_qual_eval_walker(qual, cost);
}
static bool
cost_qual_eval_walker(Node *node, QualCost *total)
{
@ -1756,19 +1743,75 @@ cost_qual_eval_walker(Node *node, QualCost *total)
return false;
/*
* Our basic strategy is to charge one cpu_operator_cost for each operator
* or function node in the given tree. Vars and Consts are charged zero,
* and so are boolean operators (AND, OR, NOT). Simplistic, but a lot
* better than no model at all.
* RestrictInfo nodes contain an eval_cost field reserved for this
* routine's use, so that it's not necessary to evaluate the qual
* clause's cost more than once. If the clause's cost hasn't been
* computed yet, the field's startup value will contain -1.
*/
if (IsA(node, RestrictInfo))
{
RestrictInfo *rinfo = (RestrictInfo *) node;
if (rinfo->eval_cost.startup < 0)
{
rinfo->eval_cost.startup = 0;
rinfo->eval_cost.per_tuple = 0;
/*
* For an OR clause, recurse into the marked-up tree so that
* we set the eval_cost for contained RestrictInfos too.
*/
if (rinfo->orclause)
cost_qual_eval_walker((Node *) rinfo->orclause,
&rinfo->eval_cost);
else
cost_qual_eval_walker((Node *) rinfo->clause,
&rinfo->eval_cost);
/*
* If the RestrictInfo is marked pseudoconstant, it will be tested
* only once, so treat its cost as all startup cost.
*/
if (rinfo->pseudoconstant)
{
/* count one execution during startup */
rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple;
rinfo->eval_cost.per_tuple = 0;
}
}
total->startup += rinfo->eval_cost.startup;
total->per_tuple += rinfo->eval_cost.per_tuple;
/* do NOT recurse into children */
return false;
}
/*
* For each operator or function node in the given tree, we charge the
* estimated execution cost given by pg_proc.procost (remember to
* multiply this by cpu_operator_cost).
*
* Vars and Consts are charged zero, and so are boolean operators (AND,
* OR, NOT). Simplistic, but a lot better than no model at all.
*
* Should we try to account for the possibility of short-circuit
* evaluation of AND/OR?
* evaluation of AND/OR? Probably *not*, because that would make the
* results depend on the clause ordering, and we are not in any position
* to expect that the current ordering of the clauses is the one that's
* going to end up being used. (Is it worth applying order_qual_clauses
* much earlier in the planning process to fix this?)
*/
if (IsA(node, FuncExpr) ||
IsA(node, OpExpr) ||
IsA(node, DistinctExpr) ||
IsA(node, NullIfExpr))
total->per_tuple += cpu_operator_cost;
if (IsA(node, FuncExpr))
{
total->per_tuple += get_func_cost(((FuncExpr *) node)->funcid) *
cpu_operator_cost;
}
else if (IsA(node, OpExpr) ||
IsA(node, DistinctExpr) ||
IsA(node, NullIfExpr))
{
/* rely on struct equivalence to treat these all alike */
set_opfuncid((OpExpr *) node);
total->per_tuple += get_func_cost(((OpExpr *) node)->opfuncid) *
cpu_operator_cost;
}
else if (IsA(node, ScalarArrayOpExpr))
{
/*
@ -1778,15 +1821,23 @@ cost_qual_eval_walker(Node *node, QualCost *total)
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
Node *arraynode = (Node *) lsecond(saop->args);
total->per_tuple +=
set_sa_opfuncid(saop);
total->per_tuple += get_func_cost(saop->opfuncid) *
cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
}
else if (IsA(node, RowCompareExpr))
{
/* Conservatively assume we will check all the columns */
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
ListCell *lc;
total->per_tuple += cpu_operator_cost * list_length(rcexpr->opnos);
foreach(lc, rcexpr->opnos)
{
Oid opid = lfirst_oid(lc);
total->per_tuple += get_func_cost(get_opcode(opid)) *
cpu_operator_cost;
}
}
else if (IsA(node, SubLink))
{
@ -1873,6 +1924,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
}
}
/* recurse into children */
return expression_tree_walker(node, cost_qual_eval_walker,
(void *) total);
}
@ -2172,16 +2224,8 @@ set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel)
rte = rt_fetch(rel->relid, root->parse->rtable);
Assert(rte->rtekind == RTE_FUNCTION);
/*
* Estimate number of rows the function itself will return.
*
* XXX no idea how to do this yet; but we can at least check whether
* function returns set or not...
*/
if (expression_returns_set(rte->funcexpr))
rel->tuples = 1000;
else
rel->tuples = 1;
/* Estimate number of rows the function itself will return */
rel->tuples = clamp_row_est(expression_returns_set_rows(rte->funcexpr));
/* Now estimate number of output rows, etc */
set_baserel_size_estimates(root, rel);

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.222 2007/01/20 20:45:39 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.223 2007/01/22 01:35:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -411,14 +411,15 @@ create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
{
List *pseudoconstants;
/* Sort into desirable execution order while still in RestrictInfo form */
quals = order_qual_clauses(root, quals);
/* Pull out any pseudoconstant quals from the RestrictInfo list */
pseudoconstants = extract_actual_clauses(quals, true);
if (!pseudoconstants)
return plan;
pseudoconstants = order_qual_clauses(root, pseudoconstants);
return (Plan *) make_result((List *) copyObject(plan->targetlist),
(Node *) pseudoconstants,
plan);
@ -553,6 +554,8 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path)
Assert(best_path->path.parent == NULL);
tlist = NIL;
/* best_path->quals is just bare clauses */
quals = order_qual_clauses(root, best_path->quals);
return make_result(tlist, (Node *) quals, NULL);
@ -810,12 +813,12 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_RELATION);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
scan_plan = make_seqscan(tlist,
scan_clauses,
scan_relid);
@ -916,10 +919,6 @@ create_indexscan_plan(PlannerInfo *root,
* predicate, but only in a plain SELECT; when scanning a target relation
* of UPDATE/DELETE/SELECT FOR UPDATE, we must leave such quals in the
* plan so that they'll be properly rechecked by EvalPlanQual testing.
*
* While at it, we strip off the RestrictInfos to produce a list of plain
* expressions (this loop replaces extract_actual_clauses used in the
* other routines in this file). We have to ignore pseudoconstants.
*/
qpqual = NIL;
foreach(l, scan_clauses)
@ -928,7 +927,7 @@ create_indexscan_plan(PlannerInfo *root,
Assert(IsA(rinfo, RestrictInfo));
if (rinfo->pseudoconstant)
continue;
continue; /* we may drop pseudoconstants here */
if (list_member_ptr(nonlossy_indexquals, rinfo))
continue;
if (!contain_mutable_functions((Node *) rinfo->clause))
@ -946,12 +945,15 @@ create_indexscan_plan(PlannerInfo *root,
continue;
}
}
qpqual = lappend(qpqual, rinfo->clause);
qpqual = lappend(qpqual, rinfo);
}
/* Sort clauses into best execution order */
qpqual = order_qual_clauses(root, qpqual);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
qpqual = extract_actual_clauses(qpqual, false);
/* Finally ready to build the plan node */
scan_plan = make_indexscan(tlist,
qpqual,
@ -1281,6 +1283,9 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
Assert(scan_relid > 0);
Assert(best_path->path.parent->rtekind == RTE_RELATION);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
@ -1293,9 +1298,6 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
ortidquals = list_make1(make_orclause(ortidquals));
scan_clauses = list_difference(scan_clauses, ortidquals);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
scan_plan = make_tidscan(tlist,
scan_clauses,
scan_relid,
@ -1322,12 +1324,12 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_SUBQUERY);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
@ -1354,12 +1356,12 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_FUNCTION);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid);
copy_path_costsize(&scan_plan->scan.plan, best_path);
@ -1383,12 +1385,12 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path,
Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_VALUES);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid);
copy_path_costsize(&scan_plan->scan.plan, best_path);
@ -1471,6 +1473,9 @@ create_nestloop_plan(PlannerInfo *root,
}
}
/* Sort join qual clauses into best execution order */
joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses);
/* Get the join qual clauses (in plain expression form) */
/* Any pseudoconstant clauses are ignored here */
if (IS_OUTER_JOIN(best_path->jointype))
@ -1485,10 +1490,6 @@ create_nestloop_plan(PlannerInfo *root,
otherclauses = NIL;
}
/* Sort clauses into best execution order */
joinclauses = order_qual_clauses(root, joinclauses);
otherclauses = order_qual_clauses(root, otherclauses);
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
@ -1527,18 +1528,21 @@ create_mergejoin_plan(PlannerInfo *root,
ListCell *lop;
ListCell *lip;
/* Sort join qual clauses into best execution order */
/* NB: do NOT reorder the mergeclauses */
joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
/* Get the join qual clauses (in plain expression form) */
/* Any pseudoconstant clauses are ignored here */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
extract_actual_join_clauses(best_path->jpath.joinrestrictinfo,
extract_actual_join_clauses(joinclauses,
&joinclauses, &otherclauses);
}
else
{
/* We can treat all clauses alike for an inner join */
joinclauses = extract_actual_clauses(best_path->jpath.joinrestrictinfo,
false);
joinclauses = extract_actual_clauses(joinclauses, false);
otherclauses = NIL;
}
@ -1557,11 +1561,6 @@ create_mergejoin_plan(PlannerInfo *root,
mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
best_path->jpath.outerjoinpath->parent->relids);
/* Sort clauses into best execution order */
/* NB: do NOT reorder the mergeclauses */
joinclauses = order_qual_clauses(root, joinclauses);
otherclauses = order_qual_clauses(root, otherclauses);
/*
* Create explicit sort nodes for the outer and inner join paths if
* necessary. The sort cost was already accounted for in the path. Make
@ -1699,18 +1698,21 @@ create_hashjoin_plan(PlannerInfo *root,
HashJoin *join_plan;
Hash *hash_plan;
/* Sort join qual clauses into best execution order */
joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
/* There's no point in sorting the hash clauses ... */
/* Get the join qual clauses (in plain expression form) */
/* Any pseudoconstant clauses are ignored here */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
extract_actual_join_clauses(best_path->jpath.joinrestrictinfo,
extract_actual_join_clauses(joinclauses,
&joinclauses, &otherclauses);
}
else
{
/* We can treat all clauses alike for an inner join */
joinclauses = extract_actual_clauses(best_path->jpath.joinrestrictinfo,
false);
joinclauses = extract_actual_clauses(joinclauses, false);
otherclauses = NIL;
}
@ -1728,11 +1730,6 @@ create_hashjoin_plan(PlannerInfo *root,
hashclauses = get_switched_clauses(best_path->path_hashclauses,
best_path->jpath.outerjoinpath->parent->relids);
/* Sort clauses into best execution order */
joinclauses = order_qual_clauses(root, joinclauses);
otherclauses = order_qual_clauses(root, otherclauses);
hashclauses = order_qual_clauses(root, hashclauses);
/* We don't want any excess columns in the hashed tuples */
disuse_physical_tlist(inner_plan, best_path->jpath.innerjoinpath);
@ -2065,35 +2062,82 @@ get_switched_clauses(List *clauses, Relids outerrelids)
* in at runtime.
*
* Ideally the order should be driven by a combination of execution cost and
* selectivity, but unfortunately we have so little information about
* execution cost of operators that it's really hard to do anything smart.
* For now, we just move any quals that contain SubPlan references (but not
* InitPlan references) to the end of the list.
* selectivity, but it's not immediately clear how to account for both,
* and given the uncertainty of the estimates the reliability of the decisions
* would be doubtful anyway. So we just order by estimated per-tuple cost,
* being careful not to change the order when (as is often the case) the
* estimates are identical.
*
* Although this will work on either bare clauses or RestrictInfos, it's
* much faster to apply it to RestrictInfos, since it can re-use cost
* information that is cached in RestrictInfos.
*
* Note: some callers pass lists that contain entries that will later be
* removed; this is the easiest way to let this routine see RestrictInfos
* instead of bare clauses. It's OK because we only sort by cost, but
* a cost/selectivity combination would likely do the wrong thing.
*/
static List *
order_qual_clauses(PlannerInfo *root, List *clauses)
{
List *nosubplans;
List *withsubplans;
ListCell *l;
typedef struct
{
Node *clause;
Cost cost;
} QualItem;
int nitems = list_length(clauses);
QualItem *items;
ListCell *lc;
int i;
List *result;
/* No need to work hard if the query is subselect-free */
if (!root->parse->hasSubLinks)
/* No need to work hard for 0 or 1 clause */
if (nitems <= 1)
return clauses;
nosubplans = NIL;
withsubplans = NIL;
foreach(l, clauses)
/*
* Collect the items and costs into an array. This is to avoid repeated
* cost_qual_eval work if the inputs aren't RestrictInfos.
*/
items = (QualItem *) palloc(nitems * sizeof(QualItem));
i = 0;
foreach(lc, clauses)
{
Node *clause = (Node *) lfirst(l);
Node *clause = (Node *) lfirst(lc);
QualCost qcost;
if (contain_subplans(clause))
withsubplans = lappend(withsubplans, clause);
else
nosubplans = lappend(nosubplans, clause);
cost_qual_eval_node(&qcost, clause);
items[i].clause = clause;
items[i].cost = qcost.per_tuple;
i++;
}
return list_concat(nosubplans, withsubplans);
/*
* Sort. We don't use qsort() because it's not guaranteed stable for
* equal keys. The expected number of entries is small enough that
* a simple insertion sort should be good enough.
*/
for (i = 1; i < nitems; i++)
{
QualItem newitem = items[i];
int j;
/* insert newitem into the already-sorted subarray */
for (j = i; j > 0; j--)
{
if (newitem.cost >= items[j-1].cost)
break;
items[j] = items[j-1];
}
items[j] = newitem;
}
/* Convert back to a list */
result = NIL;
for (i = 0; i < nitems; i++)
result = lappend(result, items[i].clause);
return result;
}
/*

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.127 2007/01/05 22:19:32 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.128 2007/01/22 01:35:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -83,7 +83,6 @@ static Node *replace_vars_with_subplan_refs(Node *node,
static Node *replace_vars_with_subplan_refs_mutator(Node *node,
replace_vars_with_subplan_refs_context *context);
static bool fix_opfuncids_walker(Node *node, void *context);
static void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
/*****************************************************************************
@ -1433,7 +1432,7 @@ set_opfuncid(OpExpr *opexpr)
* set_sa_opfuncid
* As above, for ScalarArrayOpExpr nodes.
*/
static void
void
set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
{
if (opexpr->opfuncid == InvalidOid)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.230 2007/01/17 17:25:52 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.231 2007/01/22 01:35:20 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -64,6 +64,7 @@ typedef struct
static bool contain_agg_clause_walker(Node *node, void *context);
static bool count_agg_clauses_walker(Node *node, AggClauseCounts *counts);
static bool expression_returns_set_walker(Node *node, void *context);
static bool expression_returns_set_rows_walker(Node *node, double *count);
static bool contain_subplans_walker(Node *node, void *context);
static bool contain_mutable_functions_walker(Node *node, void *context);
static bool contain_volatile_functions_walker(Node *node, void *context);
@ -566,6 +567,78 @@ expression_returns_set_walker(Node *node, void *context)
context);
}
/*
* expression_returns_set_rows
* Estimate the number of rows in a set result.
*
* We use the product of the rowcount estimates of all the functions in
* the given tree. The result is 1 if there are no set-returning functions.
*/
double
expression_returns_set_rows(Node *clause)
{
double result = 1;
(void) expression_returns_set_rows_walker(clause, &result);
return result;
}
static bool
expression_returns_set_rows_walker(Node *node, double *count)
{
if (node == NULL)
return false;
if (IsA(node, FuncExpr))
{
FuncExpr *expr = (FuncExpr *) node;
if (expr->funcretset)
*count *= get_func_rows(expr->funcid);
}
if (IsA(node, OpExpr))
{
OpExpr *expr = (OpExpr *) node;
if (expr->opretset)
{
set_opfuncid(expr);
*count *= get_func_rows(expr->opfuncid);
}
}
/* Avoid recursion for some cases that can't return a set */
if (IsA(node, Aggref))
return false;
if (IsA(node, DistinctExpr))
return false;
if (IsA(node, ScalarArrayOpExpr))
return false;
if (IsA(node, BoolExpr))
return false;
if (IsA(node, SubLink))
return false;
if (IsA(node, SubPlan))
return false;
if (IsA(node, ArrayExpr))
return false;
if (IsA(node, RowExpr))
return false;
if (IsA(node, RowCompareExpr))
return false;
if (IsA(node, CoalesceExpr))
return false;
if (IsA(node, MinMaxExpr))
return false;
if (IsA(node, XmlExpr))
return false;
if (IsA(node, NullIfExpr))
return false;
return expression_tree_walker(node, expression_returns_set_rows_walker,
(void *) count);
}
/*****************************************************************************
* Subplan clause manipulation
*****************************************************************************/

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.574 2007/01/14 13:11:53 petere Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.575 2007/01/22 01:35:21 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -370,7 +370,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
COMMITTED CONCURRENTLY CONNECTION CONSTRAINT CONSTRAINTS
CONTENT_P CONVERSION_P CONVERT COPY CREATE CREATEDB
CONTENT_P CONVERSION_P CONVERT COPY COST CREATE CREATEDB
CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME
CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@ -3958,6 +3958,14 @@ common_func_opt_item:
{
$$ = makeDefElem("security", (Node *)makeInteger(FALSE));
}
| COST NumericOnly
{
$$ = makeDefElem("cost", (Node *)$2);
}
| ROWS NumericOnly
{
$$ = makeDefElem("rows", (Node *)$2);
}
;
createfunc_opt_item:
@ -8564,6 +8572,7 @@ unreserved_keyword:
| CONTENT_P
| CONVERSION_P
| COPY
| COST
| CREATEDB
| CREATEROLE
| CREATEUSER

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.181 2007/01/09 02:14:14 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.182 2007/01/22 01:35:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -93,6 +93,7 @@ static const ScanKeyword ScanKeywords[] = {
{"conversion", CONVERSION_P},
{"convert", CONVERT},
{"copy", COPY},
{"cost", COST},
{"create", CREATE},
{"createdb", CREATEDB},
{"createrole", CREATEROLE},

View File

@ -9,7 +9,7 @@
#
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/utils/Gen_fmgrtab.sh,v 1.34 2007/01/05 22:19:39 momjian Exp $
# $PostgreSQL: pgsql/src/backend/utils/Gen_fmgrtab.sh,v 1.35 2007/01/22 01:35:21 tgl Exp $
#
#-------------------------------------------------------------------------
@ -72,6 +72,8 @@ trap 'echo "Caught signal." ; cleanup ; exit 1' 1 2 15
#
# Generate the file containing raw pg_proc tuple data
# (but only for "internal" language procedures...).
# Basically we strip off the DATA macro call, leaving procedure OID as $1
# and all the pg_proc field values as $2, $3, etc on each line.
#
# Note assumption here that prolang == $5 and INTERNALlanguageId == 12.
#
@ -202,15 +204,15 @@ FuNkYfMgRtAbStUfF
# may seem tedious, but avoid the temptation to write a quick x?y:z
# conditional expression instead. Not all awks have conditional expressions.
#
# Note assumptions here that prosrc == $(NF-2), pronargs == $11,
# proisstrict == $8, proretset == $9
# Note assumptions here that prosrc == $(NF-2), pronargs == $13,
# proisstrict == $10, proretset == $11
$AWK 'BEGIN {
Bool["t"] = "true"
Bool["f"] = "false"
}
{ printf (" { %d, \"%s\", %d, %s, %s, %s },\n"), \
$1, $(NF-2), $11, Bool[$8], Bool[$9], $(NF-2)
$1, $(NF-2), $13, Bool[$10], Bool[$11], $(NF-2)
}' $SORTEDFILE >> "$$-$TABLEFILE"
if [ $? -ne 0 ]; then

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.145 2007/01/21 00:57:15 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.146 2007/01/22 01:35:21 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@ -1295,6 +1295,48 @@ func_volatile(Oid funcid)
return result;
}
/*
* get_func_cost
* Given procedure id, return the function's procost field.
*/
float4
get_func_cost(Oid funcid)
{
HeapTuple tp;
float4 result;
tp = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", funcid);
result = ((Form_pg_proc) GETSTRUCT(tp))->procost;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_rows
* Given procedure id, return the function's prorows field.
*/
float4
get_func_rows(Oid funcid)
{
HeapTuple tp;
float4 result;
tp = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", funcid);
result = ((Form_pg_proc) GETSTRUCT(tp))->prorows;
ReleaseSysCache(tp);
return result;
}
/* ---------- RELATION CACHE ---------- */
/*

View File

@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.456 2007/01/05 22:19:48 momjian Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.457 2007/01/22 01:35:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -5837,6 +5837,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
char *provolatile;
char *proisstrict;
char *prosecdef;
char *procost;
char *prorows;
char *lanname;
char *rettypename;
int nallargs;
@ -5857,12 +5859,25 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
selectSourceSchema(finfo->dobj.namespace->dobj.name);
/* Fetch function-specific details */
if (g_fout->remoteVersion >= 80100)
if (g_fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, "
"proallargtypes, proargmodes, proargnames, "
"provolatile, proisstrict, prosecdef, "
"procost, prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
finfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 80100)
{
appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, "
"proallargtypes, proargmodes, proargnames, "
"provolatile, proisstrict, prosecdef, "
"0 as procost, 0 as prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
@ -5876,6 +5891,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"null as proargmodes, "
"proargnames, "
"provolatile, proisstrict, prosecdef, "
"0 as procost, 0 as prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
@ -5889,6 +5905,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"null as proargmodes, "
"null as proargnames, "
"provolatile, proisstrict, prosecdef, "
"0 as procost, 0 as prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
@ -5904,6 +5921,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"case when proiscachable then 'i' else 'v' end as provolatile, "
"proisstrict, "
"'f'::boolean as prosecdef, "
"0 as procost, 0 as prorows, "
"(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
"FROM pg_proc "
"WHERE oid = '%u'::oid",
@ -5919,6 +5937,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"case when proiscachable then 'i' else 'v' end as provolatile, "
"'f'::boolean as proisstrict, "
"'f'::boolean as prosecdef, "
"0 as procost, 0 as prorows, "
"(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
"FROM pg_proc "
"WHERE oid = '%u'::oid",
@ -5946,6 +5965,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
/*
@ -6072,6 +6093,30 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
if (prosecdef[0] == 't')
appendPQExpBuffer(q, " SECURITY DEFINER");
/*
* COST and ROWS are emitted only if present and not default, so as not to
* break backwards-compatibility of the dump without need. Keep this code
* in sync with the defaults in functioncmds.c.
*/
if (strcmp(procost, "0") != 0)
{
if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
{
/* default cost is 1 */
if (strcmp(procost, "1") != 0)
appendPQExpBuffer(q, " COST %s", procost);
}
else
{
/* default cost is 100 */
if (strcmp(procost, "100") != 0)
appendPQExpBuffer(q, " COST %s", procost);
}
}
if (proretset[0] == 't' &&
strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
appendPQExpBuffer(q, " ROWS %s", prorows);
appendPQExpBuffer(q, ";\n");
ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.375 2007/01/20 23:13:01 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.376 2007/01/22 01:35:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200701203
#define CATALOG_VERSION_NO 200701211
#endif

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.129 2007/01/09 02:14:15 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.130 2007/01/22 01:35:21 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -290,39 +290,43 @@ DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
{ 1255, {"pronamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"proowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"prolang"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"proisagg"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"prosecdef"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"proisstrict"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"proretset"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"provolatile"}, 18, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"pronargs"}, 21, -1, 2, 10, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
{ 1255, {"prorettype"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"proargtypes"}, 30, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"proallargtypes"}, 1028, -1, -1, 13, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"proargmodes"}, 1002, -1, -1, 14, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"proargnames"}, 1009, -1, -1, 15, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"prosrc"}, 25, -1, -1, 16, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"probin"}, 17, -1, -1, 17, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"proacl"}, 1034, -1, -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
{ 1255, {"procost"}, 700, -1, 4, 5, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"prorows"}, 700, -1, 4, 6, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"proisagg"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"prosecdef"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"proisstrict"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"proretset"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"provolatile"}, 18, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1255, {"pronargs"}, 21, -1, 2, 12, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
{ 1255, {"prorettype"}, 26, -1, 4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"proargtypes"}, 30, -1, -1, 14, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
{ 1255, {"proallargtypes"}, 1028, -1, -1, 15, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"proargmodes"}, 1002, -1, -1, 16, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"proargnames"}, 1009, -1, -1, 17, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"prosrc"}, 25, -1, -1, 18, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"probin"}, 17, -1, -1, 19, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 1255, {"proacl"}, 1034, -1, -1, 20, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
DATA(insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 proowner 26 -1 4 3 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 prolang 26 -1 4 4 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 proisagg 16 -1 1 5 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 prosecdef 16 -1 1 6 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 proisstrict 16 -1 1 7 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 proretset 16 -1 1 8 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 provolatile 18 -1 1 9 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 pronargs 21 -1 2 10 0 -1 -1 t p s t f f t 0));
DATA(insert ( 1255 prorettype 26 -1 4 11 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 proargtypes 30 -1 -1 12 1 -1 -1 f p i t f f t 0));
DATA(insert ( 1255 proallargtypes 1028 -1 -1 13 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 proargmodes 1002 -1 -1 14 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 proargnames 1009 -1 -1 15 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 prosrc 25 -1 -1 16 0 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 probin 17 -1 -1 17 0 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 proacl 1034 -1 -1 18 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 procost 700 -1 4 5 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1255 prorows 700 -1 4 6 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1255 proisagg 16 -1 1 7 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 prosecdef 16 -1 1 8 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 proisstrict 16 -1 1 9 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 proretset 16 -1 1 10 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 provolatile 18 -1 1 11 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1255 pronargs 21 -1 2 12 0 -1 -1 t p s t f f t 0));
DATA(insert ( 1255 prorettype 26 -1 4 13 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 proargtypes 30 -1 -1 14 1 -1 -1 f p i t f f t 0));
DATA(insert ( 1255 proallargtypes 1028 -1 -1 15 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 proargmodes 1002 -1 -1 16 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 proargnames 1009 -1 -1 17 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 prosrc 25 -1 -1 18 0 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 probin 17 -1 -1 19 0 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 proacl 1034 -1 -1 20 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1255 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
DATA(insert ( 1255 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.99 2007/01/05 22:19:52 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.100 2007/01/22 01:35:22 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -136,7 +136,7 @@ DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 25 0 0
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 0 0 0 t f f f 3 _null_ _null_ ));
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 20 0 0 0 0 0 t f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 27 0 0 0 0 0 t f f f 3 _null_ _null_ ));
DESCR("");

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.85 2007/01/05 22:19:56 momjian Exp $
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.86 2007/01/22 01:35:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -51,6 +51,7 @@ extern bool contain_agg_clause(Node *clause);
extern void count_agg_clauses(Node *clause, AggClauseCounts *counts);
extern bool expression_returns_set(Node *clause);
extern double expression_returns_set_rows(Node *clause);
extern bool contain_subplans(Node *clause);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.83 2007/01/05 22:19:56 momjian Exp $
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.84 2007/01/22 01:35:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -89,6 +89,7 @@ extern void cost_nestloop(NestPath *path, PlannerInfo *root);
extern void cost_mergejoin(MergePath *path, PlannerInfo *root);
extern void cost_hashjoin(HashPath *path, PlannerInfo *root);
extern void cost_qual_eval(QualCost *cost, List *quals);
extern void cost_qual_eval_node(QualCost *cost, Node *qual);
extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel);
extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
RelOptInfo *outer_rel,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.98 2007/01/20 20:45:40 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.99 2007/01/22 01:35:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -97,5 +97,6 @@ extern List *set_returning_clause_references(List *rlist,
Index resultRelation);
extern void fix_opfuncids(Node *node);
extern void set_opfuncid(OpExpr *opexpr);
extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
#endif /* PLANMAIN_H */

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.114 2007/01/21 00:57:15 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.115 2007/01/22 01:35:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -76,6 +76,8 @@ extern Oid get_func_signature(Oid funcid, Oid **argtypes, int *nargs);
extern bool get_func_retset(Oid funcid);
extern bool func_strict(Oid funcid);
extern char func_volatile(Oid funcid);
extern float4 get_func_cost(Oid funcid);
extern float4 get_func_rows(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);

View File

@ -46,7 +46,9 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR
p1.pronargs < 0 OR
array_lower(p1.proargtypes, 1) != 0 OR
array_upper(p1.proargtypes, 1) != p1.pronargs-1 OR
0::oid = ANY (p1.proargtypes);
0::oid = ANY (p1.proargtypes) OR
procost <= 0 OR
CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END;
oid | proname
-----+---------
(0 rows)

View File

@ -53,7 +53,9 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR
p1.pronargs < 0 OR
array_lower(p1.proargtypes, 1) != 0 OR
array_upper(p1.proargtypes, 1) != p1.pronargs-1 OR
0::oid = ANY (p1.proargtypes);
0::oid = ANY (p1.proargtypes) OR
procost <= 0 OR
CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END;
-- Look for conflicting proc definitions (same names and input datatypes).
-- (This test should be dead code now that we have the unique index