mirror of
https://github.com/postgres/postgres.git
synced 2025-11-06 07:49:08 +03:00
When using extended-query protocol, postpone planning of unnamed statements
until Bind is received, so that actual parameter values are visible to the planner. Make use of the parameter values for estimation purposes (but don't fold them into the actual plan). This buys back most of the potential loss of plan quality that ensues from using out-of-line parameters instead of putting literal values right into the query text. This patch creates a notion of constant-folding expressions 'for estimation purposes only', in which case we can be more aggressive than the normal eval_const_expressions() logic can be. Right now the only difference in behavior is inserting bound values for Params, but it will be interesting to look at other possibilities. One that we've seen come up repeatedly is reducing now() and related functions to current values, so that queries like ... WHERE timestampcol > now() - '1 day' have some chance of being planned effectively. Oliver Jowett, with some kibitzing from Tom Lane.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.175 2004/06/09 19:08:16 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.176 2004/06/11 01:08:54 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/planner.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/analyze.h"
|
||||
#include "parser/parse_clause.h"
|
||||
@@ -41,6 +42,12 @@
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
List *active_fns;
|
||||
bool estimate;
|
||||
} eval_const_expressions_context;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int nargs;
|
||||
@@ -57,17 +64,20 @@ static bool contain_mutable_functions_walker(Node *node, void *context);
|
||||
static bool contain_volatile_functions_walker(Node *node, void *context);
|
||||
static bool contain_nonstrict_functions_walker(Node *node, void *context);
|
||||
static bool set_coercionform_dontcare_walker(Node *node, void *context);
|
||||
static Node *eval_const_expressions_mutator(Node *node, List *active_fns);
|
||||
static Node *eval_const_expressions_mutator(Node *node,
|
||||
eval_const_expressions_context *context);
|
||||
static List *simplify_or_arguments(List *args,
|
||||
bool *haveNull, bool *forceTrue);
|
||||
static List *simplify_and_arguments(List *args,
|
||||
bool *haveNull, bool *forceFalse);
|
||||
static Expr *simplify_function(Oid funcid, Oid result_type, List *args,
|
||||
bool allow_inline, List *active_fns);
|
||||
bool allow_inline,
|
||||
eval_const_expressions_context *context);
|
||||
static Expr *evaluate_function(Oid funcid, Oid result_type, List *args,
|
||||
HeapTuple func_tuple);
|
||||
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
|
||||
HeapTuple func_tuple, List *active_fns);
|
||||
HeapTuple func_tuple,
|
||||
eval_const_expressions_context *context);
|
||||
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
|
||||
int *usecounts);
|
||||
static Node *substitute_actual_parameters_mutator(Node *node,
|
||||
@@ -1070,18 +1080,101 @@ set_coercionform_dontcare_walker(Node *node, void *context)
|
||||
Node *
|
||||
eval_const_expressions(Node *node)
|
||||
{
|
||||
/*
|
||||
* The context for the mutator is a list of SQL functions being
|
||||
* recursively simplified, so we start with an empty list.
|
||||
*/
|
||||
return eval_const_expressions_mutator(node, NIL);
|
||||
eval_const_expressions_context context;
|
||||
|
||||
context.active_fns = NIL; /* nothing being recursively simplified */
|
||||
context.estimate = false; /* safe transformations only */
|
||||
return eval_const_expressions_mutator(node, &context);
|
||||
}
|
||||
|
||||
/*
|
||||
* estimate_expression_value
|
||||
*
|
||||
* This function attempts to estimate the value of an expression for
|
||||
* planning purposes. It is in essence a more aggressive version of
|
||||
* eval_const_expressions(): we will perform constant reductions that are
|
||||
* not necessarily 100% safe, but are reasonable for estimation purposes.
|
||||
*
|
||||
* Currently the only such transform is to substitute values for Params,
|
||||
* when a bound Param value has been made available by the caller of planner().
|
||||
* In future we might consider other things, such as reducing now() to current
|
||||
* time. (XXX seems like there could be a lot of scope for ideas here...
|
||||
* but we might need more volatility classifications ...)
|
||||
*/
|
||||
Node *
|
||||
estimate_expression_value(Node *node)
|
||||
{
|
||||
eval_const_expressions_context context;
|
||||
|
||||
context.active_fns = NIL; /* nothing being recursively simplified */
|
||||
context.estimate = true; /* unsafe transformations OK */
|
||||
return eval_const_expressions_mutator(node, &context);
|
||||
}
|
||||
|
||||
static Node *
|
||||
eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
eval_const_expressions_mutator(Node *node,
|
||||
eval_const_expressions_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
if (IsA(node, Param))
|
||||
{
|
||||
Param *param = (Param *) node;
|
||||
int thisParamKind = param->paramkind;
|
||||
|
||||
/* OK to try to substitute value? */
|
||||
if (context->estimate && thisParamKind != PARAM_EXEC &&
|
||||
PlannerBoundParamList != NULL)
|
||||
{
|
||||
ParamListInfo paramList = PlannerBoundParamList;
|
||||
bool matchFound = false;
|
||||
|
||||
/* Search to see if we've been given a value for this Param */
|
||||
while (paramList->kind != PARAM_INVALID && !matchFound)
|
||||
{
|
||||
if (thisParamKind == paramList->kind)
|
||||
{
|
||||
switch (thisParamKind)
|
||||
{
|
||||
case PARAM_NAMED:
|
||||
if (strcmp(paramList->name, param->paramname) == 0)
|
||||
matchFound = true;
|
||||
break;
|
||||
case PARAM_NUM:
|
||||
if (paramList->id == param->paramid)
|
||||
matchFound = true;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized paramkind: %d",
|
||||
thisParamKind);
|
||||
}
|
||||
}
|
||||
if (!matchFound)
|
||||
paramList++;
|
||||
}
|
||||
if (matchFound)
|
||||
{
|
||||
/*
|
||||
* Found it, so return a Const representing the param value.
|
||||
* Note that we don't copy pass-by-ref datatypes, so the
|
||||
* Const will only be valid as long as the bound parameter
|
||||
* list exists. This is okay for intended uses of
|
||||
* estimate_expression_value().
|
||||
*/
|
||||
int16 typLen;
|
||||
bool typByVal;
|
||||
|
||||
get_typlenbyval(param->paramtype, &typLen, &typByVal);
|
||||
return (Node *) makeConst(param->paramtype,
|
||||
(int) typLen,
|
||||
paramList->value,
|
||||
paramList->isnull,
|
||||
typByVal);
|
||||
}
|
||||
}
|
||||
/* Not replaceable, so just copy the Param (no need to recurse) */
|
||||
return (Node *) copyObject(param);
|
||||
}
|
||||
if (IsA(node, FuncExpr))
|
||||
{
|
||||
FuncExpr *expr = (FuncExpr *) node;
|
||||
@@ -1096,14 +1189,14 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
*/
|
||||
args = (List *) expression_tree_mutator((Node *) expr->args,
|
||||
eval_const_expressions_mutator,
|
||||
(void *) active_fns);
|
||||
(void *) context);
|
||||
|
||||
/*
|
||||
* Code for op/func reduction is pretty bulky, so split it out as
|
||||
* a separate function.
|
||||
*/
|
||||
simple = simplify_function(expr->funcid, expr->funcresulttype, args,
|
||||
true, active_fns);
|
||||
true, context);
|
||||
if (simple) /* successfully simplified it */
|
||||
return (Node *) simple;
|
||||
|
||||
@@ -1134,7 +1227,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
*/
|
||||
args = (List *) expression_tree_mutator((Node *) expr->args,
|
||||
eval_const_expressions_mutator,
|
||||
(void *) active_fns);
|
||||
(void *) context);
|
||||
|
||||
/*
|
||||
* Need to get OID of underlying function. Okay to scribble on
|
||||
@@ -1147,7 +1240,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
* a separate function.
|
||||
*/
|
||||
simple = simplify_function(expr->opfuncid, expr->opresulttype, args,
|
||||
true, active_fns);
|
||||
true, context);
|
||||
if (simple) /* successfully simplified it */
|
||||
return (Node *) simple;
|
||||
|
||||
@@ -1182,7 +1275,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
*/
|
||||
args = (List *) expression_tree_mutator((Node *) expr->args,
|
||||
eval_const_expressions_mutator,
|
||||
(void *) active_fns);
|
||||
(void *) context);
|
||||
|
||||
/*
|
||||
* We must do our own check for NULLs because DistinctExpr has
|
||||
@@ -1226,7 +1319,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
* as a separate function.
|
||||
*/
|
||||
simple = simplify_function(expr->opfuncid, expr->opresulttype,
|
||||
args, false, active_fns);
|
||||
args, false, context);
|
||||
if (simple) /* successfully simplified it */
|
||||
{
|
||||
/*
|
||||
@@ -1267,7 +1360,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
*/
|
||||
args = (List *) expression_tree_mutator((Node *) expr->args,
|
||||
eval_const_expressions_mutator,
|
||||
(void *) active_fns);
|
||||
(void *) context);
|
||||
|
||||
switch (expr->boolop)
|
||||
{
|
||||
@@ -1360,7 +1453,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
Node *arg;
|
||||
|
||||
arg = eval_const_expressions_mutator((Node *) relabel->arg,
|
||||
active_fns);
|
||||
context);
|
||||
|
||||
/*
|
||||
* If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
|
||||
@@ -1424,7 +1517,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
|
||||
/* Simplify the test expression, if any */
|
||||
newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
|
||||
active_fns);
|
||||
context);
|
||||
|
||||
/* Simplify the WHEN clauses */
|
||||
newargs = NIL;
|
||||
@@ -1434,7 +1527,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
CaseWhen *casewhen = (CaseWhen *)
|
||||
expression_tree_mutator((Node *) lfirst(arg),
|
||||
eval_const_expressions_mutator,
|
||||
(void *) active_fns);
|
||||
(void *) context);
|
||||
|
||||
Assert(IsA(casewhen, CaseWhen));
|
||||
if (casewhen->expr == NULL ||
|
||||
@@ -1464,7 +1557,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
|
||||
/* Simplify the default result */
|
||||
defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
|
||||
active_fns);
|
||||
context);
|
||||
|
||||
/*
|
||||
* If no non-FALSE alternatives, CASE reduces to the default
|
||||
@@ -1494,7 +1587,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
Node *e;
|
||||
|
||||
e = eval_const_expressions_mutator((Node *) lfirst(element),
|
||||
active_fns);
|
||||
context);
|
||||
if (!IsA(e, Const))
|
||||
all_const = false;
|
||||
newelems = lappend(newelems, e);
|
||||
@@ -1525,7 +1618,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
Node *e;
|
||||
|
||||
e = eval_const_expressions_mutator((Node *) lfirst(arg),
|
||||
active_fns);
|
||||
context);
|
||||
|
||||
/*
|
||||
* We can remove null constants from the list. For a non-null
|
||||
@@ -1561,7 +1654,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
Node *arg;
|
||||
|
||||
arg = eval_const_expressions_mutator((Node *) fselect->arg,
|
||||
active_fns);
|
||||
context);
|
||||
if (arg && IsA(arg, Var) &&
|
||||
((Var *) arg)->varattno == InvalidAttrNumber)
|
||||
{
|
||||
@@ -1595,7 +1688,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
* simplify constant expressions in its subscripts.
|
||||
*/
|
||||
return expression_tree_mutator(node, eval_const_expressions_mutator,
|
||||
(void *) active_fns);
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1726,14 +1819,15 @@ simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse)
|
||||
*
|
||||
* Inputs are the function OID, actual result type OID (which is needed for
|
||||
* polymorphic functions), and the pre-simplified argument list;
|
||||
* also a list of already-active inline function expansions.
|
||||
* also the context data for eval_const_expressions.
|
||||
*
|
||||
* Returns a simplified expression if successful, or NULL if cannot
|
||||
* simplify the function call.
|
||||
*/
|
||||
static Expr *
|
||||
simplify_function(Oid funcid, Oid result_type, List *args,
|
||||
bool allow_inline, List *active_fns)
|
||||
bool allow_inline,
|
||||
eval_const_expressions_context *context)
|
||||
{
|
||||
HeapTuple func_tuple;
|
||||
Expr *newexpr;
|
||||
@@ -1756,7 +1850,7 @@ simplify_function(Oid funcid, Oid result_type, List *args,
|
||||
|
||||
if (!newexpr && allow_inline)
|
||||
newexpr = inline_function(funcid, result_type, args,
|
||||
func_tuple, active_fns);
|
||||
func_tuple, context);
|
||||
|
||||
ReleaseSysCache(func_tuple);
|
||||
|
||||
@@ -1860,7 +1954,8 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
|
||||
*/
|
||||
static Expr *
|
||||
inline_function(Oid funcid, Oid result_type, List *args,
|
||||
HeapTuple func_tuple, List *active_fns)
|
||||
HeapTuple func_tuple,
|
||||
eval_const_expressions_context *context)
|
||||
{
|
||||
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||
bool polymorphic = false;
|
||||
@@ -1890,7 +1985,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
return NULL;
|
||||
|
||||
/* Check for recursive function, and give up trying to expand if so */
|
||||
if (list_member_oid(active_fns, funcid))
|
||||
if (list_member_oid(context->active_fns, funcid))
|
||||
return NULL;
|
||||
|
||||
/* Check permission to call function (fail later, if not) */
|
||||
@@ -2083,8 +2178,9 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
* Recursively try to simplify the modified expression. Here we must
|
||||
* add the current function to the context list of active functions.
|
||||
*/
|
||||
newexpr = eval_const_expressions_mutator(newexpr,
|
||||
lcons_oid(funcid, active_fns));
|
||||
context->active_fns = lcons_oid(funcid, context->active_fns);
|
||||
newexpr = eval_const_expressions_mutator(newexpr, context);
|
||||
context->active_fns = list_delete_first(context->active_fns);
|
||||
|
||||
error_context_stack = sqlerrcontext.previous;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user