* clauses.c
* routines to manipulate qualification clauses
*
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.136 2003/04/29 22:13:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.227 2007/01/05 22:19:32 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
#include "postgres.h"
+#include "catalog/pg_aggregate.h"
#include "catalog/pg_language.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
+#include "executor/functions.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
#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"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
-/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
-#define MAKEBOOLCONST(val,isnull) \
- ((Node *) makeConst(BOOLOID, 1, (Datum) (val), (isnull), true))
+typedef struct
+{
+ List *active_fns;
+ Node *case_val;
+ bool estimate;
+} eval_const_expressions_context;
typedef struct
{
} substitute_actual_parameters_context;
static bool contain_agg_clause_walker(Node *node, void *context);
-static bool contain_distinct_agg_clause_walker(Node *node, void *context);
-static bool count_agg_clause_walker(Node *node, int *count);
+static bool count_agg_clauses_walker(Node *node, AggClauseCounts *counts);
static bool expression_returns_set_walker(Node *node, void *context);
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);
static bool contain_nonstrict_functions_walker(Node *node, void *context);
-static Node *eval_const_expressions_mutator(Node *node, List *active_fns);
+static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
+static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
+static bool set_coercionform_dontcare_walker(Node *node, void *context);
+static Node *eval_const_expressions_mutator(Node *node,
+ eval_const_expressions_context *context);
+static List *simplify_or_arguments(List *args,
+ eval_const_expressions_context *context,
+ bool *haveNull, bool *forceTrue);
+static List *simplify_and_arguments(List *args,
+ eval_const_expressions_context *context,
+ bool *haveNull, bool *forceFalse);
+static Expr *simplify_boolean_equality(List *args);
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);
+ HeapTuple func_tuple,
+ eval_const_expressions_context *context);
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);
+ int *usecounts);
static Node *substitute_actual_parameters_mutator(Node *node,
- substitute_actual_parameters_context *context);
+ substitute_actual_parameters_context *context);
+static void sql_inline_error_callback(void *arg);
static Expr *evaluate_expr(Expr *expr, Oid result_type);
expr->opresulttype = opresulttype;
expr->opretset = opretset;
if (rightop)
- expr->args = makeList2(leftop, rightop);
+ expr->args = list_make2(leftop, rightop);
else
- expr->args = makeList1(leftop);
+ expr->args = list_make1(leftop);
return (Expr *) expr;
}
Node *
get_leftop(Expr *clause)
{
- OpExpr *expr = (OpExpr *) clause;
+ OpExpr *expr = (OpExpr *) clause;
if (expr->args != NIL)
- return lfirst(expr->args);
+ return linitial(expr->args);
else
return NULL;
}
Node *
get_rightop(Expr *clause)
{
- OpExpr *expr = (OpExpr *) clause;
+ OpExpr *expr = (OpExpr *) clause;
- if (expr->args != NIL && lnext(expr->args) != NIL)
- return lfirst(lnext(expr->args));
+ if (list_length(expr->args) >= 2)
+ return lsecond(expr->args);
else
return NULL;
}
-/*****************************************************************************
- * FUNCTION clause functions
- *****************************************************************************/
-
-/*
- * make_funcclause
- * Creates a function clause given its function info and argument list.
- */
-Expr *
-make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
- CoercionForm funcformat, List *funcargs)
-{
- FuncExpr *expr = makeNode(FuncExpr);
-
- expr->funcid = funcid;
- expr->funcresulttype = funcresulttype;
- expr->funcretset = funcretset;
- expr->funcformat = funcformat;
- expr->args = funcargs;
- return (Expr *) expr;
-}
-
/*****************************************************************************
* NOT clause functions
*****************************************************************************/
BoolExpr *expr = makeNode(BoolExpr);
expr->boolop = NOT_EXPR;
- expr->args = makeList1(notclause);
+ expr->args = list_make1(notclause);
return (Expr *) expr;
}
Expr *
get_notclausearg(Expr *notclause)
{
- return lfirst(((BoolExpr *) notclause)->args);
+ return linitial(((BoolExpr *) notclause)->args);
}
/*****************************************************************************
* Variant of make_andclause for ANDing two qual conditions together.
* Qual conditions have the property that a NULL nodetree is interpreted
* as 'true'.
+ *
+ * NB: this makes no attempt to preserve AND/OR flatness; so it should not
+ * be used on a qual that has already been run through prepqual.c.
*/
Node *
make_and_qual(Node *qual1, Node *qual2)
return qual2;
if (qual2 == NULL)
return qual1;
- return (Node *) make_andclause(makeList2(qual1, qual2));
+ return (Node *) make_andclause(list_make2(qual1, qual2));
}
/*
- * Sometimes (such as in the result of canonicalize_qual or the input of
- * ExecQual), we use lists of expression nodes with implicit AND semantics.
+ * Sometimes (such as in the input of ExecQual), we use lists of expression
+ * nodes with implicit AND semantics.
*
* These functions convert between an AND-semantics expression list and the
* ordinary representation of a boolean expression.
make_ands_explicit(List *andclauses)
{
if (andclauses == NIL)
- return (Expr *) MAKEBOOLCONST(true, false);
- else if (lnext(andclauses) == NIL)
- return (Expr *) lfirst(andclauses);
+ return (Expr *) makeBoolConst(true, false);
+ else if (list_length(andclauses) == 1)
+ return (Expr *) linitial(andclauses);
else
return make_andclause(andclauses);
}
make_ands_implicit(Expr *clause)
{
/*
- * NB: because the parser sets the qual field to NULL in a query that
- * has no WHERE clause, we must consider a NULL input clause as TRUE,
- * even though one might more reasonably think it FALSE. Grumble. If
- * this causes trouble, consider changing the parser's behavior.
+ * NB: because the parser sets the qual field to NULL in a query that has
+ * no WHERE clause, we must consider a NULL input clause as TRUE, even
+ * though one might more reasonably think it FALSE. Grumble. If this
+ * causes trouble, consider changing the parser's behavior.
*/
if (clause == NULL)
return NIL; /* NULL -> NIL list == TRUE */
DatumGetBool(((Const *) clause)->constvalue))
return NIL; /* constant TRUE input -> NIL list */
else
- return makeList1(clause);
+ return list_make1(clause);
}
* Recursively search for Aggref nodes within a clause.
*
* Returns true if any aggregate found.
+ *
+ * This does not descend into subqueries, and so should be used only after
+ * reduction of sublinks to subplans, or in contexts where it's known there
+ * are no subqueries. There mustn't be outer-aggregate references either.
+ *
+ * (If you want something like this but able to deal with subqueries,
+ * see rewriteManip.c's checkExprHasAggs().)
*/
bool
contain_agg_clause(Node *clause)
static bool
contain_agg_clause_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- return true; /* abort the tree traversal and return
- * true */
- return expression_tree_walker(node, contain_agg_clause_walker, context);
-}
-
-/*
- * contain_distinct_agg_clause
- * Recursively search for DISTINCT Aggref nodes within a clause.
- *
- * Returns true if any DISTINCT aggregate found.
- */
-bool
-contain_distinct_agg_clause(Node *clause)
-{
- return contain_distinct_agg_clause_walker(clause, NULL);
-}
-
-static bool
-contain_distinct_agg_clause_walker(Node *node, void *context)
{
if (node == NULL)
return false;
if (IsA(node, Aggref))
{
- if (((Aggref *) node)->aggdistinct)
- return true; /* abort the tree traversal and return
- * true */
+ Assert(((Aggref *) node)->agglevelsup == 0);
+ return true; /* abort the tree traversal and return true */
}
- return expression_tree_walker(node, contain_distinct_agg_clause_walker, context);
+ Assert(!IsA(node, SubLink));
+ return expression_tree_walker(node, contain_agg_clause_walker, context);
}
/*
- * count_agg_clause
+ * count_agg_clauses
* Recursively count the Aggref nodes in an expression tree.
*
* Note: this also checks for nested aggregates, which are an error.
+ *
+ * We not only count the nodes, but attempt to estimate the total space
+ * needed for their transition state values if all are evaluated in parallel
+ * (as would be done in a HashAgg plan). See AggClauseCounts for the exact
+ * set of statistics returned.
+ *
+ * NOTE that the counts are ADDED to those already in *counts ... so the
+ * caller is responsible for zeroing the struct initially.
+ *
+ * This does not descend into subqueries, and so should be used only after
+ * reduction of sublinks to subplans, or in contexts where it's known there
+ * are no subqueries. There mustn't be outer-aggregate references either.
*/
-int
-count_agg_clause(Node *clause)
+void
+count_agg_clauses(Node *clause, AggClauseCounts *counts)
{
- int result = 0;
-
- count_agg_clause_walker(clause, &result);
- return result;
+ /* no setup needed */
+ count_agg_clauses_walker(clause, counts);
}
static bool
-count_agg_clause_walker(Node *node, int *count)
+count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
{
if (node == NULL)
return false;
if (IsA(node, Aggref))
{
- (*count)++;
+ Aggref *aggref = (Aggref *) node;
+ Oid *inputTypes;
+ int numArguments;
+ HeapTuple aggTuple;
+ Form_pg_aggregate aggform;
+ Oid aggtranstype;
+ int i;
+ ListCell *l;
+
+ Assert(aggref->agglevelsup == 0);
+ counts->numAggs++;
+ if (aggref->aggdistinct)
+ counts->numDistinctAggs++;
+
+ /* extract argument types */
+ numArguments = list_length(aggref->args);
+ inputTypes = (Oid *) palloc(sizeof(Oid) * numArguments);
+ i = 0;
+ foreach(l, aggref->args)
+ {
+ inputTypes[i++] = exprType((Node *) lfirst(l));
+ }
+
+ /* fetch aggregate transition datatype from pg_aggregate */
+ aggTuple = SearchSysCache(AGGFNOID,
+ ObjectIdGetDatum(aggref->aggfnoid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(aggTuple))
+ elog(ERROR, "cache lookup failed for aggregate %u",
+ aggref->aggfnoid);
+ aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+ aggtranstype = aggform->aggtranstype;
+ ReleaseSysCache(aggTuple);
+
+ /* resolve actual type of transition state, if polymorphic */
+ if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
+ {
+ /* have to fetch the agg's declared input types... */
+ Oid *declaredArgTypes;
+ int agg_nargs;
+
+ (void) get_func_signature(aggref->aggfnoid,
+ &declaredArgTypes, &agg_nargs);
+ Assert(agg_nargs == numArguments);
+ aggtranstype = enforce_generic_type_consistency(inputTypes,
+ declaredArgTypes,
+ agg_nargs,
+ aggtranstype);
+ pfree(declaredArgTypes);
+ }
+
+ /*
+ * If the transition type is pass-by-value then it doesn't add
+ * anything to the required size of the hashtable. If it is
+ * pass-by-reference then we have to add the estimated size of the
+ * value itself, plus palloc overhead.
+ */
+ if (!get_typbyval(aggtranstype))
+ {
+ int32 aggtranstypmod;
+ int32 avgwidth;
+
+ /*
+ * If transition state is of same type as first input, assume it's
+ * the same typmod (same width) as well. This works for cases
+ * like MAX/MIN and is probably somewhat reasonable otherwise.
+ */
+ if (numArguments > 0 && aggtranstype == inputTypes[0])
+ aggtranstypmod = exprTypmod((Node *) linitial(aggref->args));
+ else
+ aggtranstypmod = -1;
+
+ avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod);
+ avgwidth = MAXALIGN(avgwidth);
+
+ counts->transitionSpace += avgwidth + 2 * sizeof(void *);
+ }
/*
- * Complain if the aggregate's argument contains any aggregates;
+ * Complain if the aggregate's arguments contain any aggregates;
* nested agg functions are semantically nonsensical.
*/
- if (contain_agg_clause((Node *) ((Aggref *) node)->target))
- elog(ERROR, "Aggregate function calls may not be nested");
+ if (contain_agg_clause((Node *) aggref->args))
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ errmsg("aggregate function calls may not be nested")));
/*
* Having checked that, we need not recurse into the argument.
*/
return false;
}
- return expression_tree_walker(node, count_agg_clause_walker,
- (void *) count);
+ Assert(!IsA(node, SubLink));
+ return expression_tree_walker(node, count_agg_clauses_walker,
+ (void *) counts);
}
}
if (IsA(node, OpExpr))
{
- OpExpr *expr = (OpExpr *) node;
+ OpExpr *expr = (OpExpr *) node;
if (expr->opretset)
return true;
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, 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 false;
if (IsA(node, SubPlan) ||
IsA(node, SubLink))
- return true; /* abort the tree traversal and return
- * true */
+ return true; /* abort the tree traversal and return true */
return expression_tree_walker(node, contain_subplans_walker, context);
}
}
if (IsA(node, OpExpr))
{
- OpExpr *expr = (OpExpr *) node;
+ OpExpr *expr = (OpExpr *) node;
if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
return true;
}
if (IsA(node, DistinctExpr))
{
- DistinctExpr *expr = (DistinctExpr *) node;
+ DistinctExpr *expr = (DistinctExpr *) node;
+
+ if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
+ return true;
+ /* else fall through to check args */
+ }
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
return true;
}
if (IsA(node, NullIfExpr))
{
- NullIfExpr *expr = (NullIfExpr *) node;
+ NullIfExpr *expr = (NullIfExpr *) node;
if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
return true;
/* else fall through to check args */
}
- if (IsA(node, SubLink))
+ if (IsA(node, RowCompareExpr))
{
- SubLink *sublink = (SubLink *) node;
- List *opid;
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *opid;
- foreach(opid, sublink->operOids)
+ foreach(opid, rcexpr->opnos)
{
- if (op_volatile(lfirsto(opid)) != PROVOLATILE_IMMUTABLE)
+ if (op_volatile(lfirst_oid(opid)) != PROVOLATILE_IMMUTABLE)
return true;
}
/* else fall through to check args */
}
if (IsA(node, OpExpr))
{
- OpExpr *expr = (OpExpr *) node;
+ OpExpr *expr = (OpExpr *) node;
if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
return true;
}
if (IsA(node, DistinctExpr))
{
- DistinctExpr *expr = (DistinctExpr *) node;
+ DistinctExpr *expr = (DistinctExpr *) node;
+
+ if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
+ return true;
+ /* else fall through to check args */
+ }
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
return true;
}
if (IsA(node, NullIfExpr))
{
- NullIfExpr *expr = (NullIfExpr *) node;
+ NullIfExpr *expr = (NullIfExpr *) node;
if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
return true;
/* else fall through to check args */
}
- if (IsA(node, SubLink))
+ if (IsA(node, RowCompareExpr))
{
- SubLink *sublink = (SubLink *) node;
- List *opid;
+ /* RowCompare probably can't have volatile ops, but check anyway */
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *opid;
- foreach(opid, sublink->operOids)
+ foreach(opid, rcexpr->opnos)
{
- if (op_volatile(lfirsto(opid)) == PROVOLATILE_VOLATILE)
+ if (op_volatile(lfirst_oid(opid)) == PROVOLATILE_VOLATILE)
return true;
}
/* else fall through to check args */
* Returns true if any nonstrict construct is found --- ie, anything that
* could produce non-NULL output with a NULL input.
*
- * XXX we do not examine sub-selects to see if they contain uses of
- * nonstrict functions. It's not real clear if that is correct or not...
- * for the current usage it does not matter, since inline_function()
- * rejects cases with sublinks.
+ * The idea here is that the caller has verified that the expression contains
+ * one or more Var or Param nodes (as appropriate for the caller's need), and
+ * now wishes to prove that the expression result will be NULL if any of these
+ * inputs is NULL. If we return false, then the proof succeeded.
*/
bool
contain_nonstrict_functions(Node *clause)
{
if (node == NULL)
return false;
+ if (IsA(node, Aggref))
+ {
+ /* an aggregate could return non-null with null input */
+ return true;
+ }
+ if (IsA(node, ArrayRef))
+ {
+ /* array assignment is nonstrict, but subscripting is strict */
+ if (((ArrayRef *) node)->refassgnexpr != NULL)
+ return true;
+ /* else fall through to check args */
+ }
if (IsA(node, FuncExpr))
{
FuncExpr *expr = (FuncExpr *) node;
}
if (IsA(node, OpExpr))
{
- OpExpr *expr = (OpExpr *) node;
+ OpExpr *expr = (OpExpr *) node;
if (!op_strict(expr->opno))
return true;
/* IS DISTINCT FROM is inherently non-strict */
return true;
}
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (!is_strict_saop(expr, false))
+ return true;
+ /* else fall through to check args */
+ }
if (IsA(node, BoolExpr))
{
BoolExpr *expr = (BoolExpr *) node;
switch (expr->boolop)
{
- case OR_EXPR:
case AND_EXPR:
- /* OR, AND are inherently non-strict */
+ case OR_EXPR:
+ /* AND, OR are inherently non-strict */
return true;
default:
break;
}
}
+ if (IsA(node, SubLink))
+ {
+ /* In some cases a sublink might be strict, but in general not */
+ return true;
+ }
+ if (IsA(node, SubPlan))
+ return true;
+ if (IsA(node, FieldStore))
+ return true;
if (IsA(node, CaseExpr))
return true;
- /* NB: ArrayExpr might someday be nonstrict */
+ if (IsA(node, CaseWhen))
+ return true;
+ if (IsA(node, ArrayExpr))
+ return true;
+ if (IsA(node, RowExpr))
+ return true;
+ if (IsA(node, RowCompareExpr))
+ return true;
if (IsA(node, CoalesceExpr))
return true;
+ if (IsA(node, MinMaxExpr))
+ return true;
+ if (IsA(node, XmlExpr))
+ return true;
if (IsA(node, NullIfExpr))
return true;
if (IsA(node, NullTest))
return true;
if (IsA(node, BooleanTest))
return true;
- if (IsA(node, SubLink))
+ return expression_tree_walker(node, contain_nonstrict_functions_walker,
+ context);
+}
+
+
+/*
+ * find_nonnullable_rels
+ * Determine which base rels are forced nonnullable by given clause.
+ *
+ * Returns the set of all Relids that are referenced in the clause in such
+ * a way that the clause cannot possibly return TRUE if any of these Relids
+ * is an all-NULL row. (It is OK to err on the side of conservatism; hence
+ * the analysis here is simplistic.)
+ *
+ * The semantics here are subtly different from contain_nonstrict_functions:
+ * that function is concerned with NULL results from arbitrary expressions,
+ * but here we assume that the input is a Boolean expression, and wish to
+ * see if NULL inputs will provably cause a FALSE-or-NULL result. We expect
+ * the expression to have been AND/OR flattened and converted to implicit-AND
+ * format.
+ *
+ * We don't use expression_tree_walker here because we don't want to
+ * descend through very many kinds of nodes; only the ones we can be sure
+ * are strict. We can descend through the top level of implicit AND'ing,
+ * but not through any explicit ANDs (or ORs) below that, since those are not
+ * strict constructs. The List case handles the top-level implicit AND list
+ * as well as lists of arguments to strict operators/functions.
+ */
+Relids
+find_nonnullable_rels(Node *clause)
+{
+ return find_nonnullable_rels_walker(clause, true);
+}
+
+static Relids
+find_nonnullable_rels_walker(Node *node, bool top_level)
+{
+ Relids result = NULL;
+
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
{
- SubLink *sublink = (SubLink *) node;
- List *opid;
+ Var *var = (Var *) node;
- foreach(opid, sublink->operOids)
+ if (var->varlevelsup == 0)
+ result = bms_make_singleton(var->varno);
+ }
+ else if (IsA(node, List))
+ {
+ ListCell *l;
+
+ foreach(l, (List *) node)
{
- if (!op_strict(lfirsto(opid)))
- return true;
+ result = bms_join(result,
+ find_nonnullable_rels_walker(lfirst(l),
+ top_level));
}
- /* else fall through to check args */
}
- return expression_tree_walker(node, contain_nonstrict_functions_walker,
- context);
+ else if (IsA(node, FuncExpr))
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+
+ if (func_strict(expr->funcid))
+ result = find_nonnullable_rels_walker((Node *) expr->args, false);
+ }
+ else if (IsA(node, OpExpr))
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ if (op_strict(expr->opno))
+ result = find_nonnullable_rels_walker((Node *) expr->args, false);
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (is_strict_saop(expr, true))
+ result = find_nonnullable_rels_walker((Node *) expr->args, false);
+ }
+ else if (IsA(node, BoolExpr))
+ {
+ BoolExpr *expr = (BoolExpr *) node;
+
+ /* NOT is strict, others are not */
+ if (expr->boolop == NOT_EXPR)
+ result = find_nonnullable_rels_walker((Node *) expr->args, false);
+ }
+ else if (IsA(node, RelabelType))
+ {
+ RelabelType *expr = (RelabelType *) node;
+
+ result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
+ }
+ else if (IsA(node, ConvertRowtypeExpr))
+ {
+ /* not clear this is useful, but it can't hurt */
+ ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node;
+
+ result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
+ }
+ else if (IsA(node, NullTest))
+ {
+ NullTest *expr = (NullTest *) node;
+
+ /*
+ * IS NOT NULL can be considered strict, but only at top level; else
+ * we might have something like NOT (x IS NOT NULL).
+ */
+ if (top_level && expr->nulltesttype == IS_NOT_NULL)
+ result = find_nonnullable_rels_walker((Node *) expr->arg, false);
+ }
+ else if (IsA(node, BooleanTest))
+ {
+ BooleanTest *expr = (BooleanTest *) node;
+
+ /*
+ * Appropriate boolean tests are strict at top level.
+ */
+ if (top_level &&
+ (expr->booltesttype == IS_TRUE ||
+ expr->booltesttype == IS_FALSE ||
+ expr->booltesttype == IS_NOT_UNKNOWN))
+ result = find_nonnullable_rels_walker((Node *) expr->arg, false);
+ }
+ return result;
+}
+
+/*
+ * Can we treat a ScalarArrayOpExpr as strict?
+ *
+ * If "falseOK" is true, then a "false" result can be considered strict,
+ * else we need to guarantee an actual NULL result for NULL input.
+ *
+ * "foo op ALL array" is strict if the op is strict *and* we can prove
+ * that the array input isn't an empty array. We can check that
+ * for the cases of an array constant and an ARRAY[] construct.
+ *
+ * "foo op ANY array" is strict in the falseOK sense if the op is strict.
+ * If not falseOK, the test is the same as for "foo op ALL array".
+ */
+static bool
+is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK)
+{
+ Node *rightop;
+
+ /* The contained operator must be strict. */
+ if (!op_strict(expr->opno))
+ return false;
+ /* If ANY and falseOK, that's all we need to check. */
+ if (expr->useOr && falseOK)
+ return true;
+ /* Else, we have to see if the array is provably non-empty. */
+ Assert(list_length(expr->args) == 2);
+ rightop = (Node *) lsecond(expr->args);
+ if (rightop && IsA(rightop, Const))
+ {
+ Datum arraydatum = ((Const *) rightop)->constvalue;
+ bool arrayisnull = ((Const *) rightop)->constisnull;
+ ArrayType *arrayval;
+ int nitems;
+
+ if (arrayisnull)
+ return false;
+ arrayval = DatumGetArrayTypeP(arraydatum);
+ nitems = ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));
+ if (nitems > 0)
+ return true;
+ }
+ else if (rightop && IsA(rightop, ArrayExpr))
+ {
+ ArrayExpr *arrayexpr = (ArrayExpr *) rightop;
+
+ if (arrayexpr->elements != NIL && !arrayexpr->multidims)
+ return true;
+ }
+ return false;
}
/*
* is_pseudo_constant_clause
- * Detect whether a clause is "constant", ie, it contains no variables
- * of the current query level and no uses of volatile functions.
- * Such a clause is not necessarily a true constant: it can still contain
+ * Detect whether an expression is "pseudo constant", ie, it contains no
+ * variables of the current query level and no uses of volatile functions.
+ * Such an expr is not necessarily a true constant: it can still contain
* Params and outer-level Vars, not to mention functions whose results
- * may vary from one statement to the next. However, the clause's value
+ * may vary from one statement to the next. However, the expr's value
* will be constant over any one scan of the current query, so it can be
- * used as an indexscan key or (if a top-level qual) can be pushed up to
- * become a gating qual.
+ * used as, eg, an indexscan key.
*/
bool
is_pseudo_constant_clause(Node *clause)
{
/*
* We could implement this check in one recursive scan. But since the
- * check for volatile functions is both moderately expensive and
- * unlikely to fail, it seems better to look for Vars first and only
- * check for volatile functions if we find no Vars.
+ * check for volatile functions is both moderately expensive and unlikely
+ * to fail, it seems better to look for Vars first and only check for
+ * volatile functions if we find no Vars.
*/
if (!contain_var_clause(clause) &&
!contain_volatile_functions(clause))
}
/*
- * pull_constant_clauses
- * Scan through a list of qualifications and separate "constant" quals
- * from those that are not.
- *
- * Returns a list of the pseudo-constant clauses in constantQual and the
- * remaining quals as the return value.
+ * is_pseudo_constant_clause_relids
+ * Same as above, except caller already has available the var membership
+ * of the expression; this lets us avoid the contain_var_clause() scan.
*/
-List *
-pull_constant_clauses(List *quals, List **constantQual)
+bool
+is_pseudo_constant_clause_relids(Node *clause, Relids relids)
{
- List *constqual = NIL;
- List *restqual = NIL;
- List *q;
-
- foreach(q, quals)
- {
- Node *qual = (Node *) lfirst(q);
-
- if (is_pseudo_constant_clause(qual))
- constqual = lappend(constqual, qual);
- else
- restqual = lappend(restqual, qual);
- }
- *constantQual = constqual;
- return restqual;
+ if (bms_is_empty(relids) &&
+ !contain_volatile_functions(clause))
+ return true;
+ return false;
}
bool
has_distinct_on_clause(Query *query)
{
- List *targetList;
+ ListCell *l;
/* Is there a DISTINCT clause at all? */
if (query->distinctClause == NIL)
/*
* If the DISTINCT list contains all the nonjunk targetlist items, and
- * nothing else (ie, no junk tlist items), then it's a simple
- * DISTINCT, else it's DISTINCT ON. We do not require the lists to be
- * in the same order (since the parser may have adjusted the DISTINCT
- * clause ordering to agree with ORDER BY). Furthermore, a
- * non-DISTINCT junk tlist item that is in the sortClause is also
- * evidence of DISTINCT ON, since we don't allow ORDER BY on junk
- * tlist items when plain DISTINCT is used.
+ * nothing else (ie, no junk tlist items), then it's a simple DISTINCT,
+ * else it's DISTINCT ON. We do not require the lists to be in the same
+ * order (since the parser may have adjusted the DISTINCT clause ordering
+ * to agree with ORDER BY). Furthermore, a non-DISTINCT junk tlist item
+ * that is in the sortClause is also evidence of DISTINCT ON, since we
+ * don't allow ORDER BY on junk tlist items when plain DISTINCT is used.
*
* This code assumes that the DISTINCT list is valid, ie, all its entries
* match some entry of the tlist.
*/
- foreach(targetList, query->targetList)
+ foreach(l, query->targetList)
{
- TargetEntry *tle = (TargetEntry *) lfirst(targetList);
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
- if (tle->resdom->ressortgroupref == 0)
+ if (tle->ressortgroupref == 0)
{
- if (tle->resdom->resjunk)
+ if (tle->resjunk)
continue; /* we can ignore unsorted junk cols */
return true; /* definitely not in DISTINCT list */
}
if (targetIsInSortList(tle, query->distinctClause))
{
- if (tle->resdom->resjunk)
+ if (tle->resjunk)
return true; /* junk TLE in DISTINCT means DISTINCT ON */
/* else this TLE is okay, keep looking */
}
else
{
/* This TLE is not in DISTINCT list */
- if (!tle->resdom->resjunk)
+ if (!tle->resjunk)
return true; /* non-junk, non-DISTINCT, so DISTINCT ON */
if (targetIsInSortList(tle, query->sortClause))
return true; /* sorted, non-distinct junk */
return false;
}
-
-/*****************************************************************************
- * *
- * General clause-manipulating routines *
- * *
- *****************************************************************************/
-
/*
- * clause_get_relids_vars
- * Retrieves distinct relids and vars appearing within a clause.
- *
- * '*relids' is set to the set of all distinct "varno"s appearing
- * in Vars within the clause.
- * '*vars' is set to a list of all distinct Vars appearing within the clause.
- * Var nodes are considered distinct if they have different varno
- * or varattno values. If there are several occurrences of the same
- * varno/varattno, you get a randomly chosen one...
- *
- * Note that upper-level vars are ignored, since they normally will
- * become Params with respect to this query level.
+ * Test whether a query uses simple DISTINCT, ie, has a distinct-list that
+ * is the same as the set of output columns.
*/
-void
-clause_get_relids_vars(Node *clause, Relids *relids, List **vars)
+bool
+has_distinct_clause(Query *query)
{
- List *clvars = pull_var_clause(clause, false);
- Relids varnos = NULL;
- List *var_list = NIL;
- List *i;
-
- foreach(i, clvars)
- {
- Var *var = (Var *) lfirst(i);
- List *vi;
+ /* Is there a DISTINCT clause at all? */
+ if (query->distinctClause == NIL)
+ return false;
- varnos = bms_add_member(varnos, var->varno);
- foreach(vi, var_list)
- {
- Var *in_list = (Var *) lfirst(vi);
+ /* It's DISTINCT if it's not DISTINCT ON */
+ return !has_distinct_on_clause(query);
+}
- if (in_list->varno == var->varno &&
- in_list->varattno == var->varattno)
- break;
- }
- if (vi == NIL)
- var_list = lcons(var, var_list);
- }
- freeList(clvars);
- *relids = varnos;
- *vars = var_list;
-}
+/*****************************************************************************
+ * *
+ * General clause-manipulating routines *
+ * *
+ *****************************************************************************/
/*
* NumRelids
}
/*
- * CommuteClause: commute a binary operator clause
+ * CommuteOpExpr: commute a binary operator clause
*
* XXX the clause is destructively modified!
*/
void
-CommuteClause(OpExpr *clause)
+CommuteOpExpr(OpExpr *clause)
{
Oid opoid;
Node *temp;
+ /* Sanity checks: caller is at fault if these fail */
if (!is_opclause(clause) ||
- length(clause->args) != 2)
- elog(ERROR, "CommuteClause: applied to non-binary-operator clause");
+ list_length(clause->args) != 2)
+ elog(ERROR, "cannot commute non-binary-operator clause");
opoid = get_commutator(clause->opno);
if (!OidIsValid(opoid))
- elog(ERROR, "CommuteClause: no commutator for operator %u",
+ elog(ERROR, "could not find commutator for operator %u",
clause->opno);
/*
clause->opfuncid = InvalidOid;
/* opresulttype and opretset are assumed not to change */
- temp = lfirst(clause->args);
- lfirst(clause->args) = lsecond(clause->args);
+ temp = linitial(clause->args);
+ linitial(clause->args) = lsecond(clause->args);
lsecond(clause->args) = temp;
}
-
-/*--------------------
- * eval_const_expressions
- *
- * Reduce any recognizably constant subexpressions of the given
- * expression tree, for example "2 + 2" => "4". More interestingly,
- * we can reduce certain boolean expressions even when they contain
- * non-constant subexpressions: "x OR true" => "true" no matter what
- * the subexpression x is. (XXX We assume that no such subexpression
- * will have important side-effects, which is not necessarily a good
- * assumption in the presence of user-defined functions; do we need a
- * pg_proc flag that prevents discarding the execution of a function?)
- *
- * We do understand that certain functions may deliver non-constant
- * results even with constant inputs, "nextval()" being the classic
- * example. Functions that are not marked "immutable" in pg_proc
- * will not be pre-evaluated here, although we will reduce their
- * arguments as far as possible.
+/*
+ * CommuteRowCompareExpr: commute a RowCompareExpr clause
*
- * We assume that the tree has already been type-checked and contains
- * only operators and functions that are reasonable to try to execute.
- *--------------------
+ * XXX the clause is destructively modified!
*/
-Node *
-eval_const_expressions(Node *node)
+void
+CommuteRowCompareExpr(RowCompareExpr *clause)
{
- /*
- * 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);
-}
+ List *newops;
+ List *temp;
+ ListCell *l;
-static Node *
-eval_const_expressions_mutator(Node *node, List *active_fns)
-{
- if (node == NULL)
+ /* Sanity checks: caller is at fault if these fail */
+ if (!IsA(clause, RowCompareExpr))
+ elog(ERROR, "expected a RowCompareExpr");
+
+ /* Build list of commuted operators */
+ newops = NIL;
+ foreach(l, clause->opnos)
+ {
+ Oid opoid = lfirst_oid(l);
+
+ opoid = get_commutator(opoid);
+ if (!OidIsValid(opoid))
+ elog(ERROR, "could not find commutator for operator %u",
+ lfirst_oid(l));
+ newops = lappend_oid(newops, opoid);
+ }
+
+ /*
+ * modify the clause in-place!
+ */
+ switch (clause->rctype)
+ {
+ case ROWCOMPARE_LT:
+ clause->rctype = ROWCOMPARE_GT;
+ break;
+ case ROWCOMPARE_LE:
+ clause->rctype = ROWCOMPARE_GE;
+ break;
+ case ROWCOMPARE_GE:
+ clause->rctype = ROWCOMPARE_LE;
+ break;
+ case ROWCOMPARE_GT:
+ clause->rctype = ROWCOMPARE_LT;
+ break;
+ default:
+ elog(ERROR, "unexpected RowCompare type: %d",
+ (int) clause->rctype);
+ break;
+ }
+
+ clause->opnos = newops;
+
+ /*
+ * Note: we need not change the opfamilies list; we assume any btree
+ * opfamily containing an operator will also contain its commutator.
+ */
+
+ temp = clause->largs;
+ clause->largs = clause->rargs;
+ clause->rargs = temp;
+}
+
+/*
+ * strip_implicit_coercions: remove implicit coercions at top level of tree
+ *
+ * Note: there isn't any useful thing we can do with a RowExpr here, so
+ * just return it unchanged, even if it's marked as an implicit coercion.
+ */
+Node *
+strip_implicit_coercions(Node *node)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, FuncExpr))
+ {
+ FuncExpr *f = (FuncExpr *) node;
+
+ if (f->funcformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions(linitial(f->args));
+ }
+ else if (IsA(node, RelabelType))
+ {
+ RelabelType *r = (RelabelType *) node;
+
+ if (r->relabelformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) r->arg);
+ }
+ else if (IsA(node, ConvertRowtypeExpr))
+ {
+ ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node;
+
+ if (c->convertformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) c->arg);
+ }
+ else if (IsA(node, CoerceToDomain))
+ {
+ CoerceToDomain *c = (CoerceToDomain *) node;
+
+ if (c->coercionformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) c->arg);
+ }
+ return node;
+}
+
+/*
+ * set_coercionform_dontcare: set all CoercionForm fields to COERCE_DONTCARE
+ *
+ * This is used to make index expressions and index predicates more easily
+ * comparable to clauses of queries. CoercionForm is not semantically
+ * significant (for cases where it does matter, the significant info is
+ * coded into the coercion function arguments) so we can ignore it during
+ * comparisons. Thus, for example, an index on "foo::int4" can match an
+ * implicit coercion to int4.
+ *
+ * Caution: the passed expression tree is modified in-place.
+ */
+void
+set_coercionform_dontcare(Node *node)
+{
+ (void) set_coercionform_dontcare_walker(node, NULL);
+}
+
+static bool
+set_coercionform_dontcare_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, FuncExpr))
+ ((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
+ else if (IsA(node, RelabelType))
+ ((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
+ else if (IsA(node, ConvertRowtypeExpr))
+ ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE;
+ else if (IsA(node, RowExpr))
+ ((RowExpr *) node)->row_format = COERCE_DONTCARE;
+ else if (IsA(node, CoerceToDomain))
+ ((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
+ return expression_tree_walker(node, set_coercionform_dontcare_walker,
+ context);
+}
+
+/*
+ * Helper for eval_const_expressions: check that datatype of an attribute
+ * is still what it was when the expression was parsed. This is needed to
+ * guard against improper simplification after ALTER COLUMN TYPE. (XXX we
+ * may well need to make similar checks elsewhere?)
+ */
+static bool
+rowtype_field_matches(Oid rowtypeid, int fieldnum,
+ Oid expectedtype, int32 expectedtypmod)
+{
+ TupleDesc tupdesc;
+ Form_pg_attribute attr;
+
+ /* No issue for RECORD, since there is no way to ALTER such a type */
+ if (rowtypeid == RECORDOID)
+ return true;
+ tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1);
+ if (fieldnum <= 0 || fieldnum > tupdesc->natts)
+ {
+ ReleaseTupleDesc(tupdesc);
+ return false;
+ }
+ attr = tupdesc->attrs[fieldnum - 1];
+ if (attr->attisdropped ||
+ attr->atttypid != expectedtype ||
+ attr->atttypmod != expectedtypmod)
+ {
+ ReleaseTupleDesc(tupdesc);
+ return false;
+ }
+ ReleaseTupleDesc(tupdesc);
+ return true;
+}
+
+
+/*--------------------
+ * eval_const_expressions
+ *
+ * Reduce any recognizably constant subexpressions of the given
+ * expression tree, for example "2 + 2" => "4". More interestingly,
+ * we can reduce certain boolean expressions even when they contain
+ * non-constant subexpressions: "x OR true" => "true" no matter what
+ * the subexpression x is. (XXX We assume that no such subexpression
+ * will have important side-effects, which is not necessarily a good
+ * assumption in the presence of user-defined functions; do we need a
+ * pg_proc flag that prevents discarding the execution of a function?)
+ *
+ * We do understand that certain functions may deliver non-constant
+ * results even with constant inputs, "nextval()" being the classic
+ * example. Functions that are not marked "immutable" in pg_proc
+ * will not be pre-evaluated here, although we will reduce their
+ * arguments as far as possible.
+ *
+ * We assume that the tree has already been type-checked and contains
+ * only operators and functions that are reasonable to try to execute.
+ *
+ * NOTE: the planner assumes that this will always flatten nested AND and
+ * OR clauses into N-argument form. See comments in prepqual.c.
+ *--------------------
+ */
+Node *
+eval_const_expressions(Node *node)
+{
+ eval_const_expressions_context context;
+
+ context.active_fns = NIL; /* nothing being recursively simplified */
+ context.case_val = NULL; /* no CASE being examined */
+ 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 extra steps that are taken in this mode are:
+ * 1. Substitute values for Params, where a bound Param value has been made
+ * available by the caller of planner(), even if the Param isn't marked
+ * constant. This effectively means that we plan using the first supplied
+ * value of the Param.
+ * 2. Fold stable, as well as immutable, functions to constants.
+ *--------------------
+ */
+Node *
+estimate_expression_value(Node *node)
+{
+ eval_const_expressions_context context;
+
+ context.active_fns = NIL; /* nothing being recursively simplified */
+ context.case_val = NULL; /* no CASE being examined */
+ context.estimate = true; /* unsafe transformations OK */
+ return eval_const_expressions_mutator(node, &context);
+}
+
+static Node *
+eval_const_expressions_mutator(Node *node,
+ eval_const_expressions_context *context)
+{
+ if (node == NULL)
return NULL;
+ if (IsA(node, Param))
+ {
+ Param *param = (Param *) node;
+
+ /* Look to see if we've been given a value for this Param */
+ if (param->paramkind == PARAM_EXTERN &&
+ PlannerBoundParamList != NULL &&
+ param->paramid > 0 &&
+ param->paramid <= PlannerBoundParamList->numParams)
+ {
+ ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
+
+ if (OidIsValid(prm->ptype))
+ {
+ /* OK to substitute parameter value? */
+ if (context->estimate || (prm->pflags & PARAM_FLAG_CONST))
+ {
+ /*
+ * Return a Const representing the param value. Must copy
+ * pass-by-ref datatypes, since the Param might be in a
+ * memory context shorter-lived than our output plan
+ * should be.
+ */
+ int16 typLen;
+ bool typByVal;
+ Datum pval;
+
+ Assert(prm->ptype == param->paramtype);
+ get_typlenbyval(param->paramtype, &typLen, &typByVal);
+ if (prm->isnull || typByVal)
+ pval = prm->value;
+ else
+ pval = datumCopy(prm->value, typByVal, typLen);
+ return (Node *) makeConst(param->paramtype,
+ (int) typLen,
+ pval,
+ prm->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;
/*
* Reduce constants in the FuncExpr's arguments. We know args is
- * either NIL or a List node, so we can call
- * expression_tree_mutator directly rather than recursing to self.
+ * either NIL or a List node, so we can call expression_tree_mutator
+ * directly rather than recursing to self.
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
- eval_const_expressions_mutator,
- (void *) active_fns);
+ eval_const_expressions_mutator,
+ (void *) context);
+
/*
- * Code for op/func reduction is pretty bulky, so split it out
- * as a separate function.
+ * 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;
+
/*
* The expression cannot be simplified any further, so build and
* return a replacement FuncExpr node using the possibly-simplified
OpExpr *newexpr;
/*
- * Reduce constants in the OpExpr's arguments. We know args is
- * either NIL or a List node, so we can call
- * expression_tree_mutator directly rather than recursing to self.
+ * Reduce constants in the OpExpr's arguments. We know args is either
+ * NIL or a List node, so we can call expression_tree_mutator directly
+ * rather than recursing to self.
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
- eval_const_expressions_mutator,
- (void *) active_fns);
+ eval_const_expressions_mutator,
+ (void *) context);
+
/*
- * Need to get OID of underlying function. Okay to scribble on
- * input to this extent.
+ * Need to get OID of underlying function. Okay to scribble on input
+ * to this extent.
*/
set_opfuncid(expr);
+
/*
- * Code for op/func reduction is pretty bulky, so split it out
- * as a separate function.
+ * Code for op/func reduction is pretty bulky, so split it out as a
+ * separate function.
*/
simple = simplify_function(expr->opfuncid, expr->opresulttype, args,
- true, active_fns);
+ true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
+
+ /*
+ * If the operator is boolean equality, we know how to simplify cases
+ * involving one constant and one non-constant argument.
+ */
+ if (expr->opno == BooleanEqualOperator)
+ {
+ simple = simplify_boolean_equality(args);
+ if (simple) /* successfully simplified it */
+ return (Node *) simple;
+ }
+
/*
* The expression cannot be simplified any further, so build and
* return a replacement OpExpr node using the possibly-simplified
{
DistinctExpr *expr = (DistinctExpr *) node;
List *args;
- List *arg;
+ ListCell *arg;
bool has_null_input = false;
bool all_null_input = true;
bool has_nonconst_input = false;
/*
* Reduce constants in the DistinctExpr's arguments. We know args is
- * either NIL or a List node, so we can call
- * expression_tree_mutator directly rather than recursing to self.
+ * either NIL or a List node, so we can call expression_tree_mutator
+ * directly rather than recursing to self.
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
- eval_const_expressions_mutator,
- (void *) active_fns);
+ eval_const_expressions_mutator,
+ (void *) context);
/*
- * We must do our own check for NULLs because
- * DistinctExpr has different results for NULL input
- * than the underlying operator does.
+ * We must do our own check for NULLs because DistinctExpr has
+ * different results for NULL input than the underlying operator does.
*/
foreach(arg, args)
{
{
/* all nulls? then not distinct */
if (all_null_input)
- return MAKEBOOLCONST(false, false);
+ return makeBoolConst(false, false);
/* one null? then distinct */
if (has_null_input)
- return MAKEBOOLCONST(true, false);
+ return makeBoolConst(true, false);
/* otherwise try to evaluate the '=' operator */
/* (NOT okay to try to inline it, though!) */
/*
- * Need to get OID of underlying function. Okay to scribble on
+ * Need to get OID of underlying function. Okay to scribble on
* input to this extent.
*/
- set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
+ set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
+
/*
- * Code for op/func reduction is pretty bulky, so split it out
- * as a separate function.
+ * Code for op/func reduction is pretty bulky, so split it out as
+ * a separate function.
*/
simple = simplify_function(expr->opfuncid, expr->opresulttype,
- args, false, active_fns);
+ args, false, context);
if (simple) /* successfully simplified it */
{
/*
* Since the underlying operator is "=", must negate its
* result
*/
- Const *csimple = (Const *) simple;
+ Const *csimple = (Const *) simple;
Assert(IsA(csimple, Const));
csimple->constvalue =
if (IsA(node, BoolExpr))
{
BoolExpr *expr = (BoolExpr *) node;
- List *args;
- Const *const_input;
-
- /*
- * Reduce constants in the BoolExpr's arguments. We know args is
- * either NIL or a List node, so we can call
- * expression_tree_mutator directly rather than recursing to self.
- */
- args = (List *) expression_tree_mutator((Node *) expr->args,
- eval_const_expressions_mutator,
- (void *) active_fns);
switch (expr->boolop)
{
case OR_EXPR:
{
- /*----------
- * OR arguments are handled as follows:
- * non constant: keep
- * FALSE: drop (does not affect result)
- * TRUE: force result to TRUE
- * NULL: keep only one
- * We keep one NULL input because ExecEvalOr returns NULL
- * when no input is TRUE and at least one is NULL.
- *----------
- */
- List *newargs = NIL;
- List *arg;
+ List *newargs;
bool haveNull = false;
bool forceTrue = false;
- foreach(arg, args)
- {
- if (!IsA(lfirst(arg), Const))
- {
- newargs = lappend(newargs, lfirst(arg));
- continue;
- }
- const_input = (Const *) lfirst(arg);
- if (const_input->constisnull)
- haveNull = true;
- else if (DatumGetBool(const_input->constvalue))
- forceTrue = true;
- /* otherwise, we can drop the constant-false input */
- }
-
- /*
- * We could return TRUE before falling out of the
- * loop, but this coding method will be easier to
- * adapt if we ever add a notion of non-removable
- * functions. We'd need to check all the inputs for
- * non-removability.
- */
+ newargs = simplify_or_arguments(expr->args, context,
+ &haveNull, &forceTrue);
if (forceTrue)
- return MAKEBOOLCONST(true, false);
+ return makeBoolConst(true, false);
if (haveNull)
- newargs = lappend(newargs, MAKEBOOLCONST(false, true));
+ newargs = lappend(newargs, makeBoolConst(false, true));
/* If all the inputs are FALSE, result is FALSE */
if (newargs == NIL)
- return MAKEBOOLCONST(false, false);
+ return makeBoolConst(false, false);
/* If only one nonconst-or-NULL input, it's the result */
- if (lnext(newargs) == NIL)
- return (Node *) lfirst(newargs);
+ if (list_length(newargs) == 1)
+ return (Node *) linitial(newargs);
/* Else we still need an OR node */
return (Node *) make_orclause(newargs);
}
case AND_EXPR:
{
- /*----------
- * AND arguments are handled as follows:
- * non constant: keep
- * TRUE: drop (does not affect result)
- * FALSE: force result to FALSE
- * NULL: keep only one
- * We keep one NULL input because ExecEvalAnd returns NULL
- * when no input is FALSE and at least one is NULL.
- *----------
- */
- List *newargs = NIL;
- List *arg;
+ List *newargs;
bool haveNull = false;
bool forceFalse = false;
- foreach(arg, args)
- {
- if (!IsA(lfirst(arg), Const))
- {
- newargs = lappend(newargs, lfirst(arg));
- continue;
- }
- const_input = (Const *) lfirst(arg);
- if (const_input->constisnull)
- haveNull = true;
- else if (!DatumGetBool(const_input->constvalue))
- forceFalse = true;
- /* otherwise, we can drop the constant-true input */
- }
-
- /*
- * We could return FALSE before falling out of the
- * loop, but this coding method will be easier to
- * adapt if we ever add a notion of non-removable
- * functions. We'd need to check all the inputs for
- * non-removability.
- */
+ newargs = simplify_and_arguments(expr->args, context,
+ &haveNull, &forceFalse);
if (forceFalse)
- return MAKEBOOLCONST(false, false);
+ return makeBoolConst(false, false);
if (haveNull)
- newargs = lappend(newargs, MAKEBOOLCONST(false, true));
+ newargs = lappend(newargs, makeBoolConst(false, true));
/* If all the inputs are TRUE, result is TRUE */
if (newargs == NIL)
- return MAKEBOOLCONST(true, false);
+ return makeBoolConst(true, false);
/* If only one nonconst-or-NULL input, it's the result */
- if (lnext(newargs) == NIL)
- return (Node *) lfirst(newargs);
+ if (list_length(newargs) == 1)
+ return (Node *) linitial(newargs);
/* Else we still need an AND node */
return (Node *) make_andclause(newargs);
}
case NOT_EXPR:
- Assert(length(args) == 1);
- if (IsA(lfirst(args), Const))
{
- const_input = (Const *) lfirst(args);
- /* NOT NULL => NULL */
- if (const_input->constisnull)
- return MAKEBOOLCONST(false, true);
- /* otherwise pretty easy */
- return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
- false);
+ Node *arg;
+
+ Assert(list_length(expr->args) == 1);
+ arg = eval_const_expressions_mutator(linitial(expr->args),
+ context);
+ if (IsA(arg, Const))
+ {
+ Const *const_input = (Const *) arg;
+
+ /* NOT NULL => NULL */
+ if (const_input->constisnull)
+ return makeBoolConst(false, true);
+ /* otherwise pretty easy */
+ return makeBoolConst(!DatumGetBool(const_input->constvalue),
+ false);
+ }
+ else if (not_clause(arg))
+ {
+ /* Cancel NOT/NOT */
+ return (Node *) get_notclausearg((Expr *) arg);
+ }
+ /* Else we still need a NOT node */
+ return (Node *) make_notclause((Expr *) arg);
}
- /* Else we still need a NOT node */
- return (Node *) make_notclause(lfirst(args));
default:
- elog(ERROR, "eval_const_expressions: unexpected boolop %d",
+ elog(ERROR, "unrecognized boolop: %d",
(int) expr->boolop);
break;
}
if (IsA(node, SubPlan))
{
/*
- * Return a SubPlan unchanged --- too late to do anything
- * with it.
+ * Return a SubPlan unchanged --- too late to do anything with it.
*
- * XXX should we elog() here instead? Probably this routine
- * should never be invoked after SubPlan creation.
+ * XXX should we ereport() here instead? Probably this routine should
+ * never be invoked after SubPlan creation.
*/
return node;
}
if (IsA(node, RelabelType))
{
/*
- * If we can simplify the input to a constant, then we don't need
- * the RelabelType node anymore: just change the type field of the
- * Const node. Otherwise, must copy the RelabelType node.
+ * If we can simplify the input to a constant, then we don't need the
+ * RelabelType node anymore: just change the type field of the Const
+ * node. Otherwise, must copy the RelabelType node.
*/
RelabelType *relabel = (RelabelType *) node;
Node *arg;
arg = eval_const_expressions_mutator((Node *) relabel->arg,
- active_fns);
+ context);
/*
- * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
- * can discard all but the top one.
+ * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we can
+ * discard all but the top one.
*/
while (arg && IsA(arg, RelabelType))
arg = (Node *) ((RelabelType *) arg)->arg;
con->consttype = relabel->resulttype;
/*
- * relabel's resulttypmod is discarded, which is OK for now;
- * if the type actually needs a runtime length coercion then
- * there should be a function call to do it just above this
- * node.
+ * relabel's resulttypmod is discarded, which is OK for now; if
+ * the type actually needs a runtime length coercion then there
+ * should be a function call to do it just above this node.
*/
return (Node *) con;
}
}
if (IsA(node, CaseExpr))
{
-
/*----------
* CASE expressions can be simplified if there are constant
* condition clauses:
* simplify the entire CASE to that alternative's expression.
* If there are no non-FALSE alternatives, we simplify the entire
* CASE to the default result (ELSE result).
+ *
+ * If we have a simple-form CASE with constant test expression,
+ * we substitute the constant value for contained CaseTestExpr
+ * placeholder nodes, so that we have the opportunity to reduce
+ * constant test conditions. For example this allows
+ * CASE 0 WHEN 0 THEN 1 ELSE 1/0 END
+ * to reduce to 1 rather than drawing a divide-by-0 error.
*----------
*/
CaseExpr *caseexpr = (CaseExpr *) node;
CaseExpr *newcase;
- List *newargs = NIL;
- Node *defresult;
- Const *const_input;
- List *arg;
+ Node *save_case_val;
+ Node *newarg;
+ List *newargs;
+ bool const_true_cond;
+ Node *defresult = NULL;
+ ListCell *arg;
+
+ /* Simplify the test expression, if any */
+ newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
+ context);
+
+ /* Set up for contained CaseTestExpr nodes */
+ save_case_val = context->case_val;
+ if (newarg && IsA(newarg, Const))
+ context->case_val = newarg;
+ else
+ context->case_val = NULL;
+ /* Simplify the WHEN clauses */
+ newargs = NIL;
+ const_true_cond = false;
foreach(arg, caseexpr->args)
{
- /* Simplify this alternative's condition and result */
- CaseWhen *casewhen = (CaseWhen *)
- expression_tree_mutator((Node *) lfirst(arg),
- eval_const_expressions_mutator,
- (void *) active_fns);
+ CaseWhen *oldcasewhen = (CaseWhen *) lfirst(arg);
+ Node *casecond;
+ Node *caseresult;
- Assert(IsA(casewhen, CaseWhen));
- if (casewhen->expr == NULL ||
- !IsA(casewhen->expr, Const))
- {
- newargs = lappend(newargs, casewhen);
- continue;
- }
- const_input = (Const *) casewhen->expr;
- if (const_input->constisnull ||
- !DatumGetBool(const_input->constvalue))
- continue; /* drop alternative with FALSE condition */
+ Assert(IsA(oldcasewhen, CaseWhen));
+
+ /* Simplify this alternative's test condition */
+ casecond =
+ eval_const_expressions_mutator((Node *) oldcasewhen->expr,
+ context);
/*
- * Found a TRUE condition. If it's the first (un-dropped)
- * alternative, the CASE reduces to just this alternative.
+ * If the test condition is constant FALSE (or NULL), then drop
+ * this WHEN clause completely, without processing the result.
*/
- if (newargs == NIL)
- return (Node *) casewhen->result;
+ if (casecond && IsA(casecond, Const))
+ {
+ Const *const_input = (Const *) casecond;
+
+ if (const_input->constisnull ||
+ !DatumGetBool(const_input->constvalue))
+ continue; /* drop alternative with FALSE condition */
+ /* Else it's constant TRUE */
+ const_true_cond = true;
+ }
+
+ /* Simplify this alternative's result value */
+ caseresult =
+ eval_const_expressions_mutator((Node *) oldcasewhen->result,
+ context);
+
+ /* If non-constant test condition, emit a new WHEN node */
+ if (!const_true_cond)
+ {
+ CaseWhen *newcasewhen = makeNode(CaseWhen);
+
+ newcasewhen->expr = (Expr *) casecond;
+ newcasewhen->result = (Expr *) caseresult;
+ newargs = lappend(newargs, newcasewhen);
+ continue;
+ }
/*
- * Otherwise, add it to the list, and drop all the rest.
+ * Found a TRUE condition, so none of the remaining alternatives
+ * can be reached. We treat the result as the default result.
*/
- newargs = lappend(newargs, casewhen);
+ defresult = caseresult;
break;
}
- /* Simplify the default result */
- defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
- active_fns);
+ /* Simplify the default result, unless we replaced it above */
+ if (!const_true_cond)
+ defresult =
+ eval_const_expressions_mutator((Node *) caseexpr->defresult,
+ context);
- /*
- * If no non-FALSE alternatives, CASE reduces to the default
- * result
- */
+ context->case_val = save_case_val;
+
+ /* If no non-FALSE alternatives, CASE reduces to the default result */
if (newargs == NIL)
return defresult;
/* Otherwise we need a new CASE node */
newcase = makeNode(CaseExpr);
newcase->casetype = caseexpr->casetype;
- newcase->arg = NULL;
+ newcase->arg = (Expr *) newarg;
newcase->args = newargs;
newcase->defresult = (Expr *) defresult;
return (Node *) newcase;
}
+ if (IsA(node, CaseTestExpr))
+ {
+ /*
+ * If we know a constant test value for the current CASE construct,
+ * substitute it for the placeholder. Else just return the
+ * placeholder as-is.
+ */
+ if (context->case_val)
+ return copyObject(context->case_val);
+ else
+ return copyObject(node);
+ }
if (IsA(node, ArrayExpr))
{
- ArrayExpr *arrayexpr = (ArrayExpr *) node;
- ArrayExpr *newarray;
- bool all_const = true;
- List *newelems = NIL;
- List *element;
+ ArrayExpr *arrayexpr = (ArrayExpr *) node;
+ ArrayExpr *newarray;
+ bool all_const = true;
+ List *newelems;
+ ListCell *element;
+ newelems = NIL;
foreach(element, arrayexpr->elements)
{
- Node *e;
+ Node *e;
e = eval_const_expressions_mutator((Node *) lfirst(element),
- active_fns);
+ context);
if (!IsA(e, Const))
all_const = false;
newelems = lappend(newelems, e);
newarray->array_typeid = arrayexpr->array_typeid;
newarray->element_typeid = arrayexpr->element_typeid;
newarray->elements = newelems;
- newarray->ndims = arrayexpr->ndims;
+ newarray->multidims = arrayexpr->multidims;
if (all_const)
return (Node *) evaluate_expr((Expr *) newarray,
newarray->array_typeid);
- return (Node *) newarray;
- }
- if (IsA(node, CoalesceExpr))
+ return (Node *) newarray;
+ }
+ if (IsA(node, CoalesceExpr))
+ {
+ CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+ CoalesceExpr *newcoalesce;
+ List *newargs;
+ ListCell *arg;
+
+ newargs = NIL;
+ foreach(arg, coalesceexpr->args)
+ {
+ Node *e;
+
+ e = eval_const_expressions_mutator((Node *) lfirst(arg),
+ context);
+
+ /*
+ * We can remove null constants from the list. For a non-null
+ * constant, if it has not been preceded by any other
+ * non-null-constant expressions then that is the result.
+ */
+ if (IsA(e, Const))
+ {
+ if (((Const *) e)->constisnull)
+ continue; /* drop null constant */
+ if (newargs == NIL)
+ return e; /* first expr */
+ }
+ newargs = lappend(newargs, e);
+ }
+
+ /* If all the arguments were constant null, the result is just null */
+ if (newargs == NIL)
+ return (Node *) makeNullConst(coalesceexpr->coalescetype);
+
+ newcoalesce = makeNode(CoalesceExpr);
+ newcoalesce->coalescetype = coalesceexpr->coalescetype;
+ newcoalesce->args = newargs;
+ return (Node *) newcoalesce;
+ }
+ if (IsA(node, FieldSelect))
+ {
+ /*
+ * We can optimize field selection from a whole-row Var into a simple
+ * Var. (This case won't be generated directly by the parser, because
+ * ParseComplexProjection short-circuits it. But it can arise while
+ * simplifying functions.) Also, we can optimize field selection from
+ * a RowExpr construct.
+ *
+ * We must however check that the declared type of the field is still
+ * the same as when the FieldSelect was created --- this can change if
+ * someone did ALTER COLUMN TYPE on the rowtype.
+ */
+ FieldSelect *fselect = (FieldSelect *) node;
+ FieldSelect *newfselect;
+ Node *arg;
+
+ arg = eval_const_expressions_mutator((Node *) fselect->arg,
+ context);
+ if (arg && IsA(arg, Var) &&
+ ((Var *) arg)->varattno == InvalidAttrNumber)
+ {
+ if (rowtype_field_matches(((Var *) arg)->vartype,
+ fselect->fieldnum,
+ fselect->resulttype,
+ fselect->resulttypmod))
+ return (Node *) makeVar(((Var *) arg)->varno,
+ fselect->fieldnum,
+ fselect->resulttype,
+ fselect->resulttypmod,
+ ((Var *) arg)->varlevelsup);
+ }
+ if (arg && IsA(arg, RowExpr))
+ {
+ RowExpr *rowexpr = (RowExpr *) arg;
+
+ if (fselect->fieldnum > 0 &&
+ fselect->fieldnum <= list_length(rowexpr->args))
+ {
+ Node *fld = (Node *) list_nth(rowexpr->args,
+ fselect->fieldnum - 1);
+
+ if (rowtype_field_matches(rowexpr->row_typeid,
+ fselect->fieldnum,
+ fselect->resulttype,
+ fselect->resulttypmod) &&
+ fselect->resulttype == exprType(fld) &&
+ fselect->resulttypmod == exprTypmod(fld))
+ return fld;
+ }
+ }
+ newfselect = makeNode(FieldSelect);
+ newfselect->arg = (Expr *) arg;
+ newfselect->fieldnum = fselect->fieldnum;
+ newfselect->resulttype = fselect->resulttype;
+ newfselect->resulttypmod = fselect->resulttypmod;
+ return (Node *) newfselect;
+ }
+ if (IsA(node, NullTest))
+ {
+ NullTest *ntest = (NullTest *) node;
+ NullTest *newntest;
+ Node *arg;
+
+ arg = eval_const_expressions_mutator((Node *) ntest->arg,
+ context);
+ if (arg && IsA(arg, RowExpr))
+ {
+ RowExpr *rarg = (RowExpr *) arg;
+ List *newargs = NIL;
+ ListCell *l;
+
+ /*
+ * We break ROW(...) IS [NOT] NULL into separate tests on its
+ * component fields. This form is usually more efficient to
+ * evaluate, as well as being more amenable to optimization.
+ */
+ foreach(l, rarg->args)
+ {
+ Node *relem = (Node *) lfirst(l);
+
+ /*
+ * A constant field refutes the whole NullTest if it's of the
+ * wrong nullness; else we can discard it.
+ */
+ if (relem && IsA(relem, Const))
+ {
+ Const *carg = (Const *) relem;
+
+ if (carg->constisnull ?
+ (ntest->nulltesttype == IS_NOT_NULL) :
+ (ntest->nulltesttype == IS_NULL))
+ return makeBoolConst(false, false);
+ continue;
+ }
+ newntest = makeNode(NullTest);
+ newntest->arg = (Expr *) relem;
+ newntest->nulltesttype = ntest->nulltesttype;
+ newargs = lappend(newargs, newntest);
+ }
+ /* If all the inputs were constants, result is TRUE */
+ if (newargs == NIL)
+ return makeBoolConst(true, false);
+ /* If only one nonconst input, it's the result */
+ if (list_length(newargs) == 1)
+ return (Node *) linitial(newargs);
+ /* Else we need an AND node */
+ return (Node *) make_andclause(newargs);
+ }
+ if (arg && IsA(arg, Const))
+ {
+ Const *carg = (Const *) arg;
+ bool result;
+
+ switch (ntest->nulltesttype)
+ {
+ case IS_NULL:
+ result = carg->constisnull;
+ break;
+ case IS_NOT_NULL:
+ result = !carg->constisnull;
+ break;
+ default:
+ elog(ERROR, "unrecognized nulltesttype: %d",
+ (int) ntest->nulltesttype);
+ result = false; /* keep compiler quiet */
+ break;
+ }
+
+ return makeBoolConst(result, false);
+ }
+
+ newntest = makeNode(NullTest);
+ newntest->arg = (Expr *) arg;
+ newntest->nulltesttype = ntest->nulltesttype;
+ return (Node *) newntest;
+ }
+ if (IsA(node, BooleanTest))
+ {
+ BooleanTest *btest = (BooleanTest *) node;
+ BooleanTest *newbtest;
+ Node *arg;
+
+ arg = eval_const_expressions_mutator((Node *) btest->arg,
+ context);
+ if (arg && IsA(arg, Const))
+ {
+ Const *carg = (Const *) arg;
+ bool result;
+
+ switch (btest->booltesttype)
+ {
+ case IS_TRUE:
+ result = (!carg->constisnull &&
+ DatumGetBool(carg->constvalue));
+ break;
+ case IS_NOT_TRUE:
+ result = (carg->constisnull ||
+ !DatumGetBool(carg->constvalue));
+ break;
+ case IS_FALSE:
+ result = (!carg->constisnull &&
+ !DatumGetBool(carg->constvalue));
+ break;
+ case IS_NOT_FALSE:
+ result = (carg->constisnull ||
+ DatumGetBool(carg->constvalue));
+ break;
+ case IS_UNKNOWN:
+ result = carg->constisnull;
+ break;
+ case IS_NOT_UNKNOWN:
+ result = !carg->constisnull;
+ break;
+ default:
+ elog(ERROR, "unrecognized booltesttype: %d",
+ (int) btest->booltesttype);
+ result = false; /* keep compiler quiet */
+ break;
+ }
+
+ return makeBoolConst(result, false);
+ }
+
+ newbtest = makeNode(BooleanTest);
+ newbtest->arg = (Expr *) arg;
+ newbtest->booltesttype = btest->booltesttype;
+ return (Node *) newbtest;
+ }
+
+ /*
+ * For any node type not handled above, we recurse using
+ * expression_tree_mutator, which will copy the node unchanged but try to
+ * simplify its arguments (if any) using this routine. For example: we
+ * cannot eliminate an ArrayRef node, but we might be able to simplify
+ * constant expressions in its subscripts.
+ */
+ return expression_tree_mutator(node, eval_const_expressions_mutator,
+ (void *) context);
+}
+
+/*
+ * Subroutine for eval_const_expressions: process arguments of an OR clause
+ *
+ * This includes flattening of nested ORs as well as recursion to
+ * eval_const_expressions to simplify the OR arguments.
+ *
+ * After simplification, OR arguments are handled as follows:
+ * non constant: keep
+ * FALSE: drop (does not affect result)
+ * TRUE: force result to TRUE
+ * NULL: keep only one
+ * We must keep one NULL input because ExecEvalOr returns NULL when no input
+ * is TRUE and at least one is NULL. We don't actually include the NULL
+ * here, that's supposed to be done by the caller.
+ *
+ * The output arguments *haveNull and *forceTrue must be initialized FALSE
+ * by the caller. They will be set TRUE if a null constant or true constant,
+ * respectively, is detected anywhere in the argument list.
+ */
+static List *
+simplify_or_arguments(List *args,
+ eval_const_expressions_context *context,
+ bool *haveNull, bool *forceTrue)
+{
+ List *newargs = NIL;
+ List *unprocessed_args;
+
+ /*
+ * Since the parser considers OR to be a binary operator, long OR lists
+ * become deeply nested expressions. We must flatten these into long
+ * argument lists of a single OR operator. To avoid blowing out the stack
+ * with recursion of eval_const_expressions, we resort to some tenseness
+ * here: we keep a list of not-yet-processed inputs, and handle flattening
+ * of nested ORs by prepending to the to-do list instead of recursing.
+ */
+ unprocessed_args = list_copy(args);
+ while (unprocessed_args)
+ {
+ Node *arg = (Node *) linitial(unprocessed_args);
+
+ unprocessed_args = list_delete_first(unprocessed_args);
+
+ /* flatten nested ORs as per above comment */
+ if (or_clause(arg))
+ {
+ List *subargs = list_copy(((BoolExpr *) arg)->args);
+
+ /* overly tense code to avoid leaking unused list header */
+ if (!unprocessed_args)
+ unprocessed_args = subargs;
+ else
+ {
+ List *oldhdr = unprocessed_args;
+
+ unprocessed_args = list_concat(subargs, unprocessed_args);
+ pfree(oldhdr);
+ }
+ continue;
+ }
+
+ /* If it's not an OR, simplify it */
+ arg = eval_const_expressions_mutator(arg, context);
+
+ /*
+ * It is unlikely but not impossible for simplification of a non-OR
+ * clause to produce an OR. Recheck, but don't be too tense about it
+ * since it's not a mainstream case. In particular we don't worry
+ * about const-simplifying the input twice.
+ */
+ if (or_clause(arg))
+ {
+ List *subargs = list_copy(((BoolExpr *) arg)->args);
+
+ unprocessed_args = list_concat(subargs, unprocessed_args);
+ continue;
+ }
+
+ /*
+ * OK, we have a const-simplified non-OR argument. Process it per
+ * comments above.
+ */
+ if (IsA(arg, Const))
+ {
+ Const *const_input = (Const *) arg;
+
+ if (const_input->constisnull)
+ *haveNull = true;
+ else if (DatumGetBool(const_input->constvalue))
+ {
+ *forceTrue = true;
+
+ /*
+ * Once we detect a TRUE result we can just exit the loop
+ * immediately. However, if we ever add a notion of
+ * non-removable functions, we'd need to keep scanning.
+ */
+ return NIL;
+ }
+ /* otherwise, we can drop the constant-false input */
+ continue;
+ }
+
+ /* else emit the simplified arg into the result list */
+ newargs = lappend(newargs, arg);
+ }
+
+ return newargs;
+}
+
+/*
+ * Subroutine for eval_const_expressions: process arguments of an AND clause
+ *
+ * This includes flattening of nested ANDs as well as recursion to
+ * eval_const_expressions to simplify the AND arguments.
+ *
+ * After simplification, AND arguments are handled as follows:
+ * non constant: keep
+ * TRUE: drop (does not affect result)
+ * FALSE: force result to FALSE
+ * NULL: keep only one
+ * We must keep one NULL input because ExecEvalAnd returns NULL when no input
+ * is FALSE and at least one is NULL. We don't actually include the NULL
+ * here, that's supposed to be done by the caller.
+ *
+ * The output arguments *haveNull and *forceFalse must be initialized FALSE
+ * by the caller. They will be set TRUE if a null constant or false constant,
+ * respectively, is detected anywhere in the argument list.
+ */
+static List *
+simplify_and_arguments(List *args,
+ eval_const_expressions_context *context,
+ bool *haveNull, bool *forceFalse)
+{
+ List *newargs = NIL;
+ List *unprocessed_args;
+
+ /* See comments in simplify_or_arguments */
+ unprocessed_args = list_copy(args);
+ while (unprocessed_args)
{
- CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
- CoalesceExpr *newcoalesce;
- List *newargs = NIL;
- List *arg;
+ Node *arg = (Node *) linitial(unprocessed_args);
- foreach(arg, coalesceexpr->args)
+ unprocessed_args = list_delete_first(unprocessed_args);
+
+ /* flatten nested ANDs as per above comment */
+ if (and_clause(arg))
{
- Node *e;
+ List *subargs = list_copy(((BoolExpr *) arg)->args);
- e = eval_const_expressions_mutator((Node *) lfirst(arg),
- active_fns);
- /*
- * We can remove null constants from the list.
- * For a non-null constant, if it has not been preceded by any
- * other non-null-constant expressions then that is the result.
- */
- if (IsA(e, Const))
+ /* overly tense code to avoid leaking unused list header */
+ if (!unprocessed_args)
+ unprocessed_args = subargs;
+ else
{
- if (((Const *) e)->constisnull)
- continue; /* drop null constant */
- if (newargs == NIL)
- return e; /* first expr */
+ List *oldhdr = unprocessed_args;
+
+ unprocessed_args = list_concat(subargs, unprocessed_args);
+ pfree(oldhdr);
}
- newargs = lappend(newargs, e);
+ continue;
}
- newcoalesce = makeNode(CoalesceExpr);
- newcoalesce->coalescetype = coalesceexpr->coalescetype;
- newcoalesce->args = newargs;
- return (Node *) newcoalesce;
- }
- if (IsA(node, FieldSelect))
- {
+ /* If it's not an AND, simplify it */
+ arg = eval_const_expressions_mutator(arg, context);
+
/*
- * We can optimize field selection from a whole-row Var into a
- * simple Var. (This case won't be generated directly by the
- * parser, because ParseComplexProjection short-circuits it.
- * But it can arise while simplifying functions.) If the argument
- * isn't a whole-row Var, just fall through to do generic processing.
+ * It is unlikely but not impossible for simplification of a non-AND
+ * clause to produce an AND. Recheck, but don't be too tense about it
+ * since it's not a mainstream case. In particular we don't worry
+ * about const-simplifying the input twice.
*/
- FieldSelect *fselect = (FieldSelect *) node;
- Var *argvar = (Var *) fselect->arg;
+ if (and_clause(arg))
+ {
+ List *subargs = list_copy(((BoolExpr *) arg)->args);
+
+ unprocessed_args = list_concat(subargs, unprocessed_args);
+ continue;
+ }
- if (argvar && IsA(argvar, Var) &&
- argvar->varattno == InvalidAttrNumber)
+ /*
+ * OK, we have a const-simplified non-AND argument. Process it per
+ * comments above.
+ */
+ if (IsA(arg, Const))
{
- return (Node *) makeVar(argvar->varno,
- fselect->fieldnum,
- fselect->resulttype,
- fselect->resulttypmod,
- argvar->varlevelsup);
+ Const *const_input = (Const *) arg;
+
+ if (const_input->constisnull)
+ *haveNull = true;
+ else if (!DatumGetBool(const_input->constvalue))
+ {
+ *forceFalse = true;
+
+ /*
+ * Once we detect a FALSE result we can just exit the loop
+ * immediately. However, if we ever add a notion of
+ * non-removable functions, we'd need to keep scanning.
+ */
+ return NIL;
+ }
+ /* otherwise, we can drop the constant-true input */
+ continue;
}
+
+ /* else emit the simplified arg into the result list */
+ newargs = lappend(newargs, arg);
}
- /*
- * For any node type not handled above, we recurse using
- * expression_tree_mutator, which will copy the node unchanged but try
- * to simplify its arguments (if any) using this routine. For example:
- * we cannot eliminate an ArrayRef node, but we might be able to
- * simplify constant expressions in its subscripts.
- */
- return expression_tree_mutator(node, eval_const_expressions_mutator,
- (void *) active_fns);
+ return newargs;
+}
+
+/*
+ * Subroutine for eval_const_expressions: try to simplify boolean equality
+ *
+ * Input is the list of simplified arguments to the operator.
+ * Returns a simplified expression if successful, or NULL if cannot
+ * simplify the expression.
+ *
+ * The idea here is to reduce "x = true" to "x" and "x = false" to "NOT x".
+ * This is only marginally useful in itself, but doing it in constant folding
+ * ensures that we will recognize the two forms as being equivalent in, for
+ * example, partial index matching.
+ *
+ * We come here only if simplify_function has failed; therefore we cannot
+ * see two constant inputs, nor a constant-NULL input.
+ */
+static Expr *
+simplify_boolean_equality(List *args)
+{
+ Expr *leftop;
+ Expr *rightop;
+
+ Assert(list_length(args) == 2);
+ leftop = linitial(args);
+ rightop = lsecond(args);
+ if (leftop && IsA(leftop, Const))
+ {
+ Assert(!((Const *) leftop)->constisnull);
+ if (DatumGetBool(((Const *) leftop)->constvalue))
+ return rightop; /* true = foo */
+ else
+ return make_notclause(rightop); /* false = foo */
+ }
+ if (rightop && IsA(rightop, Const))
+ {
+ Assert(!((Const *) rightop)->constisnull);
+ if (DatumGetBool(((Const *) rightop)->constvalue))
+ return leftop; /* foo = true */
+ else
+ return make_notclause(leftop); /* foo = false */
+ }
+ return NULL;
}
/*
*
* 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;
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(func_tuple))
- elog(ERROR, "Function OID %u does not exist", funcid);
+ elog(ERROR, "cache lookup failed for function %u", funcid);
- newexpr = evaluate_function(funcid, result_type, args, func_tuple);
+ newexpr = evaluate_function(funcid, result_type, args,
+ func_tuple, context);
if (!newexpr && allow_inline)
newexpr = inline_function(funcid, result_type, args,
- func_tuple, active_fns);
+ func_tuple, context);
ReleaseSysCache(func_tuple);
*
* We can do this if the function is strict and has any constant-null inputs
* (just return a null constant), or if the function is immutable and has all
- * constant inputs (call it and return the result as a Const node).
+ * constant inputs (call it and return the result as a Const node). In
+ * estimation mode we are willing to pre-evaluate stable functions too.
*
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function.
*/
static Expr *
evaluate_function(Oid funcid, Oid result_type, List *args,
- HeapTuple func_tuple)
+ HeapTuple func_tuple,
+ eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
bool has_nonconst_input = false;
bool has_null_input = false;
- List *arg;
+ ListCell *arg;
FuncExpr *newexpr;
/*
if (funcform->proretset)
return NULL;
+ /*
+ * Can't simplify if it returns RECORD. The immediate problem is that it
+ * will be needing an expected tupdesc which we can't supply here.
+ *
+ * In the case where it has OUT parameters, it could get by without an
+ * expected tupdesc, but we still have issues: get_expr_result_type()
+ * doesn't know how to extract type info from a RECORD constant, and in
+ * the case of a NULL function result there doesn't seem to be any clean
+ * way to fix that. In view of the likelihood of there being still other
+ * gotchas, seems best to leave the function call unreduced.
+ */
+ if (funcform->prorettype == RECORDOID)
+ return NULL;
+
/*
* Check for constant inputs and especially constant-NULL inputs.
*/
}
/*
- * If the function is strict and has a constant-NULL input, it will
- * never be called at all, so we can replace the call by a NULL
- * constant, even if there are other inputs that aren't constant,
- * and even if the function is not otherwise immutable.
+ * If the function is strict and has a constant-NULL input, it will never
+ * be called at all, so we can replace the call by a NULL constant, even
+ * if there are other inputs that aren't constant, and even if the
+ * function is not otherwise immutable.
*/
if (funcform->proisstrict && has_null_input)
return (Expr *) makeNullConst(result_type);
/*
- * Otherwise, can simplify only if the function is immutable and
- * all inputs are constants. (For a non-strict function, constant NULL
- * inputs are treated the same as constant non-NULL inputs.)
+ * Otherwise, can simplify only if all inputs are constants. (For a
+ * non-strict function, constant NULL inputs are treated the same as
+ * constant non-NULL inputs.)
+ */
+ if (has_nonconst_input)
+ return NULL;
+
+ /*
+ * Ordinarily we are only allowed to simplify immutable functions. But for
+ * purposes of estimation, we consider it okay to simplify functions that
+ * are merely stable; the risk that the result might change from planning
+ * time to execution time is worth taking in preference to not being able
+ * to estimate the value at all.
*/
- if (funcform->provolatile != PROVOLATILE_IMMUTABLE ||
- has_nonconst_input)
+ if (funcform->provolatile == PROVOLATILE_IMMUTABLE)
+ /* okay */ ;
+ else if (context->estimate && funcform->provolatile == PROVOLATILE_STABLE)
+ /* okay */ ;
+ else
return NULL;
/*
newexpr->funcid = funcid;
newexpr->funcresulttype = result_type;
newexpr->funcretset = false;
- newexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */
+ newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
newexpr->args = args;
return evaluate_expr((Expr *) newexpr, result_type);
* so we keep track of which functions we are already expanding and
* do not re-expand them. Also, if a parameter is used more than once
* in the SQL-function body, we require it not to contain any volatile
- * functions or sublinks --- volatiles might deliver inconsistent answers,
- * and subplans might be unreasonably expensive to evaluate multiple times.
+ * functions (volatiles might deliver inconsistent answers) nor to be
+ * unreasonably expensive to evaluate. The expensiveness check not only
+ * prevents us from doing multiple evaluations of an expensive parameter
+ * at runtime, but is a safety value to limit growth of an expression due
+ * to repeated inlining.
+ *
* We must also beware of changing the volatility or strictness status of
* functions by inlining them.
*
*/
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);
- char result_typtype;
+ bool polymorphic = false;
+ Oid *argtypes;
char *src;
Datum tmp;
bool isNull;
MemoryContext oldcxt;
MemoryContext mycxt;
+ ErrorContextCallback sqlerrcontext;
List *raw_parsetree_list;
List *querytree_list;
Query *querytree;
Node *newexpr;
int *usecounts;
- List *arg;
+ ListCell *arg;
int i;
/*
- * Forget it if the function is not SQL-language or has other
- * showstopper properties. (The nargs check is just paranoia.)
+ * Forget it if the function is not SQL-language or has other showstopper
+ * properties. (The nargs check is just paranoia.)
*/
if (funcform->prolang != SQLlanguageId ||
funcform->prosecdef ||
funcform->proretset ||
- funcform->pronargs != length(args))
- return NULL;
-
- /* Forget it if declared return type is tuple or void */
- result_typtype = get_typtype(funcform->prorettype);
- if (result_typtype != 'b' &&
- result_typtype != 'd')
+ funcform->pronargs != list_length(args))
return NULL;
/* Check for recursive function, and give up trying to expand if so */
- if (oidMember(funcid, active_fns))
+ if (list_member_oid(context->active_fns, funcid))
return NULL;
/* Check permission to call function (fail later, if not) */
return NULL;
/*
- * Make a temporary memory context, so that we don't leak all the
- * stuff that parsing might create.
+ * Setup error traceback support for ereport(). This is so that we can
+ * finger the function that bad information came from.
+ */
+ sqlerrcontext.callback = sql_inline_error_callback;
+ sqlerrcontext.arg = func_tuple;
+ sqlerrcontext.previous = error_context_stack;
+ error_context_stack = &sqlerrcontext;
+
+ /*
+ * Make a temporary memory context, so that we don't leak all the stuff
+ * that parsing might create.
*/
mycxt = AllocSetContextCreate(CurrentMemoryContext,
"inline_function",
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(mycxt);
+ /* Check for polymorphic arguments, and substitute actual arg types */
+ argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
+ memcpy(argtypes, funcform->proargtypes.values,
+ funcform->pronargs * sizeof(Oid));
+ for (i = 0; i < funcform->pronargs; i++)
+ {
+ if (argtypes[i] == ANYARRAYOID ||
+ argtypes[i] == ANYELEMENTOID)
+ {
+ polymorphic = true;
+ argtypes[i] = exprType((Node *) list_nth(args, i));
+ }
+ }
+
+ if (funcform->prorettype == ANYARRAYOID ||
+ funcform->prorettype == ANYELEMENTOID)
+ polymorphic = true;
+
/* Fetch and parse the function body */
tmp = SysCacheGetAttr(PROCOID,
func_tuple,
Anum_pg_proc_prosrc,
&isNull);
if (isNull)
- elog(ERROR, "inline_function: null prosrc for procedure %u",
- funcid);
+ elog(ERROR, "null prosrc for function %u", funcid);
src = DatumGetCString(DirectFunctionCall1(textout, tmp));
/*
- * We just do parsing and parse analysis, not rewriting, because
- * rewriting will not affect table-free-SELECT-only queries, which is all
- * that we care about. Also, we can punt as soon as we detect more than
- * one command in the function body.
+ * We just do parsing and parse analysis, not rewriting, because rewriting
+ * will not affect table-free-SELECT-only queries, which is all that we
+ * care about. Also, we can punt as soon as we detect more than one
+ * command in the function body.
*/
raw_parsetree_list = pg_parse_query(src);
- if (length(raw_parsetree_list) != 1)
+ if (list_length(raw_parsetree_list) != 1)
goto fail;
- querytree_list = parse_analyze(lfirst(raw_parsetree_list),
- funcform->proargtypes,
- funcform->pronargs);
+ querytree_list = parse_analyze(linitial(raw_parsetree_list), src,
+ argtypes, funcform->pronargs);
- if (length(querytree_list) != 1)
+ if (list_length(querytree_list) != 1)
goto fail;
- querytree = (Query *) lfirst(querytree_list);
+ querytree = (Query *) linitial(querytree_list);
/*
* The single command must be a simple "SELECT expression".
*/
if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT ||
- querytree->resultRelation != 0 ||
querytree->into ||
querytree->hasAggs ||
querytree->hasSubLinks ||
querytree->limitOffset ||
querytree->limitCount ||
querytree->setOperations ||
- length(querytree->targetList) != 1)
+ list_length(querytree->targetList) != 1)
goto fail;
- newexpr = (Node *) ((TargetEntry *) lfirst(querytree->targetList))->expr;
+ newexpr = (Node *) ((TargetEntry *) linitial(querytree->targetList))->expr;
+
+ /*
+ * If the function has any arguments declared as polymorphic types, then
+ * it wasn't type-checked at definition time; must do so now. (This will
+ * raise an error if wrong, but that's okay since the function would fail
+ * at runtime anyway. Note we do not try this until we have verified that
+ * no rewriting was needed; that's probably not important, but let's be
+ * careful.)
+ */
+ if (polymorphic)
+ (void) check_sql_fn_retval(funcid, result_type, querytree_list, NULL);
/*
- * Additional validity checks on the expression. It mustn't return a
- * set, and it mustn't be more volatile than the surrounding function
- * (this is to avoid breaking hacks that involve pretending a function
- * is immutable when it really ain't). If the surrounding function is
- * declared strict, then the expression must contain only strict constructs
- * and must use all of the function parameters (this is overkill, but
- * an exact analysis is hard).
+ * Additional validity checks on the expression. It mustn't return a set,
+ * and it mustn't be more volatile than the surrounding function (this is
+ * to avoid breaking hacks that involve pretending a function is immutable
+ * when it really ain't). If the surrounding function is declared strict,
+ * then the expression must contain only strict constructs and must use
+ * all of the function parameters (this is overkill, but an exact analysis
+ * is hard).
*/
if (expression_returns_set(newexpr))
goto fail;
contain_mutable_functions(newexpr))
goto fail;
else if (funcform->provolatile == PROVOLATILE_STABLE &&
- contain_volatile_functions(newexpr))
+ contain_volatile_functions(newexpr))
goto fail;
if (funcform->proisstrict &&
goto fail;
/*
- * We may be able to do it; there are still checks on parameter usage
- * to make, but those are most easily done in combination with the
- * actual substitution of the inputs. So start building expression
- * with inputs substituted.
+ * We may be able to do it; there are still checks on parameter usage to
+ * make, but those are most easily done in combination with the actual
+ * substitution of the inputs. So start building expression with inputs
+ * substituted.
*/
- usecounts = (int *) palloc0((funcform->pronargs + 1) * sizeof(int));
+ usecounts = (int *) palloc0(funcform->pronargs * sizeof(int));
newexpr = substitute_actual_parameters(newexpr, funcform->pronargs,
args, usecounts);
i = 0;
foreach(arg, args)
{
- Node *param = lfirst(arg);
+ Node *param = lfirst(arg);
if (usecounts[i] == 0)
{
}
else if (usecounts[i] != 1)
{
- /* Param used multiple times: uncool if volatile or expensive */
- if (contain_volatile_functions(param) ||
- contain_subplans(param))
+ /* Param used multiple times: uncool if expensive or volatile */
+ QualCost eval_cost;
+
+ /*
+ * We define "expensive" as "contains any subplan or more than 10
+ * operators". Note that the subplan search has to be done
+ * explicitly, since cost_qual_eval() will barf on unplanned
+ * subselects.
+ */
+ if (contain_subplans(param))
+ goto fail;
+ cost_qual_eval(&eval_cost, list_make1(param));
+ if (eval_cost.startup + eval_cost.per_tuple >
+ 10 * cpu_operator_cost)
+ goto fail;
+
+ /*
+ * Check volatility last since this is more expensive than the
+ * above tests
+ */
+ if (contain_volatile_functions(param))
goto fail;
}
i++;
MemoryContextDelete(mycxt);
/*
- * Recursively try to simplify the modified expression. Here we must
- * add the current function to the context list of active functions.
+ * 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,
- lconso(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;
return (Expr *) newexpr;
fail:
MemoryContextSwitchTo(oldcxt);
MemoryContextDelete(mycxt);
+ error_context_stack = sqlerrcontext.previous;
return NULL;
}
{
substitute_actual_parameters_context context;
- context.nargs = nargs;
+ context.nargs = nargs;
context.args = args;
context.usecounts = usecounts;
static Node *
substitute_actual_parameters_mutator(Node *node,
- substitute_actual_parameters_context *context)
+ substitute_actual_parameters_context *context)
{
if (node == NULL)
return NULL;
{
Param *param = (Param *) node;
- if (param->paramkind != PARAM_NUM)
- elog(ERROR, "substitute_actual_parameters_mutator: unexpected paramkind");
+ if (param->paramkind != PARAM_EXTERN)
+ elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind);
if (param->paramid <= 0 || param->paramid > context->nargs)
- elog(ERROR, "substitute_actual_parameters_mutator: unexpected paramid");
+ elog(ERROR, "invalid paramid: %d", param->paramid);
/* Count usage of parameter */
context->usecounts[param->paramid - 1]++;
/* Select the appropriate actual arg and replace the Param with it */
/* We don't need to copy at this time (it'll get done later) */
- return nth(param->paramid - 1, context->args);
+ return list_nth(context->args, param->paramid - 1);
}
return expression_tree_mutator(node, substitute_actual_parameters_mutator,
(void *) context);
}
+/*
+ * error context callback to let us supply a call-stack traceback
+ */
+static void
+sql_inline_error_callback(void *arg)
+{
+ HeapTuple func_tuple = (HeapTuple) arg;
+ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+ int syntaxerrposition;
+
+ /* If it's a syntax error, convert to internal syntax error report */
+ syntaxerrposition = geterrposition();
+ if (syntaxerrposition > 0)
+ {
+ bool isnull;
+ Datum tmp;
+ char *prosrc;
+
+ tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null prosrc");
+ prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+ errposition(0);
+ internalerrposition(syntaxerrposition);
+ internalerrquery(prosrc);
+ }
+
+ errcontext("SQL function \"%s\" during inlining",
+ NameStr(funcform->proname));
+}
+
/*
* evaluate_expr: pre-evaluate a constant expression
*
/*
* And evaluate it.
*
- * It is OK to use a default econtext because none of the
- * ExecEvalExpr() code used in this situation will use econtext. That
- * might seem fortuitous, but it's not so unreasonable --- a constant
- * expression does not depend on context, by definition, n'est ce pas?
+ * It is OK to use a default econtext because none of the ExecEvalExpr()
+ * code used in this situation will use econtext. That might seem
+ * fortuitous, but it's not so unreasonable --- a constant expression does
+ * not depend on context, by definition, n'est ce pas?
*/
const_val = ExecEvalExprSwitchContext(exprstate,
GetPerTupleExprContext(estate),
* stage. In particular, it handles List nodes since a cnf-ified qual clause
* will have List structure at the top level, and it handles TargetEntry nodes
* so that a scan of a target list can be handled without additional code.
- * (But only the "expr" part of a TargetEntry is examined, unless the walker
- * chooses to process TargetEntry nodes specially.) Also, RangeTblRef,
- * FromExpr, JoinExpr, and SetOperationStmt nodes are handled, so that query
- * jointrees and setOperation trees can be processed without additional code.
+ * Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are
+ * handled, so that query jointrees and setOperation trees can be processed
+ * without additional code.
*
- * expression_tree_walker will handle SubLink nodes by recursing normally into
- * the "lefthand" arguments (which are expressions belonging to the outer
+ * expression_tree_walker will handle SubLink nodes by recursing normally
+ * into the "testexpr" subtree (which is an expression belonging to the outer
* plan). It will also call the walker on the sub-Query node; however, when
* expression_tree_walker itself is called on a Query node, it does nothing
* and returns "false". The net effect is that unless the walker does
* walker on all the expression subtrees of the given Query node.
*
* expression_tree_walker will handle SubPlan nodes by recursing normally
- * into the "exprs" and "args" lists (which are expressions belonging to
- * the outer plan). It will not touch the completed subplan, however. Since
+ * into the "testexpr" and the "args" list (which are expressions belonging to
+ * the outer plan). It will not touch the completed subplan, however. Since
* there is no link to the original Query, it is not possible to recurse into
* subselects of an already-planned expression tree. This is OK for current
* uses, but may need to be revisited in future.
bool (*walker) (),
void *context)
{
- List *temp;
+ ListCell *temp;
/*
- * The walker has already visited the current node, and so we need
- * only recurse into any sub-nodes it has.
+ * The walker has already visited the current node, and so we need only
+ * recurse into any sub-nodes it has.
*
* We assume that the walker is not interested in List nodes per se, so
* when we expect a List we just recurse directly to self without
*/
if (node == NULL)
return false;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
switch (nodeTag(node))
{
case T_Var:
case T_Const:
case T_Param:
case T_CoerceToDomainValue:
+ case T_CaseTestExpr:
+ case T_SetToDefault:
case T_RangeTblRef:
- /* primitive node types with no subnodes */
+ case T_OuterJoinInfo:
+ /* primitive node types with no expression subnodes */
break;
case T_Aggref:
- return walker(((Aggref *) node)->target, context);
+ {
+ Aggref *expr = (Aggref *) node;
+
+ /* recurse directly on List */
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ }
+ break;
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
return true;
}
break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ }
+ break;
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
{
SubLink *sublink = (SubLink *) node;
- if (expression_tree_walker((Node *) sublink->lefthand,
- walker, context))
+ if (walker(sublink->testexpr, context))
return true;
+
/*
- * Also invoke the walker on the sublink's Query node, so
- * it can recurse into the sub-query if it wants to.
+ * Also invoke the walker on the sublink's Query node, so it
+ * can recurse into the sub-query if it wants to.
*/
return walker(sublink->subselect, context);
}
break;
case T_SubPlan:
{
- SubPlan *subplan = (SubPlan *) node;
+ SubPlan *subplan = (SubPlan *) node;
- /* recurse into the exprs list, but not into the Plan */
- if (expression_tree_walker((Node *) subplan->exprs,
- walker, context))
+ /* recurse into the testexpr, but not into the Plan */
+ if (walker(subplan->testexpr, context))
return true;
/* also examine args list */
if (expression_tree_walker((Node *) subplan->args,
break;
case T_FieldSelect:
return walker(((FieldSelect *) node)->arg, context);
+ case T_FieldStore:
+ {
+ FieldStore *fstore = (FieldStore *) node;
+
+ if (walker(fstore->arg, context))
+ return true;
+ if (walker(fstore->newvals, context))
+ return true;
+ }
+ break;
case T_RelabelType:
return walker(((RelabelType *) node)->arg, context);
+ case T_ConvertRowtypeExpr:
+ return walker(((ConvertRowtypeExpr *) node)->arg, context);
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
+ if (walker(caseexpr->arg, context))
+ return true;
/* we assume walker doesn't care about CaseWhens, either */
foreach(temp, caseexpr->args)
{
if (walker(when->result, context))
return true;
}
- /* caseexpr->arg should be null, but we'll check it anyway */
- if (walker(caseexpr->arg, context))
- return true;
if (walker(caseexpr->defresult, context))
return true;
}
break;
case T_ArrayExpr:
return walker(((ArrayExpr *) node)->elements, context);
+ case T_RowExpr:
+ return walker(((RowExpr *) node)->args, context);
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+
+ if (walker(rcexpr->largs, context))
+ return true;
+ if (walker(rcexpr->rargs, context))
+ return true;
+ }
+ break;
case T_CoalesceExpr:
return walker(((CoalesceExpr *) node)->args, context);
+ case T_MinMaxExpr:
+ return walker(((MinMaxExpr *) node)->args, context);
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+
+ if (walker(xexpr->named_args, context))
+ return true;
+ /* we assume walker doesn't care about arg_names */
+ if (walker(xexpr->args, context))
+ return true;
+ }
+ break;
case T_NullIfExpr:
return walker(((NullIfExpr *) node)->args, context);
case T_NullTest:
return true;
}
break;
+ case T_AppendRelInfo:
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+ if (expression_tree_walker((Node *) appinfo->translated_vars,
+ walker, context))
+ return true;
+ }
+ break;
default:
- elog(ERROR, "expression_tree_walker: Unexpected node type %d",
- nodeTag(node));
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
break;
}
return false;
void *context,
int flags)
{
- List *rt;
-
Assert(query != NULL && IsA(query, Query));
if (walker((Node *) query->targetList, context))
return true;
+ if (walker((Node *) query->returningList, context))
+ return true;
if (walker((Node *) query->jointree, context))
return true;
if (walker(query->setOperations, context))
return true;
if (walker(query->havingQual, context))
return true;
- if (walker(query->in_info_list, context))
+ if (walker(query->limitOffset, context))
+ return true;
+ if (walker(query->limitCount, context))
return true;
- foreach(rt, query->rtable)
+ if (range_table_walker(query->rtable, walker, context, flags))
+ return true;
+ if (query->utilityStmt)
+ {
+ /*
+ * Certain utility commands contain general-purpose Querys embedded in
+ * them --- if this is one, invoke the walker on the sub-Query.
+ */
+ if (IsA(query->utilityStmt, CopyStmt))
+ {
+ if (walker(((CopyStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, DeclareCursorStmt))
+ {
+ if (walker(((DeclareCursorStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, ExplainStmt))
+ {
+ if (walker(((ExplainStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, PrepareStmt))
+ {
+ if (walker(((PrepareStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ if (IsA(query->utilityStmt, ViewStmt))
+ {
+ if (walker(((ViewStmt *) query->utilityStmt)->query, context))
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * range_table_walker is just the part of query_tree_walker that scans
+ * a query's rangetable. This is split out since it can be useful on
+ * its own.
+ */
+bool
+range_table_walker(List *rtable,
+ bool (*walker) (),
+ void *context,
+ int flags)
+{
+ ListCell *rt;
+
+ foreach(rt, rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
/* nothing to do */
break;
case RTE_SUBQUERY:
- if (! (flags & QTW_IGNORE_RT_SUBQUERIES))
+ if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
if (walker(rte->subquery, context))
return true;
break;
case RTE_JOIN:
- if (! (flags & QTW_IGNORE_JOINALIASES))
+ if (!(flags & QTW_IGNORE_JOINALIASES))
if (walker(rte->joinaliasvars, context))
return true;
break;
if (walker(rte->funcexpr, context))
return true;
break;
+ case RTE_VALUES:
+ if (walker(rte->values_lists, context))
+ return true;
+ break;
}
}
return false;
* and qualifier clauses during the planning stage.
*
* expression_tree_mutator will handle SubLink nodes by recursing normally
- * into the "lefthand" arguments (which are expressions belonging to the outer
+ * into the "testexpr" subtree (which is an expression belonging to the outer
* plan). It will also call the mutator on the sub-Query node; however, when
* expression_tree_mutator itself is called on a Query node, it does nothing
* and returns the unmodified Query node. The net effect is that unless the
* SubLink node. Mutators that want to descend into sub-selects will usually
* do so by recognizing Query nodes and calling query_tree_mutator (below).
*
- * expression_tree_mutator will handle a SubPlan node by recursing into
- * the "exprs" and "args" lists (which belong to the outer plan), but it
+ * expression_tree_mutator will handle a SubPlan node by recursing into the
+ * "testexpr" and the "args" list (which belong to the outer plan), but it
* will simply copy the link to the inner plan, since that's typically what
* expression tree mutators want. A mutator that wants to modify the subplan
* can force appropriate behavior by recognizing SubPlan expression nodes
void *context)
{
/*
- * The mutator has already decided not to modify the current node, but
- * we must call the mutator for any sub-nodes.
+ * The mutator has already decided not to modify the current node, but we
+ * must call the mutator for any sub-nodes.
*/
#define FLATCOPY(newnode, node, nodetype) \
if (node == NULL)
return NULL;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
switch (nodeTag(node))
{
case T_Var:
case T_Const:
case T_Param:
case T_CoerceToDomainValue:
+ case T_CaseTestExpr:
+ case T_SetToDefault:
case T_RangeTblRef:
- /* primitive node types with no subnodes */
+ case T_OuterJoinInfo:
+ /* primitive node types with no expression subnodes */
return (Node *) copyObject(node);
case T_Aggref:
{
Aggref *newnode;
FLATCOPY(newnode, aggref, Aggref);
- MUTATE(newnode->target, aggref->target, Expr *);
+ MUTATE(newnode->args, aggref->args, List *);
return (Node *) newnode;
}
break;
break;
case T_DistinctExpr:
{
- DistinctExpr *expr = (DistinctExpr *) node;
- DistinctExpr *newnode;
+ DistinctExpr *expr = (DistinctExpr *) node;
+ DistinctExpr *newnode;
FLATCOPY(newnode, expr, DistinctExpr);
MUTATE(newnode->args, expr->args, List *);
return (Node *) newnode;
}
break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+ ScalarArrayOpExpr *newnode;
+
+ FLATCOPY(newnode, expr, ScalarArrayOpExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
SubLink *newnode;
FLATCOPY(newnode, sublink, SubLink);
- MUTATE(newnode->lefthand, sublink->lefthand, List *);
+ MUTATE(newnode->testexpr, sublink->testexpr, Node *);
+
/*
- * Also invoke the mutator on the sublink's Query node, so
- * it can recurse into the sub-query if it wants to.
+ * Also invoke the mutator on the sublink's Query node, so it
+ * can recurse into the sub-query if it wants to.
*/
MUTATE(newnode->subselect, sublink->subselect, Node *);
return (Node *) newnode;
break;
case T_SubPlan:
{
- SubPlan *subplan = (SubPlan *) node;
- SubPlan *newnode;
+ SubPlan *subplan = (SubPlan *) node;
+ SubPlan *newnode;
FLATCOPY(newnode, subplan, SubPlan);
- /* transform exprs list */
- MUTATE(newnode->exprs, subplan->exprs, List *);
+ /* transform testexpr */
+ MUTATE(newnode->testexpr, subplan->testexpr, Node *);
/* transform args list (params to be passed to subplan) */
MUTATE(newnode->args, subplan->args, List *);
/* but not the sub-Plan itself, which is referenced as-is */
return (Node *) newnode;
}
break;
+ case T_FieldStore:
+ {
+ FieldStore *fstore = (FieldStore *) node;
+ FieldStore *newnode;
+
+ FLATCOPY(newnode, fstore, FieldStore);
+ MUTATE(newnode->arg, fstore->arg, Expr *);
+ MUTATE(newnode->newvals, fstore->newvals, List *);
+ newnode->fieldnums = list_copy(fstore->fieldnums);
+ return (Node *) newnode;
+ }
+ break;
case T_RelabelType:
{
RelabelType *relabel = (RelabelType *) node;
return (Node *) newnode;
}
break;
+ case T_ConvertRowtypeExpr:
+ {
+ ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
+ ConvertRowtypeExpr *newnode;
+
+ FLATCOPY(newnode, convexpr, ConvertRowtypeExpr);
+ MUTATE(newnode->arg, convexpr->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
CaseExpr *newnode;
FLATCOPY(newnode, caseexpr, CaseExpr);
- MUTATE(newnode->args, caseexpr->args, List *);
- /* caseexpr->arg should be null, but we'll check it anyway */
MUTATE(newnode->arg, caseexpr->arg, Expr *);
+ MUTATE(newnode->args, caseexpr->args, List *);
MUTATE(newnode->defresult, caseexpr->defresult, Expr *);
return (Node *) newnode;
}
break;
case T_ArrayExpr:
{
- ArrayExpr *arrayexpr = (ArrayExpr *) node;
- ArrayExpr *newnode;
+ ArrayExpr *arrayexpr = (ArrayExpr *) node;
+ ArrayExpr *newnode;
FLATCOPY(newnode, arrayexpr, ArrayExpr);
MUTATE(newnode->elements, arrayexpr->elements, List *);
return (Node *) newnode;
}
break;
+ case T_RowExpr:
+ {
+ RowExpr *rowexpr = (RowExpr *) node;
+ RowExpr *newnode;
+
+ FLATCOPY(newnode, rowexpr, RowExpr);
+ MUTATE(newnode->args, rowexpr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ RowCompareExpr *newnode;
+
+ FLATCOPY(newnode, rcexpr, RowCompareExpr);
+ MUTATE(newnode->largs, rcexpr->largs, List *);
+ MUTATE(newnode->rargs, rcexpr->rargs, List *);
+ return (Node *) newnode;
+ }
+ break;
case T_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
return (Node *) newnode;
}
break;
+ case T_MinMaxExpr:
+ {
+ MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+ MinMaxExpr *newnode;
+
+ FLATCOPY(newnode, minmaxexpr, MinMaxExpr);
+ MUTATE(newnode->args, minmaxexpr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+ XmlExpr *newnode;
+
+ FLATCOPY(newnode, xexpr, XmlExpr);
+ MUTATE(newnode->named_args, xexpr->named_args, List *);
+ /* assume mutator does not care about arg_names */
+ MUTATE(newnode->args, xexpr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
case T_NullIfExpr:
{
- NullIfExpr *expr = (NullIfExpr *) node;
- NullIfExpr *newnode;
+ NullIfExpr *expr = (NullIfExpr *) node;
+ NullIfExpr *newnode;
FLATCOPY(newnode, expr, NullIfExpr);
MUTATE(newnode->args, expr->args, List *);
break;
case T_TargetEntry:
{
- /*
- * We mutate the expression, but not the resdom, by
- * default.
- */
TargetEntry *targetentry = (TargetEntry *) node;
TargetEntry *newnode;
case T_List:
{
/*
- * We assume the mutator isn't interested in the list
- * nodes per se, so just invoke it on each list element.
- * NOTE: this would fail badly on a list with integer
- * elements!
+ * We assume the mutator isn't interested in the list nodes
+ * per se, so just invoke it on each list element. NOTE: this
+ * would fail badly on a list with integer elements!
*/
- List *resultlist = NIL;
- List *temp;
+ List *resultlist;
+ ListCell *temp;
+ resultlist = NIL;
foreach(temp, (List *) node)
{
resultlist = lappend(resultlist,
return (Node *) newnode;
}
break;
+ case T_AppendRelInfo:
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+ AppendRelInfo *newnode;
+
+ FLATCOPY(newnode, appinfo, AppendRelInfo);
+ MUTATE(newnode->translated_vars, appinfo->translated_vars, List *);
+ return (Node *) newnode;
+ }
+ break;
default:
- elog(ERROR, "expression_tree_mutator: Unexpected node type %d",
- nodeTag(node));
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
break;
}
/* can't get here, but keep compiler happy */
* indicated items. (More flag bits may be added as needed.)
*
* Normally the Query node itself is copied, but some callers want it to be
- * modified in-place; they must pass QTW_DONT_COPY_QUERY in flags. All
+ * modified in-place; they must pass QTW_DONT_COPY_QUERY in flags. All
* modified substructure is safely copied in any case.
*/
Query *
void *context,
int flags)
{
- List *newrt = NIL;
- List *rt;
-
Assert(query != NULL && IsA(query, Query));
- if (! (flags & QTW_DONT_COPY_QUERY))
+ if (!(flags & QTW_DONT_COPY_QUERY))
{
- Query *newquery;
+ Query *newquery;
FLATCOPY(newquery, query, Query);
query = newquery;
}
MUTATE(query->targetList, query->targetList, List *);
+ MUTATE(query->returningList, query->returningList, List *);
MUTATE(query->jointree, query->jointree, FromExpr *);
MUTATE(query->setOperations, query->setOperations, Node *);
MUTATE(query->havingQual, query->havingQual, Node *);
- MUTATE(query->in_info_list, query->in_info_list, List *);
- foreach(rt, query->rtable)
+ MUTATE(query->limitOffset, query->limitOffset, Node *);
+ MUTATE(query->limitCount, query->limitCount, Node *);
+ query->rtable = range_table_mutator(query->rtable,
+ mutator, context, flags);
+ return query;
+}
+
+/*
+ * range_table_mutator is just the part of query_tree_mutator that processes
+ * a query's rangetable. This is split out since it can be useful on
+ * its own.
+ */
+List *
+range_table_mutator(List *rtable,
+ Node *(*mutator) (),
+ void *context,
+ int flags)
+{
+ List *newrt = NIL;
+ ListCell *rt;
+
+ foreach(rt, rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
RangeTblEntry *newrte;
+ FLATCOPY(newrte, rte, RangeTblEntry);
switch (rte->rtekind)
{
case RTE_RELATION:
case RTE_SPECIAL:
- /* nothing to do, don't bother to make a copy */
+ /* we don't bother to copy eref, aliases, etc; OK? */
break;
case RTE_SUBQUERY:
- if (! (flags & QTW_IGNORE_RT_SUBQUERIES))
+ if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
{
- FLATCOPY(newrte, rte, RangeTblEntry);
CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
MUTATE(newrte->subquery, newrte->subquery, Query *);
- rte = newrte;
+ }
+ else
+ {
+ /* else, copy RT subqueries as-is */
+ newrte->subquery = copyObject(rte->subquery);
}
break;
case RTE_JOIN:
- if (! (flags & QTW_IGNORE_JOINALIASES))
- {
- FLATCOPY(newrte, rte, RangeTblEntry);
+ if (!(flags & QTW_IGNORE_JOINALIASES))
MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
- rte = newrte;
+ else
+ {
+ /* else, copy join aliases as-is */
+ newrte->joinaliasvars = copyObject(rte->joinaliasvars);
}
break;
case RTE_FUNCTION:
- FLATCOPY(newrte, rte, RangeTblEntry);
MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
- rte = newrte;
+ break;
+ case RTE_VALUES:
+ MUTATE(newrte->values_lists, rte->values_lists, List *);
break;
}
- newrt = lappend(newrt, rte);
+ newrt = lappend(newrt, newrte);
}
- query->rtable = newrt;
- return query;
+ return newrt;
}
/*