* clauses.c
* routines to manipulate qualification clauses
*
- * Portions Copyright (c) 1996-2006, 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.218 2006/08/12 02:52:05 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
Form_pg_aggregate aggform;
Oid aggtranstype;
int i;
- ListCell *l;
+ ListCell *l;
Assert(aggref->agglevelsup == 0);
counts->numAggs++;
return false;
if (IsA(node, MinMaxExpr))
return false;
+ if (IsA(node, XmlExpr))
+ return false;
if (IsA(node, NullIfExpr))
return false;
return true;
if (IsA(node, MinMaxExpr))
return true;
+ if (IsA(node, XmlExpr))
+ return true;
if (IsA(node, NullIfExpr))
return true;
if (IsA(node, NullTest))
*
* 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
+ * 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:
static bool
is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK)
{
- Node *rightop;
+ Node *rightop;
/* The contained operator must be strict. */
if (!op_strict(expr->opno))
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->resjunk)
return true; /* junk TLE in DISTINCT means DISTINCT ON */
/* This TLE is not in DISTINCT list */
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 */
}
}
clause->opnos = newops;
+
/*
- * Note: we don't bother to update the opclasses list, but just set
- * it to empty. This is OK since this routine is currently only used
- * for index quals, and the index machinery won't use the opclass
- * information. The original opclass list is NOT valid if we have
- * commuted any cross-type comparisons, so don't leave it in place.
+ * Note: we need not change the opfamilies list; we assume any btree
+ * opfamily containing an operator will also contain its commutator.
*/
- clause->opclasses = NIL; /* XXX */
temp = clause->largs;
clause->largs = clause->rargs;
*
* 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().
+ * 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.
*--------------------
*/
{
Param *param = (Param *) node;
- /* OK to try to substitute value? */
- if (context->estimate && param->paramkind == PARAM_EXTERN &&
- 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)
{
- /* Look to see if we've been given a value for this Param */
- if (param->paramid > 0 &&
- param->paramid <= PlannerBoundParamList->numParams)
- {
- ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
+ ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
- if (OidIsValid(prm->ptype))
+ if (OidIsValid(prm->ptype))
+ {
+ /* OK to substitute parameter value? */
+ if (context->estimate || (prm->pflags & PARAM_FLAG_CONST))
{
/*
- * 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().
+ * 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,
- prm->value,
+ pval,
prm->isnull,
typByVal);
}
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;
context);
if (arg && IsA(arg, Const))
{
- Const *carg = (Const *) arg;
- bool result;
+ Const *carg = (Const *) arg;
+ bool result;
switch (btest->booltesttype)
{
default:
elog(ERROR, "unrecognized booltesttype: %d",
(int) btest->booltesttype);
- result = false; /* keep compiler quiet */
+ result = false; /* keep compiler quiet */
break;
}
*/
if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT ||
- querytree->resultRelation != 0 ||
querytree->into ||
querytree->hasAggs ||
querytree->hasSubLinks ||
* 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 "testexpr" subtree (which is an expression belonging to the outer
break;
case T_Aggref:
{
- Aggref *expr = (Aggref *) node;
+ Aggref *expr = (Aggref *) node;
+ /* recurse directly on List */
if (expression_tree_walker((Node *) expr->args,
walker, context))
return true;
{
SubLink *sublink = (SubLink *) node;
- if (expression_tree_walker(sublink->testexpr,
- walker, context))
+ if (walker(sublink->testexpr, context))
return true;
/*
SubPlan *subplan = (SubPlan *) node;
/* recurse into the testexpr, but not into the Plan */
- if (expression_tree_walker(subplan->testexpr,
- walker, context))
+ if (walker(subplan->testexpr, context))
return true;
/* also examine args list */
if (expression_tree_walker((Node *) subplan->args,
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;
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;
}
break;
case T_RowCompareExpr:
{
- RowCompareExpr *rcexpr = (RowCompareExpr *) node;
- RowCompareExpr *newnode;
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ RowCompareExpr *newnode;
FLATCOPY(newnode, rcexpr, RowCompareExpr);
MUTATE(newnode->largs, rcexpr->largs, 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;
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;