* clauses.c
* routines to manipulate qualification clauses
*
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.178 2004/08/17 18:47:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.229 2007/01/10 18:06:04 tgl 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 "optimizer/clauses.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"
typedef struct
{
List *active_fns;
+ Node *case_val;
bool estimate;
} eval_const_expressions_context;
} 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 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);
+ eval_const_expressions_context *context);
static List *simplify_or_arguments(List *args,
- bool *haveNull, bool *forceTrue);
+ eval_const_expressions_context *context,
+ bool *haveNull, bool *forceTrue);
static List *simplify_and_arguments(List *args,
- bool *haveNull, bool *forceFalse);
+ 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,
- eval_const_expressions_context *context);
+ 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,
- eval_const_expressions_context *context);
+ HeapTuple func_tuple,
+ eval_const_expressions_context *context);
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
int *usecounts);
static Node *substitute_actual_parameters_mutator(Node *node,
- 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);
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 */
if (IsA(node, Aggref))
{
Assert(((Aggref *) node)->agglevelsup == 0);
- return true; /* abort the tree traversal and return
- * true */
+ return true; /* abort the tree traversal and return true */
}
Assert(!IsA(node, SubLink));
return expression_tree_walker(node, contain_agg_clause_walker, context);
}
/*
- * contain_distinct_agg_clause
- * Recursively search for DISTINCT Aggref nodes within a clause.
+ * count_agg_clauses
+ * Recursively count the Aggref nodes in an expression tree.
*
- * Returns true if any DISTINCT aggregate found.
+ * 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.
*/
-bool
-contain_distinct_agg_clause(Node *clause)
+void
+count_agg_clauses(Node *clause, AggClauseCounts *counts)
{
- return contain_distinct_agg_clause_walker(clause, NULL);
+ /* no setup needed */
+ count_agg_clauses_walker(clause, counts);
}
static bool
-contain_distinct_agg_clause_walker(Node *node, void *context)
+count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
{
if (node == NULL)
return false;
if (IsA(node, Aggref))
{
- Assert(((Aggref *) node)->agglevelsup == 0);
- if (((Aggref *) node)->aggdistinct)
- return true; /* abort the tree traversal and return
- * true */
- }
- Assert(!IsA(node, SubLink));
- return expression_tree_walker(node, contain_distinct_agg_clause_walker, context);
-}
+ 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));
+ }
-/*
- * count_agg_clause
- * Recursively count the Aggref nodes in an expression tree.
- *
- * Note: this also checks for nested aggregates, which are an error.
- *
- * 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)
-{
- int result = 0;
+ /* 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);
+ }
- count_agg_clause_walker(clause, &result);
- return result;
-}
+ /*
+ * 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;
-static bool
-count_agg_clause_walker(Node *node, int *count)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- {
- Assert(((Aggref *) node)->agglevelsup == 0);
- (*count)++;
+ /*
+ * 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))
+ if (contain_agg_clause((Node *) aggref->args))
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
- errmsg("aggregate function calls may not be nested")));
+ errmsg("aggregate function calls may not be nested")));
/*
* Having checked that, we need not recurse into the argument.
return false;
}
Assert(!IsA(node, SubLink));
- return expression_tree_walker(node, count_agg_clause_walker,
- (void *) count);
+ return expression_tree_walker(node, count_agg_clauses_walker,
+ (void *) counts);
}
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);
}
return true;
/* else fall through to check args */
}
- if (IsA(node, SubLink))
+ if (IsA(node, RowCompareExpr))
{
- SubLink *sublink = (SubLink *) node;
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
ListCell *opid;
- foreach(opid, sublink->operOids)
+ foreach(opid, rcexpr->opnos)
{
if (op_volatile(lfirst_oid(opid)) != PROVOLATILE_IMMUTABLE)
return true;
return true;
/* else fall through to check args */
}
- if (IsA(node, SubLink))
+ if (IsA(node, RowCompareExpr))
{
- SubLink *sublink = (SubLink *) node;
+ /* 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(lfirst_oid(opid)) == PROVOLATILE_VOLATILE)
return true;
* 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.
+ * inputs is NULL. If we return false, then the proof succeeded.
*/
bool
contain_nonstrict_functions(Node *clause)
}
if (IsA(node, ArrayRef))
{
- /* array assignment is nonstrict */
+ /* array assignment is nonstrict, but subscripting is strict */
if (((ArrayRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
}
if (IsA(node, ScalarArrayOpExpr))
{
- /* inherently non-strict, consider null scalar and empty array */
- return true;
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (!is_strict_saop(expr, false))
+ return true;
+ /* else fall through to check args */
}
if (IsA(node, BoolExpr))
{
return true;
if (IsA(node, CaseWhen))
return true;
- /* NB: ArrayExpr might someday be nonstrict */
+ 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))
}
+/*
+ * 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))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup == 0)
+ result = bms_make_singleton(var->varno);
+ }
+ else if (IsA(node, List))
+ {
+ ListCell *l;
+
+ foreach(l, (List *) node)
+ {
+ result = bms_join(result,
+ find_nonnullable_rels_walker(lfirst(l),
+ top_level));
+ }
+ }
+ 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;
+}
+
+
/*****************************************************************************
* Check for "pseudo-constant" clauses
*****************************************************************************/
/*
* 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))
/*
* is_pseudo_constant_clause_relids
* Same as above, except caller already has available the var membership
- * of the clause; this lets us avoid the contain_var_clause() scan.
+ * of the expression; this lets us avoid the contain_var_clause() scan.
*/
bool
is_pseudo_constant_clause_relids(Node *clause, Relids relids)
return false;
}
-/*
- * 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.
- */
-List *
-pull_constant_clauses(List *quals, List **constantQual)
-{
- List *constqual = NIL,
- *restqual = NIL;
- ListCell *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;
-}
-
/*****************************************************************************
* Tests on clauses of queries
/*
* 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.
{
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 (targetIsInSortList(tle, InvalidOid, 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))
+ if (targetIsInSortList(tle, InvalidOid, query->sortClause))
return true; /* sorted, non-distinct junk */
/* unsorted junk is okay, keep looking */
}
}
/*
- * 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;
lsecond(clause->args) = temp;
}
+/*
+ * CommuteRowCompareExpr: commute a RowCompareExpr clause
+ *
+ * XXX the clause is destructively modified!
+ */
+void
+CommuteRowCompareExpr(RowCompareExpr *clause)
+{
+ List *newops;
+ List *temp;
+ ListCell *l;
+
+ /* 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
*
return false;
if (IsA(node, FuncExpr))
((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
- if (IsA(node, RelabelType))
+ else if (IsA(node, RelabelType))
((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
- if (IsA(node, RowExpr))
+ else if (IsA(node, ConvertRowtypeExpr))
+ ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE;
+ else if (IsA(node, RowExpr))
((RowExpr *) node)->row_format = COERCE_DONTCARE;
- if (IsA(node, CoerceToDomain))
+ else if (IsA(node, CoerceToDomain))
((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
return expression_tree_walker(node, set_coercionform_dontcare_walker,
context);
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;
}
*
* 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_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
* eval_const_expressions(): we will perform constant reductions that are
* not necessarily 100% safe, but are reasonable for estimation purposes.
*
- * Currently the only such transform is to substitute values for Params,
- * when a bound Param value has been made available by the caller of planner().
- * In future we might consider other things, such as reducing now() to current
- * time. (XXX seems like there could be a lot of scope for ideas here...
- * but we might need more volatility classifications ...)
+ * 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);
}
{
Param *param = (Param *) node;
- /* OK to try to substitute value? */
- if (context->estimate && param->paramkind != PARAM_EXEC &&
- PlannerBoundParamList != NULL)
+ /* 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)
{
- ParamListInfo paramInfo;
+ ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
- /* Search to see if we've been given a value for this Param */
- paramInfo = lookupParam(PlannerBoundParamList,
- param->paramkind,
- param->paramname,
- param->paramid,
- true);
- if (paramInfo)
+ if (OidIsValid(prm->ptype))
{
- /*
- * Found it, so return a Const representing the param value.
- * Note that we don't copy pass-by-ref datatypes, so the
- * Const will only be valid as long as the bound parameter
- * list exists. This is okay for intended uses of
- * estimate_expression_value().
- */
- int16 typLen;
- bool typByVal;
-
- Assert(paramInfo->ptype == param->paramtype);
- get_typlenbyval(param->paramtype, &typLen, &typByVal);
- return (Node *) makeConst(param->paramtype,
- (int) typLen,
- paramInfo->value,
- paramInfo->isnull,
- typByVal);
+ /* 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) */
/*
* 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,
+ 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, context);
/*
* The expression cannot be simplified any further, so build and
- * return a replacement FuncExpr node using the
- * possibly-simplified arguments.
+ * return a replacement FuncExpr node using the possibly-simplified
+ * arguments.
*/
newexpr = makeNode(FuncExpr);
newexpr->funcid = expr->funcid;
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,
+ 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, 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 *newexpr;
/*
- * 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.
+ * 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.
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
- eval_const_expressions_mutator,
+ 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.
+ * different results for NULL input than the underlying operator does.
*/
foreach(arg, args)
{
/* (NOT okay to try to inline it, though!) */
/*
- * 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((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, context);
if (IsA(node, BoolExpr))
{
BoolExpr *expr = (BoolExpr *) node;
- List *args;
-
- /*
- * 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 *) context);
switch (expr->boolop)
{
bool haveNull = false;
bool forceTrue = false;
- newargs = simplify_or_arguments(args,
+ newargs = simplify_or_arguments(expr->args, context,
&haveNull, &forceTrue);
if (forceTrue)
return makeBoolConst(true, false);
bool haveNull = false;
bool forceFalse = false;
- newargs = simplify_and_arguments(args,
+ newargs = simplify_and_arguments(expr->args, context,
&haveNull, &forceFalse);
if (forceFalse)
return makeBoolConst(false, false);
return (Node *) make_andclause(newargs);
}
case NOT_EXPR:
- Assert(list_length(args) == 1);
- if (IsA(linitial(args), Const))
- {
- Const *const_input = (Const *) linitial(args);
-
- /* 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((Node *) linitial(args)))
{
- /* Cancel NOT/NOT */
- return (Node *) get_notclausearg((Expr *) linitial(args));
+ 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((Expr *) linitial(args));
default:
elog(ERROR, "unrecognized boolop: %d",
(int) expr->boolop);
/*
* Return a SubPlan unchanged --- too late to do anything with it.
*
- * XXX should we ereport() 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;
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 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 and
- * one or more constant comparison expressions, we could run the
- * implied comparisons and potentially reduce those arms to constants.
- * This is not yet implemented, however. At present, the
- * CaseTestExpr placeholder will always act as a non-constant node
- * and prevent the comparison boolean expressions from being reduced
- * to Const nodes.
+ * 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;
+ Node *save_case_val;
Node *newarg;
List *newargs;
- Node *defresult;
- Const *const_input;
+ 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 *) context);
+ 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,
- context);
+ /* 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->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;
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;
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 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.
+ * 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,
+ 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, 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))
+ if (arg && IsA(arg, Const))
{
- RowExpr *rowexpr = (RowExpr *) arg;
+ Const *carg = (Const *) arg;
+ bool result;
- if (fselect->fieldnum > 0 &&
- fselect->fieldnum <= list_length(rowexpr->args))
+ switch (btest->booltesttype)
{
- 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;
+ 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);
}
- newfselect = makeNode(FieldSelect);
- newfselect->arg = (Expr *) arg;
- newfselect->fieldnum = fselect->fieldnum;
- newfselect->resulttype = fselect->resulttype;
- newfselect->resulttypmod = fselect->resulttypmod;
- return (Node *) newfselect;
+
+ 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.
+ * 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: scan the arguments of an OR clause
+ * 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.
*
- * OR arguments are handled as follows:
+ * 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.
- *
- * This is split out as a subroutine so that we can recurse to fold sub-ORs
- * into the upper OR clause, thereby preserving AND/OR flatness.
+ * 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, bool *haveNull, bool *forceTrue)
+simplify_or_arguments(List *args,
+ eval_const_expressions_context *context,
+ bool *haveNull, bool *forceTrue)
{
List *newargs = NIL;
- ListCell *larg;
+ List *unprocessed_args;
- foreach(larg, 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 *) lfirst(larg);
+ 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;
+ 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
return NIL;
}
/* otherwise, we can drop the constant-false input */
+ continue;
}
- else if (or_clause(arg))
- {
- newargs = list_concat(newargs,
- simplify_or_arguments(((BoolExpr *) arg)->args,
- haveNull, forceTrue));
- }
- else
- {
- newargs = lappend(newargs, arg);
- }
+
+ /* else emit the simplified arg into the result list */
+ newargs = lappend(newargs, arg);
}
return newargs;
}
/*
- * Subroutine for eval_const_expressions: scan the arguments of an AND clause
+ * Subroutine for eval_const_expressions: process arguments of an AND clause
*
- * AND arguments are handled as follows:
+ * 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.
- *
- * This is split out as a subroutine so that we can recurse to fold sub-ANDs
- * into the upper AND clause, thereby preserving AND/OR flatness.
+ * 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, bool *haveNull, bool *forceFalse)
+simplify_and_arguments(List *args,
+ eval_const_expressions_context *context,
+ bool *haveNull, bool *forceFalse)
{
List *newargs = NIL;
- ListCell *larg;
+ List *unprocessed_args;
- foreach(larg, args)
+ /* See comments in simplify_or_arguments */
+ unprocessed_args = list_copy(args);
+ while (unprocessed_args)
{
- Node *arg = (Node *) lfirst(larg);
+ Node *arg = (Node *) linitial(unprocessed_args);
+
+ unprocessed_args = list_delete_first(unprocessed_args);
+
+ /* flatten nested ANDs as per above comment */
+ if (and_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 AND, simplify it */
+ arg = eval_const_expressions_mutator(arg, context);
+
+ /*
+ * 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.
+ */
+ if (and_clause(arg))
+ {
+ List *subargs = list_copy(((BoolExpr *) arg)->args);
+ unprocessed_args = list_concat(subargs, unprocessed_args);
+ continue;
+ }
+
+ /*
+ * OK, we have a const-simplified non-AND argument. Process it per
+ * comments above.
+ */
if (IsA(arg, Const))
{
- Const *const_input = (Const *) arg;
+ 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
return NIL;
}
/* otherwise, we can drop the constant-true input */
+ continue;
}
- else if (and_clause(arg))
- {
- newargs = list_concat(newargs,
- simplify_and_arguments(((BoolExpr *) arg)->args,
- haveNull, forceFalse));
- }
- else
- {
- newargs = lappend(newargs, arg);
- }
+
+ /* else emit the simplified arg into the result list */
+ newargs = lappend(newargs, arg);
}
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;
+}
+
/*
* Subroutine for eval_const_expressions: try to simplify a function call
* (which might originally have been an operator; we don't care)
Expr *newexpr;
/*
- * We have two strategies for simplification: either execute the
- * function to deliver a constant result, or expand in-line the body
- * of the function definition (which only works for simple
- * SQL-language functions, but that is a common case). In either case
- * we need access to the function's pg_proc tuple, so fetch it just
- * once to use in both attempts.
+ * We have two strategies for simplification: either execute the function
+ * to deliver a constant result, or expand in-line the body of the
+ * function definition (which only works for simple SQL-language
+ * functions, but that is a common case). In either case we need access
+ * to the function's pg_proc tuple, so fetch it just once to use in both
+ * attempts.
*/
func_tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
if (!HeapTupleIsValid(func_tuple))
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,
*
* 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;
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;
/*
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
bool polymorphic = false;
- Oid argtypes[FUNC_MAX_ARGS];
+ Oid *argtypes;
char *src;
Datum tmp;
bool isNull;
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 ||
if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
return NULL;
- /* Check for polymorphic arguments, and substitute actual arg types */
- memcpy(argtypes, funcform->proargtypes, FUNC_MAX_ARGS * 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;
-
/*
- * Setup error traceback support for ereport(). This is so that we
- * can finger the function that bad information came from.
+ * 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;
error_context_stack = &sqlerrcontext;
/*
- * Make a temporary memory context, so that we don't leak all the
- * stuff that parsing might create.
+ * 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,
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 (list_length(raw_parsetree_list) != 1)
goto fail;
- querytree_list = parse_analyze(linitial(raw_parsetree_list),
+ querytree_list = parse_analyze(linitial(raw_parsetree_list), src,
argtypes, funcform->pronargs);
if (list_length(querytree_list) != 1)
*/
if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT ||
- querytree->resultRelation != 0 ||
querytree->into ||
querytree->hasAggs ||
querytree->hasSubLinks ||
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 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(result_type, get_typtype(result_type),
- querytree_list);
+ (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;
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 * sizeof(int));
newexpr = substitute_actual_parameters(newexpr, funcform->pronargs,
QualCost eval_cost;
/*
- * We define "expensive" as "contains any subplan or more than
- * 10 operators". Note that the subplan search has to be done
+ * 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.
*/
}
/*
- * Whew --- we can make the substitution. Copy the modified
- * expression out of the temporary memory context, and clean up.
+ * Whew --- we can make the substitution. Copy the modified expression
+ * out of the temporary memory context, and clean up.
*/
MemoryContextSwitchTo(oldcxt);
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.
*/
context->active_fns = lcons_oid(funcid, context->active_fns);
newexpr = eval_const_expressions_mutator(newexpr, context);
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, "unexpected paramkind: %d", param->paramkind);
+ if (param->paramkind != PARAM_EXTERN)
+ elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind);
if (param->paramid <= 0 || param->paramid > context->nargs)
elog(ERROR, "invalid paramid: %d", param->paramid);
static void
sql_inline_error_callback(void *arg)
{
- HeapTuple func_tuple = (HeapTuple) arg;
+ HeapTuple func_tuple = (HeapTuple) arg;
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int syntaxerrposition;
*
* 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?
+ * 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
+ * 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
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
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;
{
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);
}
{
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,
return walker(((FieldSelect *) node)->arg, context);
case T_FieldStore:
{
- FieldStore *fstore = (FieldStore *) node;
+ FieldStore *fstore = (FieldStore *) node;
if (walker(fstore->arg, 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;
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, "unrecognized node type: %d",
(int) nodeTag(node));
void *context,
int flags)
{
- ListCell *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->limitCount, context))
return true;
- if (walker(query->in_info_list, context))
+ if (range_table_walker(query->rtable, walker, context, flags))
return true;
- foreach(rt, query->rtable)
+ 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);
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) \
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;
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;
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_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;
break;
case T_RowExpr:
{
- RowExpr *rowexpr = (RowExpr *) node;
- RowExpr *newnode;
+ 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;
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;
ListCell *temp;
FLATCOPY(newnode, ininfo, InClauseInfo);
MUTATE(newnode->sub_targetlist, ininfo->sub_targetlist, List *);
+ /* Assume we need not make a copy of in_operators list */
+ 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;
void *context,
int flags)
{
- List *newrt;
- ListCell *rt;
-
Assert(query != NULL && IsA(query, Query));
if (!(flags & QTW_DONT_COPY_QUERY))
}
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->limitOffset, query->limitOffset, Node *);
MUTATE(query->limitCount, query->limitCount, Node *);
- MUTATE(query->in_info_list, query->in_info_list, List *);
- newrt = NIL;
- foreach(rt, query->rtable)
+ 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;
CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
MUTATE(newrte->subquery, newrte->subquery, Query *);
}
+ else
+ {
+ /* else, copy RT subqueries as-is */
+ newrte->subquery = copyObject(rte->subquery);
+ }
break;
case RTE_JOIN:
if (!(flags & QTW_IGNORE_JOINALIASES))
- {
MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
+ else
+ {
+ /* else, copy join aliases as-is */
+ newrte->joinaliasvars = copyObject(rte->joinaliasvars);
}
break;
case RTE_FUNCTION:
MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
break;
+ case RTE_VALUES:
+ MUTATE(newrte->values_lists, rte->values_lists, List *);
+ break;
}
newrt = lappend(newrt, newrte);
}
- query->rtable = newrt;
- return query;
+ return newrt;
}
/*