/*
* Transform raw parsetree to executable expression.
*/
- expr = transformExpr(pstate, raw_default);
+ expr = transformExpr(pstate, raw_default, EXPR_KIND_COLUMN_DEFAULT);
/*
- * Make sure default expr does not refer to any vars.
+ * Make sure default expr does not refer to any vars (we need this check
+ * since the pstate includes the target table).
*/
if (contain_var_clause(expr))
ereport(ERROR,
errmsg("cannot use column references in default expression")));
/*
+ * transformExpr() should have already rejected subqueries, aggregates,
+ * and window functions, based on the EXPR_KIND_ for a default expression.
+ *
* It can't return a set either.
*/
if (expression_returns_set(expr))
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("default expression must not return a set")));
- /*
- * No subplans or aggregates, either...
- */
- if (pstate->p_hasSubLinks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in default expression")));
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in default expression")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in default expression")));
-
/*
* Coerce the expression to the correct type and typmod, if given. This
* should match the parser's processing of non-defaulted expressions ---
/*
* Transform raw parsetree to executable expression.
*/
- expr = transformExpr(pstate, raw_constraint);
+ expr = transformExpr(pstate, raw_constraint, EXPR_KIND_CHECK_CONSTRAINT);
/*
* Make sure it yields a boolean result.
assign_expr_collations(pstate, expr);
/*
- * Make sure no outside relations are referred to.
+ * Make sure no outside relations are referred to (this is probably dead
+ * code now that add_missing_from is history).
*/
if (list_length(pstate->p_rtable) != 1)
ereport(ERROR,
errmsg("only table \"%s\" can be referenced in check constraint",
relname)));
- /*
- * No subplans or aggregates, either...
- */
- if (pstate->p_hasSubLinks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in check constraint")));
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in check constraint")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in check constraint")));
-
return expr;
}
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("only input parameters can have default values")));
- def = transformExpr(pstate, fp->defexpr);
+ def = transformExpr(pstate, fp->defexpr,
+ EXPR_KIND_FUNCTION_DEFAULT);
def = coerce_to_specific_type(pstate, def, toid, "DEFAULT");
assign_expr_collations(pstate, def);
/*
- * Make sure no variables are referred to.
+ * Make sure no variables are referred to (this is probably dead
+ * code now that add_missing_from is history).
*/
if (list_length(pstate->p_rtable) != 0 ||
contain_var_clause(def))
errmsg("cannot use table references in parameter default value")));
/*
+ * transformExpr() should have already rejected subqueries,
+ * aggregates, and window functions, based on the EXPR_KIND_ for a
+ * default expression.
+ *
* It can't return a set either --- but coerce_to_specific_type
* already checked that for us.
*
- * No subplans or aggregates, either...
- *
* Note: the point of these restrictions is to ensure that an
* expression that, on its face, hasn't got subplans, aggregates,
* etc cannot suddenly have them after function default arguments
* are inserted.
*/
- if (pstate->p_hasSubLinks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in parameter default value")));
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in parameter default value")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in parameter default value")));
*parameterDefaults = lappend(*parameterDefaults, def);
have_defaults = true;
CheckPredicate(Expr *predicate)
{
/*
- * We don't currently support generation of an actual query plan for a
- * predicate, only simple scalar expressions; hence these restrictions.
+ * transformExpr() should have already rejected subqueries, aggregates,
+ * and window functions, based on the EXPR_KIND_ for a predicate.
*/
- if (contain_subplans((Node *) predicate))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in index predicate")));
- if (contain_agg_clause((Node *) predicate))
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate in index predicate")));
/*
* A predicate using mutable functions is probably wrong, for the same
expr);
/*
- * We don't currently support generation of an actual query
- * plan for an index expression, only simple scalar
- * expressions; hence these restrictions.
+ * transformExpr() should have already rejected subqueries,
+ * aggregates, and window functions, based on the EXPR_KIND_
+ * for an index expression.
*/
- if (contain_subplans(expr))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in index expression")));
- if (contain_agg_clause(expr))
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in index expression")));
/*
* A expression using mutable functions is probably wrong,
Oid expected_type_id = param_types[i];
Oid given_type_id;
- expr = transformExpr(pstate, expr);
-
- /* Cannot contain subselects or aggregates */
- if (pstate->p_hasSubLinks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in EXECUTE parameter")));
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in EXECUTE parameter")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in EXECUTE parameter")));
+ expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);
given_type_id = exprType(expr);
true);
addRTEtoQuery(pstate, rte, false, true, true);
- transform = transformExpr(pstate, transform);
+ transform = transformExpr(pstate, transform,
+ EXPR_KIND_ALTER_COL_TRANSFORM);
/* It can't return a set */
if (expression_returns_set(transform))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("transform expression must not return a set")));
-
- /* No subplans or aggregates, either... */
- if (pstate->p_hasSubLinks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in transform expression")));
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in transform expression")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in transform expression")));
}
else
{
/* Transform expression. Copy to be sure we don't modify original */
whenClause = transformWhereClause(pstate,
copyObject(stmt->whenClause),
+ EXPR_KIND_TRIGGER_WHEN,
"WHEN");
/* we have to fix its collations too */
assign_expr_collations(pstate, whenClause);
- /*
- * No subplans or aggregates, please
- */
- if (pstate->p_hasSubLinks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in trigger WHEN condition")));
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in trigger WHEN condition")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in trigger WHEN condition")));
-
/*
* Check for disallowed references to OLD/NEW.
*
pstate->p_value_substitute = (Node *) domVal;
- expr = transformExpr(pstate, constr->raw_expr);
+ expr = transformExpr(pstate, constr->raw_expr, EXPR_KIND_DOMAIN_CHECK);
/*
* Make sure it yields a boolean result.
assign_expr_collations(pstate, expr);
/*
- * Make sure no outside relations are referred to.
+ * Domains don't allow variables (this is probably dead code now that
+ * add_missing_from is history, but let's be sure).
*/
- if (list_length(pstate->p_rtable) != 0)
+ if (list_length(pstate->p_rtable) != 0 ||
+ contain_var_clause(expr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("cannot use table references in domain check constraint")));
- /*
- * Domains don't allow var clauses (this should be redundant with the
- * above check, but make it anyway)
- */
- if (contain_var_clause(expr))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
- errmsg("cannot use table references in domain check constraint")));
-
- /*
- * No subplans or aggregates, either...
- */
- if (pstate->p_hasSubLinks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use subquery in check constraint")));
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in check constraint")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in check constraint")));
-
/*
* Convert to string form for storage.
*/
bool
contain_window_function(Node *clause)
{
- return checkExprHasWindowFuncs(clause);
+ return contain_windowfuncs(clause);
}
/*
int sublevels_up;
} locate_var_of_level_context;
-typedef struct
-{
- int min_varlevel;
- int sublevels_up;
-} find_minimum_var_level_context;
-
typedef struct
{
List *varlist;
static bool contain_vars_of_level_walker(Node *node, int *sublevels_up);
static bool locate_var_of_level_walker(Node *node,
locate_var_of_level_context *context);
-static bool find_minimum_var_level_walker(Node *node,
- find_minimum_var_level_context *context);
static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context);
static Node *flatten_join_alias_vars_mutator(Node *node,
}
-/*
- * find_minimum_var_level
- * Recursively scan a clause to find the lowest variable level it
- * contains --- for example, zero is returned if there are any local
- * variables, one if there are no local variables but there are
- * one-level-up outer references, etc. Subqueries are scanned to see
- * if they possess relevant outer references. (But any local variables
- * within subqueries are not relevant.)
- *
- * -1 is returned if the clause has no variables at all.
- *
- * Will recurse into sublinks. Also, may be invoked directly on a Query.
- */
-int
-find_minimum_var_level(Node *node)
-{
- find_minimum_var_level_context context;
-
- context.min_varlevel = -1; /* signifies nothing found yet */
- context.sublevels_up = 0;
-
- (void) query_or_expression_tree_walker(node,
- find_minimum_var_level_walker,
- (void *) &context,
- 0);
-
- return context.min_varlevel;
-}
-
-static bool
-find_minimum_var_level_walker(Node *node,
- find_minimum_var_level_context *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- int varlevelsup = ((Var *) node)->varlevelsup;
-
- /* convert levelsup to frame of reference of original query */
- varlevelsup -= context->sublevels_up;
- /* ignore local vars of subqueries */
- if (varlevelsup >= 0)
- {
- if (context->min_varlevel < 0 ||
- context->min_varlevel > varlevelsup)
- {
- context->min_varlevel = varlevelsup;
-
- /*
- * As soon as we find a local variable, we can abort the tree
- * traversal, since min_varlevel is then certainly 0.
- */
- if (varlevelsup == 0)
- return true;
- }
- }
- }
- if (IsA(node, CurrentOfExpr))
- {
- int varlevelsup = 0;
-
- /* convert levelsup to frame of reference of original query */
- varlevelsup -= context->sublevels_up;
- /* ignore local vars of subqueries */
- if (varlevelsup >= 0)
- {
- if (context->min_varlevel < 0 ||
- context->min_varlevel > varlevelsup)
- {
- context->min_varlevel = varlevelsup;
-
- /*
- * As soon as we find a local variable, we can abort the tree
- * traversal, since min_varlevel is then certainly 0.
- */
- if (varlevelsup == 0)
- return true;
- }
- }
- }
-
- /*
- * An Aggref must be treated like a Var of its level. Normally we'd get
- * the same result from looking at the Vars in the aggregate's argument,
- * but this fails in the case of a Var-less aggregate call (COUNT(*)).
- */
- if (IsA(node, Aggref))
- {
- int agglevelsup = ((Aggref *) node)->agglevelsup;
-
- /* convert levelsup to frame of reference of original query */
- agglevelsup -= context->sublevels_up;
- /* ignore local aggs of subqueries */
- if (agglevelsup >= 0)
- {
- if (context->min_varlevel < 0 ||
- context->min_varlevel > agglevelsup)
- {
- context->min_varlevel = agglevelsup;
-
- /*
- * As soon as we find a local aggregate, we can abort the tree
- * traversal, since min_varlevel is then certainly 0.
- */
- if (agglevelsup == 0)
- return true;
- }
- }
- }
- /* Likewise, make sure PlaceHolderVar is treated correctly */
- if (IsA(node, PlaceHolderVar))
- {
- int phlevelsup = ((PlaceHolderVar *) node)->phlevelsup;
-
- /* convert levelsup to frame of reference of original query */
- phlevelsup -= context->sublevels_up;
- /* ignore local vars of subqueries */
- if (phlevelsup >= 0)
- {
- if (context->min_varlevel < 0 ||
- context->min_varlevel > phlevelsup)
- {
- context->min_varlevel = phlevelsup;
-
- /*
- * As soon as we find a local variable, we can abort the tree
- * traversal, since min_varlevel is then certainly 0.
- */
- if (phlevelsup == 0)
- return true;
- }
- }
- }
- if (IsA(node, Query))
- {
- /* Recurse into subselects */
- bool result;
-
- context->sublevels_up++;
- result = query_tree_walker((Query *) node,
- find_minimum_var_level_walker,
- (void *) context,
- 0);
- context->sublevels_up--;
- return result;
- }
- return expression_tree_walker(node,
- find_minimum_var_level_walker,
- (void *) context);
-}
-
-
/*
* pull_var_clause
* Recursively pulls all Var nodes from an expression clause.
*/
transformFromClause(pstate, stmt->usingClause);
- qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
+ qual = transformWhereClause(pstate, stmt->whereClause,
+ EXPR_KIND_WHERE, "WHERE");
qry->returningList = transformReturningList(pstate, stmt->returningList);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
- if (pstate->p_hasWindowFuncs)
- parseCheckWindowFuncs(pstate, qry);
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry);
List *sublist = (List *) lfirst(lc);
/* Do basic expression transformation (same as a ROW() expr) */
- sublist = transformExpressionList(pstate, sublist);
+ sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES);
/*
* All the sublists must be the same length, *after*
}
else
{
- /*----------
- * Process INSERT ... VALUES with a single VALUES sublist.
- * We treat this separately for efficiency and for historical
- * compatibility --- specifically, allowing table references,
- * such as
- * INSERT INTO foo VALUES(bar.*)
- *
- * The sublist is just computed directly as the Query's targetlist,
- * with no VALUES RTE. So it works just like SELECT without FROM.
- *----------
+ /*
+ * Process INSERT ... VALUES with a single VALUES sublist. We treat
+ * this case separately for efficiency. The sublist is just computed
+ * directly as the Query's targetlist, with no VALUES RTE. So it
+ * works just like a SELECT without any FROM.
*/
List *valuesLists = selectStmt->valuesLists;
/* Do basic expression transformation (same as a ROW() expr) */
exprList = transformExpressionList(pstate,
- (List *) linitial(valuesLists));
+ (List *) linitial(valuesLists),
+ EXPR_KIND_VALUES);
/* Prepare row for assignment to target table */
exprList = transformInsertRow(pstate, exprList,
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
- /* aggregates not allowed (but subselects are okay) */
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in VALUES"),
- parser_errposition(pstate,
- locate_agg_of_level((Node *) qry, 0))));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in VALUES"),
- parser_errposition(pstate,
- locate_windowfunc((Node *) qry))));
assign_query_collations(pstate, qry);
Assert(IsA(col, ResTarget));
expr = transformAssignedExpr(pstate, expr,
+ EXPR_KIND_INSERT_TARGET,
col->name,
lfirst_int(attnos),
col->indirection,
transformFromClause(pstate, stmt->fromClause);
/* transform targetlist */
- qry->targetList = transformTargetList(pstate, stmt->targetList);
+ qry->targetList = transformTargetList(pstate, stmt->targetList,
+ EXPR_KIND_SELECT_TARGET);
/* mark column origins */
markTargetListOrigins(pstate, qry->targetList);
/* transform WHERE */
- qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
+ qual = transformWhereClause(pstate, stmt->whereClause,
+ EXPR_KIND_WHERE, "WHERE");
- /*
- * Initial processing of HAVING clause is just like WHERE clause.
- */
+ /* initial processing of HAVING clause is much like WHERE clause */
qry->havingQual = transformWhereClause(pstate, stmt->havingClause,
- "HAVING");
+ EXPR_KIND_HAVING, "HAVING");
/*
* Transform sorting/grouping stuff. Do ORDER BY first because both
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
&qry->targetList,
+ EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
false /* allow SQL92 rules */ );
stmt->groupClause,
&qry->targetList,
qry->sortClause,
+ EXPR_KIND_GROUP_BY,
false /* allow SQL92 rules */ );
if (stmt->distinctClause == NIL)
/* transform LIMIT */
qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
- "OFFSET");
+ EXPR_KIND_OFFSET, "OFFSET");
qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
- "LIMIT");
+ EXPR_KIND_LIMIT, "LIMIT");
/* transform window clauses after we have seen all window functions */
qry->windowClause = transformWindowDefinitions(pstate,
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
- if (pstate->p_hasWindowFuncs)
- parseCheckWindowFuncs(pstate, qry);
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
List *sublist = (List *) lfirst(lc);
/* Do basic expression transformation (same as a ROW() expr) */
- sublist = transformExpressionList(pstate, sublist);
+ sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES);
/*
* All the sublists must be the same length, *after* transformation
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
&qry->targetList,
+ EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
false /* allow SQL92 rules */ );
qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
- "OFFSET");
+ EXPR_KIND_OFFSET, "OFFSET");
qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
- "LIMIT");
+ EXPR_KIND_LIMIT, "LIMIT");
if (stmt->lockingClause)
ereport(ERROR,
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
- /* aggregates not allowed (but subselects are okay) */
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in VALUES"),
- parser_errposition(pstate,
- locate_agg_of_level((Node *) exprsLists, 0))));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in VALUES"),
- parser_errposition(pstate,
- locate_windowfunc((Node *) exprsLists))));
assign_query_collations(pstate, qry);
qry->sortClause = transformSortClause(pstate,
sortClause,
&qry->targetList,
+ EXPR_KIND_ORDER_BY,
false /* no unknowns expected */ ,
false /* allow SQL92 rules */ );
exprLocation(list_nth(qry->targetList, tllen)))));
qry->limitOffset = transformLimitClause(pstate, limitOffset,
- "OFFSET");
+ EXPR_KIND_OFFSET, "OFFSET");
qry->limitCount = transformLimitClause(pstate, limitCount,
- "LIMIT");
+ EXPR_KIND_LIMIT, "LIMIT");
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
- if (pstate->p_hasWindowFuncs)
- parseCheckWindowFuncs(pstate, qry);
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
*/
transformFromClause(pstate, stmt->fromClause);
- qry->targetList = transformTargetList(pstate, stmt->targetList);
+ qry->targetList = transformTargetList(pstate, stmt->targetList,
+ EXPR_KIND_UPDATE_SOURCE);
- qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
+ qual = transformWhereClause(pstate, stmt->whereClause,
+ EXPR_KIND_WHERE, "WHERE");
qry->returningList = transformReturningList(pstate, stmt->returningList);
qry->hasSubLinks = pstate->p_hasSubLinks;
- /*
- * Top-level aggregates are simply disallowed in UPDATE, per spec. (From
- * an implementation point of view, this is forced because the implicit
- * ctid reference would otherwise be an ungrouped variable.)
- */
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in UPDATE"),
- parser_errposition(pstate,
- locate_agg_of_level((Node *) qry, 0))));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in UPDATE"),
- parser_errposition(pstate,
- locate_windowfunc((Node *) qry))));
-
/*
* Now we are done with SELECT-like processing, and can get on with
* transforming the target list to match the UPDATE target columns.
{
List *rlist;
int save_next_resno;
- bool save_hasAggs;
- bool save_hasWindowFuncs;
if (returningList == NIL)
return NIL; /* nothing to do */
save_next_resno = pstate->p_next_resno;
pstate->p_next_resno = 1;
- /* save other state so that we can detect disallowed stuff */
- save_hasAggs = pstate->p_hasAggs;
- pstate->p_hasAggs = false;
- save_hasWindowFuncs = pstate->p_hasWindowFuncs;
- pstate->p_hasWindowFuncs = false;
-
/* transform RETURNING identically to a SELECT targetlist */
- rlist = transformTargetList(pstate, returningList);
-
- /* check for disallowed stuff */
-
- /* aggregates not allowed (but subselects are okay) */
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in RETURNING"),
- parser_errposition(pstate,
- locate_agg_of_level((Node *) rlist, 0))));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in RETURNING"),
- parser_errposition(pstate,
- locate_windowfunc((Node *) rlist))));
+ rlist = transformTargetList(pstate, returningList, EXPR_KIND_RETURNING);
/* mark column origins */
markTargetListOrigins(pstate, rlist);
/* restore state */
pstate->p_next_resno = save_next_resno;
- pstate->p_hasAggs = save_hasAggs;
- pstate->p_hasWindowFuncs = save_hasWindowFuncs;
return rlist;
}
#include "optimizer/tlist.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
+#include "parser/parse_expr.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
+typedef struct
+{
+ ParseState *pstate;
+ int min_varlevel;
+ int min_agglevel;
+ int sublevels_up;
+} check_agg_arguments_context;
+
typedef struct
{
ParseState *pstate;
int sublevels_up;
} check_ungrouped_columns_context;
+static int check_agg_arguments(ParseState *pstate, List *args);
+static bool check_agg_arguments_walker(Node *node,
+ check_agg_arguments_context *context);
static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
List *groupClauses, bool have_non_var_grouping,
List **func_grouped_rels);
int save_next_resno;
int min_varlevel;
ListCell *lc;
+ const char *err;
+ bool errkind;
/*
* Transform the plain list of Exprs into a targetlist. We don't bother
torder = transformSortClause(pstate,
aggorder,
&tlist,
+ EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
true /* force SQL99 rules */ );
pstate->p_next_resno = save_next_resno;
/*
- * The aggregate's level is the same as the level of the lowest-level
- * variable or aggregate in its arguments; or if it contains no variables
- * at all, we presume it to be local.
+ * Check the arguments to compute the aggregate's level and detect
+ * improper nesting.
*/
- min_varlevel = find_minimum_var_level((Node *) agg->args);
+ min_varlevel = check_agg_arguments(pstate, agg->args);
+ agg->agglevelsup = min_varlevel;
+
+ /* Mark the correct pstate level as having aggregates */
+ while (min_varlevel-- > 0)
+ pstate = pstate->parentParseState;
+ pstate->p_hasAggs = true;
/*
- * An aggregate can't directly contain another aggregate call of the same
- * level (though outer aggs are okay). We can skip this check if we
- * didn't find any local vars or aggs.
+ * Check to see if the aggregate function is in an invalid place within
+ * its aggregation query.
+ *
+ * For brevity we support two schemes for reporting an error here: set
+ * "err" to a custom message, or set "errkind" true if the error context
+ * is sufficiently identified by what ParseExprKindName will return, *and*
+ * what it will return is just a SQL keyword. (Otherwise, use a custom
+ * message to avoid creating translation problems.)
*/
- if (min_varlevel == 0)
+ err = NULL;
+ errkind = false;
+ switch (pstate->p_expr_kind)
{
- if (pstate->p_hasAggs &&
- checkExprHasAggs((Node *) agg->args))
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("aggregate function calls cannot be nested"),
- parser_errposition(pstate,
- locate_agg_of_level((Node *) agg->args, 0))));
- }
+ case EXPR_KIND_NONE:
+ Assert(false); /* can't happen */
+ break;
+ case EXPR_KIND_OTHER:
+ /* Accept aggregate here; caller must throw error if wanted */
+ break;
+ case EXPR_KIND_JOIN_ON:
+ case EXPR_KIND_JOIN_USING:
+ err = _("aggregate functions are not allowed in JOIN conditions");
+ break;
+ case EXPR_KIND_FROM_SUBSELECT:
+ /* Should only be possible in a LATERAL subquery */
+ Assert(pstate->p_lateral_active);
+ /* Aggregate scope rules make it worth being explicit here */
+ err = _("aggregate functions are not allowed in FROM clause of their own query level");
+ break;
+ case EXPR_KIND_FROM_FUNCTION:
+ err = _("aggregate functions are not allowed in functions in FROM");
+ break;
+ case EXPR_KIND_WHERE:
+ errkind = true;
+ break;
+ case EXPR_KIND_HAVING:
+ /* okay */
+ break;
+ case EXPR_KIND_WINDOW_PARTITION:
+ /* okay */
+ break;
+ case EXPR_KIND_WINDOW_ORDER:
+ /* okay */
+ break;
+ case EXPR_KIND_WINDOW_FRAME_RANGE:
+ err = _("aggregate functions are not allowed in window RANGE");
+ break;
+ case EXPR_KIND_WINDOW_FRAME_ROWS:
+ err = _("aggregate functions are not allowed in window ROWS");
+ break;
+ case EXPR_KIND_SELECT_TARGET:
+ /* okay */
+ break;
+ case EXPR_KIND_INSERT_TARGET:
+ case EXPR_KIND_UPDATE_SOURCE:
+ case EXPR_KIND_UPDATE_TARGET:
+ errkind = true;
+ break;
+ case EXPR_KIND_GROUP_BY:
+ errkind = true;
+ break;
+ case EXPR_KIND_ORDER_BY:
+ /* okay */
+ break;
+ case EXPR_KIND_DISTINCT_ON:
+ /* okay */
+ break;
+ case EXPR_KIND_LIMIT:
+ case EXPR_KIND_OFFSET:
+ errkind = true;
+ break;
+ case EXPR_KIND_RETURNING:
+ errkind = true;
+ break;
+ case EXPR_KIND_VALUES:
+ errkind = true;
+ break;
+ case EXPR_KIND_CHECK_CONSTRAINT:
+ case EXPR_KIND_DOMAIN_CHECK:
+ err = _("aggregate functions are not allowed in CHECK constraints");
+ break;
+ case EXPR_KIND_COLUMN_DEFAULT:
+ case EXPR_KIND_FUNCTION_DEFAULT:
+ err = _("aggregate functions are not allowed in DEFAULT expressions");
+ break;
+ case EXPR_KIND_INDEX_EXPRESSION:
+ err = _("aggregate functions are not allowed in index expressions");
+ break;
+ case EXPR_KIND_INDEX_PREDICATE:
+ err = _("aggregate functions are not allowed in index predicates");
+ break;
+ case EXPR_KIND_ALTER_COL_TRANSFORM:
+ err = _("aggregate functions are not allowed in transform expressions");
+ break;
+ case EXPR_KIND_EXECUTE_PARAMETER:
+ err = _("aggregate functions are not allowed in EXECUTE parameters");
+ break;
+ case EXPR_KIND_TRIGGER_WHEN:
+ err = _("aggregate functions are not allowed in trigger WHEN conditions");
+ break;
- /* It can't contain window functions either */
- if (pstate->p_hasWindowFuncs &&
- checkExprHasWindowFuncs((Node *) agg->args))
+ /*
+ * There is intentionally no default: case here, so that the
+ * compiler will warn if we add a new ParseExprKind without
+ * extending this switch. If we do see an unrecognized value at
+ * runtime, the behavior will be the same as for EXPR_KIND_OTHER,
+ * which is sane anyway.
+ */
+ }
+ if (err)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
- errmsg("aggregate function calls cannot contain window function calls"),
- parser_errposition(pstate,
- locate_windowfunc((Node *) agg->args))));
+ errmsg_internal("%s", err),
+ parser_errposition(pstate, agg->location)));
+ if (errkind)
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ /* translator: %s is name of a SQL construct, eg GROUP BY */
+ errmsg("aggregate functions are not allowed in %s",
+ ParseExprKindName(pstate->p_expr_kind)),
+ parser_errposition(pstate, agg->location)));
+}
- if (min_varlevel < 0)
- min_varlevel = 0;
- agg->agglevelsup = min_varlevel;
+/*
+ * check_agg_arguments
+ * Scan the arguments of an aggregate function to determine the
+ * aggregate's semantic level (zero is the current select's level,
+ * one is its parent, etc).
+ *
+ * The aggregate's level is the same as the level of the lowest-level variable
+ * or aggregate in its arguments; or if it contains no variables at all, we
+ * presume it to be local.
+ *
+ * We also take this opportunity to detect any aggregates or window functions
+ * nested within the arguments. We can throw error immediately if we find
+ * a window function. Aggregates are a bit trickier because it's only an
+ * error if the inner aggregate is of the same semantic level as the outer,
+ * which we can't know until we finish scanning the arguments.
+ */
+static int
+check_agg_arguments(ParseState *pstate, List *args)
+{
+ int agglevel;
+ check_agg_arguments_context context;
- /* Mark the correct pstate as having aggregates */
- while (min_varlevel-- > 0)
- pstate = pstate->parentParseState;
- pstate->p_hasAggs = true;
+ context.pstate = pstate;
+ context.min_varlevel = -1; /* signifies nothing found yet */
+ context.min_agglevel = -1;
+ context.sublevels_up = 0;
+
+ (void) expression_tree_walker((Node *) args,
+ check_agg_arguments_walker,
+ (void *) &context);
+
+ /*
+ * If we found no vars nor aggs at all, it's a level-zero aggregate;
+ * otherwise, its level is the minimum of vars or aggs.
+ */
+ if (context.min_varlevel < 0)
+ {
+ if (context.min_agglevel < 0)
+ return 0;
+ agglevel = context.min_agglevel;
+ }
+ else if (context.min_agglevel < 0)
+ agglevel = context.min_varlevel;
+ else
+ agglevel = Min(context.min_varlevel, context.min_agglevel);
/*
- * Complain if we are inside a LATERAL subquery of the aggregation query.
- * We must be in its FROM clause, so the aggregate is misplaced.
+ * If there's a nested aggregate of the same semantic level, complain.
*/
- if (pstate->p_lateral_active)
+ if (agglevel == context.min_agglevel)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
- errmsg("aggregates not allowed in FROM clause"),
- parser_errposition(pstate, agg->location)));
+ errmsg("aggregate function calls cannot be nested"),
+ parser_errposition(pstate,
+ locate_agg_of_level((Node *) args,
+ agglevel))));
+
+ return agglevel;
+}
+
+static bool
+check_agg_arguments_walker(Node *node,
+ check_agg_arguments_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ int varlevelsup = ((Var *) node)->varlevelsup;
+
+ /* convert levelsup to frame of reference of original query */
+ varlevelsup -= context->sublevels_up;
+ /* ignore local vars of subqueries */
+ if (varlevelsup >= 0)
+ {
+ if (context->min_varlevel < 0 ||
+ context->min_varlevel > varlevelsup)
+ context->min_varlevel = varlevelsup;
+ }
+ return false;
+ }
+ if (IsA(node, Aggref))
+ {
+ int agglevelsup = ((Aggref *) node)->agglevelsup;
+
+ /* convert levelsup to frame of reference of original query */
+ agglevelsup -= context->sublevels_up;
+ /* ignore local aggs of subqueries */
+ if (agglevelsup >= 0)
+ {
+ if (context->min_agglevel < 0 ||
+ context->min_agglevel > agglevelsup)
+ context->min_agglevel = agglevelsup;
+ }
+ /* no need to examine args of the inner aggregate */
+ return false;
+ }
+ /* We can throw error on sight for a window function */
+ if (IsA(node, WindowFunc))
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ errmsg("aggregate function calls cannot contain window function calls"),
+ parser_errposition(context->pstate,
+ ((WindowFunc *) node)->location)));
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node,
+ check_agg_arguments_walker,
+ (void *) context,
+ 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node,
+ check_agg_arguments_walker,
+ (void *) context);
}
/*
transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
WindowDef *windef)
{
+ const char *err;
+ bool errkind;
+
/*
* A window function call can't contain another one (but aggs are OK). XXX
* is this required by spec, or just an unimplemented feature?
*/
if (pstate->p_hasWindowFuncs &&
- checkExprHasWindowFuncs((Node *) wfunc->args))
+ contain_windowfuncs((Node *) wfunc->args))
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("window function calls cannot be nested"),
parser_errposition(pstate,
locate_windowfunc((Node *) wfunc->args))));
+ /*
+ * Check to see if the window function is in an invalid place within the
+ * query.
+ *
+ * For brevity we support two schemes for reporting an error here: set
+ * "err" to a custom message, or set "errkind" true if the error context
+ * is sufficiently identified by what ParseExprKindName will return, *and*
+ * what it will return is just a SQL keyword. (Otherwise, use a custom
+ * message to avoid creating translation problems.)
+ */
+ err = NULL;
+ errkind = false;
+ switch (pstate->p_expr_kind)
+ {
+ case EXPR_KIND_NONE:
+ Assert(false); /* can't happen */
+ break;
+ case EXPR_KIND_OTHER:
+ /* Accept window func here; caller must throw error if wanted */
+ break;
+ case EXPR_KIND_JOIN_ON:
+ case EXPR_KIND_JOIN_USING:
+ err = _("window functions are not allowed in JOIN conditions");
+ break;
+ case EXPR_KIND_FROM_SUBSELECT:
+ /* can't get here, but just in case, throw an error */
+ errkind = true;
+ break;
+ case EXPR_KIND_FROM_FUNCTION:
+ err = _("window functions are not allowed in functions in FROM");
+ break;
+ case EXPR_KIND_WHERE:
+ errkind = true;
+ break;
+ case EXPR_KIND_HAVING:
+ errkind = true;
+ break;
+ case EXPR_KIND_WINDOW_PARTITION:
+ case EXPR_KIND_WINDOW_ORDER:
+ case EXPR_KIND_WINDOW_FRAME_RANGE:
+ case EXPR_KIND_WINDOW_FRAME_ROWS:
+ err = _("window functions are not allowed in window definitions");
+ break;
+ case EXPR_KIND_SELECT_TARGET:
+ /* okay */
+ break;
+ case EXPR_KIND_INSERT_TARGET:
+ case EXPR_KIND_UPDATE_SOURCE:
+ case EXPR_KIND_UPDATE_TARGET:
+ errkind = true;
+ break;
+ case EXPR_KIND_GROUP_BY:
+ errkind = true;
+ break;
+ case EXPR_KIND_ORDER_BY:
+ /* okay */
+ break;
+ case EXPR_KIND_DISTINCT_ON:
+ /* okay */
+ break;
+ case EXPR_KIND_LIMIT:
+ case EXPR_KIND_OFFSET:
+ errkind = true;
+ break;
+ case EXPR_KIND_RETURNING:
+ errkind = true;
+ break;
+ case EXPR_KIND_VALUES:
+ errkind = true;
+ break;
+ case EXPR_KIND_CHECK_CONSTRAINT:
+ case EXPR_KIND_DOMAIN_CHECK:
+ err = _("window functions are not allowed in CHECK constraints");
+ break;
+ case EXPR_KIND_COLUMN_DEFAULT:
+ case EXPR_KIND_FUNCTION_DEFAULT:
+ err = _("window functions are not allowed in DEFAULT expressions");
+ break;
+ case EXPR_KIND_INDEX_EXPRESSION:
+ err = _("window functions are not allowed in index expressions");
+ break;
+ case EXPR_KIND_INDEX_PREDICATE:
+ err = _("window functions are not allowed in index predicates");
+ break;
+ case EXPR_KIND_ALTER_COL_TRANSFORM:
+ err = _("window functions are not allowed in transform expressions");
+ break;
+ case EXPR_KIND_EXECUTE_PARAMETER:
+ err = _("window functions are not allowed in EXECUTE parameters");
+ break;
+ case EXPR_KIND_TRIGGER_WHEN:
+ err = _("window functions are not allowed in trigger WHEN conditions");
+ break;
+
+ /*
+ * There is intentionally no default: case here, so that the
+ * compiler will warn if we add a new ParseExprKind without
+ * extending this switch. If we do see an unrecognized value at
+ * runtime, the behavior will be the same as for EXPR_KIND_OTHER,
+ * which is sane anyway.
+ */
+ }
+ if (err)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg_internal("%s", err),
+ parser_errposition(pstate, wfunc->location)));
+ if (errkind)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ /* translator: %s is name of a SQL construct, eg GROUP BY */
+ errmsg("window functions are not allowed in %s",
+ ParseExprKindName(pstate->p_expr_kind)),
+ parser_errposition(pstate, wfunc->location)));
+
/*
* If the OVER clause just specifies a window name, find that WINDOW
* clause (which had better be present). Otherwise, try to match all the
/*
* parseCheckAggregates
* Check for aggregates where they shouldn't be and improper grouping.
+ * This function should be called after the target list and qualifications
+ * are finalized.
*
- * Ideally this should be done earlier, but it's difficult to distinguish
- * aggregates from plain functions at the grammar level. So instead we
- * check here. This function should be called after the target list and
- * qualifications are finalized.
+ * Misplaced aggregates are now mostly detected in transformAggregateCall,
+ * but it seems more robust to check for aggregates in recursive queries
+ * only after everything is finalized. In any case it's hard to detect
+ * improper grouping on-the-fly, so we have to make another pass over the
+ * query for that.
*/
void
parseCheckAggregates(ParseState *pstate, Query *qry)
}
/*
- * Aggregates must never appear in WHERE or JOIN/ON clauses.
- *
- * (Note this check should appear first to deliver an appropriate error
- * message; otherwise we are likely to complain about some innocent
- * variable in the target list, which is outright misleading if the
- * problem is in WHERE.)
- */
- if (checkExprHasAggs(qry->jointree->quals))
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("aggregates not allowed in WHERE clause"),
- parser_errposition(pstate,
- locate_agg_of_level(qry->jointree->quals, 0))));
- if (checkExprHasAggs((Node *) qry->jointree->fromlist))
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("aggregates not allowed in JOIN conditions"),
- parser_errposition(pstate,
- locate_agg_of_level((Node *) qry->jointree->fromlist, 0))));
-
- /*
- * No aggregates allowed in GROUP BY clauses, either.
- *
- * While we are at it, build a list of the acceptable GROUP BY expressions
- * for use by check_ungrouped_columns().
+ * Build a list of the acceptable GROUP BY expressions for use by
+ * check_ungrouped_columns().
*/
foreach(l, qry->groupClause)
{
expr = get_sortgroupclause_expr(grpcl, qry->targetList);
if (expr == NULL)
continue; /* probably cannot happen */
- if (checkExprHasAggs(expr))
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("aggregates not allowed in GROUP BY clause"),
- parser_errposition(pstate,
- locate_agg_of_level(expr, 0))));
groupClauses = lcons(expr, groupClauses);
}
if (pstate->p_hasAggs && hasSelfRefRTEs)
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
- errmsg("aggregate functions not allowed in a recursive query's recursive term"),
+ errmsg("aggregate functions are not allowed in a recursive query's recursive term"),
parser_errposition(pstate,
locate_agg_of_level((Node *) qry, 0))));
}
-/*
- * parseCheckWindowFuncs
- * Check for window functions where they shouldn't be.
- *
- * We have to forbid window functions in WHERE, JOIN/ON, HAVING, GROUP BY,
- * and window specifications. (Other clauses, such as RETURNING and LIMIT,
- * have already been checked.) Transformation of all these clauses must
- * be completed already.
- */
-void
-parseCheckWindowFuncs(ParseState *pstate, Query *qry)
-{
- ListCell *l;
-
- /* This should only be called if we found window functions */
- Assert(pstate->p_hasWindowFuncs);
-
- if (checkExprHasWindowFuncs(qry->jointree->quals))
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("window functions not allowed in WHERE clause"),
- parser_errposition(pstate,
- locate_windowfunc(qry->jointree->quals))));
- if (checkExprHasWindowFuncs((Node *) qry->jointree->fromlist))
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("window functions not allowed in JOIN conditions"),
- parser_errposition(pstate,
- locate_windowfunc((Node *) qry->jointree->fromlist))));
- if (checkExprHasWindowFuncs(qry->havingQual))
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("window functions not allowed in HAVING clause"),
- parser_errposition(pstate,
- locate_windowfunc(qry->havingQual))));
-
- foreach(l, qry->groupClause)
- {
- SortGroupClause *grpcl = (SortGroupClause *) lfirst(l);
- Node *expr;
-
- expr = get_sortgroupclause_expr(grpcl, qry->targetList);
- if (checkExprHasWindowFuncs(expr))
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("window functions not allowed in GROUP BY clause"),
- parser_errposition(pstate,
- locate_windowfunc(expr))));
- }
-
- foreach(l, qry->windowClause)
- {
- WindowClause *wc = (WindowClause *) lfirst(l);
- ListCell *l2;
-
- foreach(l2, wc->partitionClause)
- {
- SortGroupClause *grpcl = (SortGroupClause *) lfirst(l2);
- Node *expr;
-
- expr = get_sortgroupclause_expr(grpcl, qry->targetList);
- if (checkExprHasWindowFuncs(expr))
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("window functions not allowed in window definition"),
- parser_errposition(pstate,
- locate_windowfunc(expr))));
- }
- foreach(l2, wc->orderClause)
- {
- SortGroupClause *grpcl = (SortGroupClause *) lfirst(l2);
- Node *expr;
-
- expr = get_sortgroupclause_expr(grpcl, qry->targetList);
- if (checkExprHasWindowFuncs(expr))
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("window functions not allowed in window definition"),
- parser_errposition(pstate,
- locate_windowfunc(expr))));
- }
- /* startOffset and limitOffset were checked in transformFrameOffset */
- }
-}
-
/*
* check_ungrouped_columns -
* Scan the given expression tree for ungrouped variables (variables
/* Convenience macro for the most common makeNamespaceItem() case */
#define makeDefaultNSItem(rte) makeNamespaceItem(rte, true, true, false, true)
-/* clause types for findTargetlistEntrySQL92 */
-#define ORDER_CLAUSE 0
-#define GROUP_CLAUSE 1
-#define DISTINCT_ON_CLAUSE 2
-
-static const char *const clauseText[] = {
- "ORDER BY",
- "GROUP BY",
- "DISTINCT ON"
-};
-
static void extractRemainingColumns(List *common_colnames,
List *src_colnames, List *src_colvars,
List **res_colnames, List **res_colvars);
static void checkExprIsVarFree(ParseState *pstate, Node *n,
const char *constructName);
static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node,
- List **tlist, int clause);
+ List **tlist, ParseExprKind exprKind);
static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node,
- List **tlist);
+ List **tlist, ParseExprKind exprKind);
static int get_matching_location(int sortgroupref,
List *sortgrouprefs, List *exprs);
static List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
* transformJoinOnClause() does. Just invoke transformExpr() to fix up
* the operators, and we're done.
*/
- result = transformExpr(pstate, result);
+ result = transformExpr(pstate, result, EXPR_KIND_JOIN_USING);
result = coerce_to_boolean(pstate, result, "JOIN/USING");
save_namespace = pstate->p_namespace;
pstate->p_namespace = namespace;
- result = transformWhereClause(pstate, j->quals, "JOIN/ON");
+ result = transformWhereClause(pstate, j->quals,
+ EXPR_KIND_JOIN_ON, "JOIN/ON");
pstate->p_namespace = save_namespace;
if (r->alias == NULL)
elog(ERROR, "subquery in FROM must have an alias");
+ /*
+ * Set p_expr_kind to show this parse level is recursing to a subselect.
+ * We can't be nested within any expression, so don't need save-restore
+ * logic here.
+ */
+ Assert(pstate->p_expr_kind == EXPR_KIND_NONE);
+ pstate->p_expr_kind = EXPR_KIND_FROM_SUBSELECT;
+
/*
* If the subselect is LATERAL, make lateral_only names of this level
* visible to it. (LATERAL can't nest within a single pstate level, so we
query = parse_sub_analyze(r->subquery, pstate, NULL,
isLockedRefname(pstate, r->alias->aliasname));
+ /* Restore state */
pstate->p_lateral_active = false;
+ pstate->p_expr_kind = EXPR_KIND_NONE;
/*
* Check that we got something reasonable. Many of these conditions are
/*
* Transform the raw expression.
*/
- funcexpr = transformExpr(pstate, r->funccallnode);
+ funcexpr = transformExpr(pstate, r->funccallnode, EXPR_KIND_FROM_FUNCTION);
pstate->p_lateral_active = false;
*/
assign_expr_collations(pstate, funcexpr);
- /*
- * Disallow aggregate functions in the expression. (No reason to postpone
- * this check until parseCheckAggregates.)
- */
- if (pstate->p_hasAggs &&
- checkExprHasAggs(funcexpr))
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in function expression in FROM"),
- parser_errposition(pstate,
- locate_agg_of_level(funcexpr, 0))));
- if (pstate->p_hasWindowFuncs &&
- checkExprHasWindowFuncs(funcexpr))
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in function expression in FROM"),
- parser_errposition(pstate,
- locate_windowfunc(funcexpr))));
-
/*
* OK, build an RTE for the function.
*/
*/
Node *
transformWhereClause(ParseState *pstate, Node *clause,
- const char *constructName)
+ ParseExprKind exprKind, const char *constructName)
{
Node *qual;
if (clause == NULL)
return NULL;
- qual = transformExpr(pstate, clause);
+ qual = transformExpr(pstate, clause, exprKind);
qual = coerce_to_boolean(pstate, qual, constructName);
*/
Node *
transformLimitClause(ParseState *pstate, Node *clause,
- const char *constructName)
+ ParseExprKind exprKind, const char *constructName)
{
Node *qual;
if (clause == NULL)
return NULL;
- qual = transformExpr(pstate, clause);
+ qual = transformExpr(pstate, clause, exprKind);
qual = coerce_to_specific_type(pstate, qual, INT8OID, constructName);
- /* LIMIT can't refer to any vars or aggregates of the current query */
+ /* LIMIT can't refer to any variables of the current query */
checkExprIsVarFree(pstate, qual, constructName);
return qual;
/*
* checkExprIsVarFree
* Check that given expr has no Vars of the current query level
- * (and no aggregates or window functions, either).
+ * (aggregates and window functions should have been rejected already).
*
* This is used to check expressions that have to have a consistent value
* across all rows of the query, such as a LIMIT. Arguably it should reject
parser_errposition(pstate,
locate_var_of_level(n, 0))));
}
- if (pstate->p_hasAggs &&
- checkExprHasAggs(n))
- {
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- /* translator: %s is name of a SQL construct, eg LIMIT */
- errmsg("argument of %s must not contain aggregate functions",
- constructName),
- parser_errposition(pstate,
- locate_agg_of_level(n, 0))));
- }
- if (pstate->p_hasWindowFuncs &&
- checkExprHasWindowFuncs(n))
+}
+
+
+/*
+ * checkTargetlistEntrySQL92 -
+ * Validate a targetlist entry found by findTargetlistEntrySQL92
+ *
+ * When we select a pre-existing tlist entry as a result of syntax such
+ * as "GROUP BY 1", we have to make sure it is acceptable for use in the
+ * indicated clause type; transformExpr() will have treated it as a regular
+ * targetlist item.
+ */
+static void
+checkTargetlistEntrySQL92(ParseState *pstate, TargetEntry *tle,
+ ParseExprKind exprKind)
+{
+ switch (exprKind)
{
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- /* translator: %s is name of a SQL construct, eg LIMIT */
- errmsg("argument of %s must not contain window functions",
- constructName),
- parser_errposition(pstate,
- locate_windowfunc(n))));
+ case EXPR_KIND_GROUP_BY:
+ /* reject aggregates and window functions */
+ if (pstate->p_hasAggs &&
+ contain_aggs_of_level((Node *) tle->expr, 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ /* translator: %s is name of a SQL construct, eg GROUP BY */
+ errmsg("aggregate functions are not allowed in %s",
+ ParseExprKindName(exprKind)),
+ parser_errposition(pstate,
+ locate_agg_of_level((Node *) tle->expr, 0))));
+ if (pstate->p_hasWindowFuncs &&
+ contain_windowfuncs((Node *) tle->expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ /* translator: %s is name of a SQL construct, eg GROUP BY */
+ errmsg("window functions are not allowed in %s",
+ ParseExprKindName(exprKind)),
+ parser_errposition(pstate,
+ locate_windowfunc((Node *) tle->expr))));
+ break;
+ case EXPR_KIND_ORDER_BY:
+ /* no extra checks needed */
+ break;
+ case EXPR_KIND_DISTINCT_ON:
+ /* no extra checks needed */
+ break;
+ default:
+ elog(ERROR, "unexpected exprKind in checkTargetlistEntrySQL92");
+ break;
}
}
-
/*
* findTargetlistEntrySQL92 -
* Returns the targetlist entry matching the given (untransformed) node.
*
* node the ORDER BY, GROUP BY, or DISTINCT ON expression to be matched
* tlist the target list (passed by reference so we can append to it)
- * clause identifies clause type being processed
+ * exprKind identifies clause type being processed
*/
static TargetEntry *
findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
- int clause)
+ ParseExprKind exprKind)
{
ListCell *tl;
char *name = strVal(linitial(((ColumnRef *) node)->fields));
int location = ((ColumnRef *) node)->location;
- if (clause == GROUP_CLAUSE)
+ if (exprKind == EXPR_KIND_GROUP_BY)
{
/*
* In GROUP BY, we must prefer a match against a FROM-clause
/*------
translator: first %s is name of a SQL construct, eg ORDER BY */
errmsg("%s \"%s\" is ambiguous",
- clauseText[clause], name),
+ ParseExprKindName(exprKind),
+ name),
parser_errposition(pstate, location)));
}
else
}
}
if (target_result != NULL)
- return target_result; /* return the first match */
+ {
+ /* return the first match, after suitable validation */
+ checkTargetlistEntrySQL92(pstate, target_result, exprKind);
+ return target_result;
+ }
}
}
if (IsA(node, A_Const))
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: %s is name of a SQL construct, eg ORDER BY */
errmsg("non-integer constant in %s",
- clauseText[clause]),
+ ParseExprKindName(exprKind)),
parser_errposition(pstate, location)));
target_pos = intVal(val);
if (!tle->resjunk)
{
if (++targetlist_pos == target_pos)
- return tle; /* return the unique match */
+ {
+ /* return the unique match, after suitable validation */
+ checkTargetlistEntrySQL92(pstate, tle, exprKind);
+ return tle;
+ }
}
}
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
/* translator: %s is name of a SQL construct, eg ORDER BY */
errmsg("%s position %d is not in select list",
- clauseText[clause], target_pos),
+ ParseExprKindName(exprKind), target_pos),
parser_errposition(pstate, location)));
}
/*
* Otherwise, we have an expression, so process it per SQL99 rules.
*/
- return findTargetlistEntrySQL99(pstate, node, tlist);
+ return findTargetlistEntrySQL99(pstate, node, tlist, exprKind);
}
/*
*
* node the ORDER BY, GROUP BY, etc expression to be matched
* tlist the target list (passed by reference so we can append to it)
+ * exprKind identifies clause type being processed
*/
static TargetEntry *
-findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist)
+findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist,
+ ParseExprKind exprKind)
{
TargetEntry *target_result;
ListCell *tl;
* resjunk target here, though the SQL92 cases above must ignore resjunk
* targets.
*/
- expr = transformExpr(pstate, node);
+ expr = transformExpr(pstate, node, exprKind);
foreach(tl, *tlist)
{
* end of the target list. This target is given resjunk = TRUE so that it
* will not be projected into the final tuple.
*/
- target_result = transformTargetEntry(pstate, node, expr, NULL, true);
+ target_result = transformTargetEntry(pstate, node, expr, exprKind,
+ NULL, true);
*tlist = lappend(*tlist, target_result);
List *
transformGroupClause(ParseState *pstate, List *grouplist,
List **targetlist, List *sortClause,
- bool useSQL99)
+ ParseExprKind exprKind, bool useSQL99)
{
List *result = NIL;
ListCell *gl;
bool found = false;
if (useSQL99)
- tle = findTargetlistEntrySQL99(pstate, gexpr, targetlist);
+ tle = findTargetlistEntrySQL99(pstate, gexpr,
+ targetlist, exprKind);
else
- tle = findTargetlistEntrySQL92(pstate, gexpr, targetlist,
- GROUP_CLAUSE);
+ tle = findTargetlistEntrySQL92(pstate, gexpr,
+ targetlist, exprKind);
/* Eliminate duplicates (GROUP BY x, x) */
if (targetIsInSortList(tle, InvalidOid, result))
transformSortClause(ParseState *pstate,
List *orderlist,
List **targetlist,
+ ParseExprKind exprKind,
bool resolveUnknown,
bool useSQL99)
{
TargetEntry *tle;
if (useSQL99)
- tle = findTargetlistEntrySQL99(pstate, sortby->node, targetlist);
+ tle = findTargetlistEntrySQL99(pstate, sortby->node,
+ targetlist, exprKind);
else
- tle = findTargetlistEntrySQL92(pstate, sortby->node, targetlist,
- ORDER_CLAUSE);
+ tle = findTargetlistEntrySQL92(pstate, sortby->node,
+ targetlist, exprKind);
sortlist = addTargetToSortList(pstate, tle,
sortlist, *targetlist, sortby,
orderClause = transformSortClause(pstate,
windef->orderClause,
targetlist,
+ EXPR_KIND_WINDOW_ORDER,
true /* fix unknowns */ ,
true /* force SQL99 rules */ );
partitionClause = transformGroupClause(pstate,
windef->partitionClause,
targetlist,
orderClause,
+ EXPR_KIND_WINDOW_PARTITION,
true /* force SQL99 rules */ );
/*
TargetEntry *tle;
tle = findTargetlistEntrySQL92(pstate, dexpr, targetlist,
- DISTINCT_ON_CLAUSE);
+ EXPR_KIND_DISTINCT_ON);
sortgroupref = assignSortGroupRef(tle, *targetlist);
sortgrouprefs = lappend_int(sortgrouprefs, sortgroupref);
}
if (clause == NULL)
return NULL;
- /* Transform the raw expression tree */
- node = transformExpr(pstate, clause);
-
if (frameOptions & FRAMEOPTION_ROWS)
{
+ /* Transform the raw expression tree */
+ node = transformExpr(pstate, clause, EXPR_KIND_WINDOW_FRAME_ROWS);
+
/*
* Like LIMIT clause, simply coerce to int8
*/
}
else if (frameOptions & FRAMEOPTION_RANGE)
{
+ /* Transform the raw expression tree */
+ node = transformExpr(pstate, clause, EXPR_KIND_WINDOW_FRAME_RANGE);
+
/*
* this needs a lot of thought to decide how to support in the context
* of Postgres' extensible datatype framework
elog(ERROR, "window frame with value offset is not implemented");
}
else
+ {
Assert(false);
+ node = NULL;
+ }
- /* Disallow variables and aggregates in frame offsets */
+ /* Disallow variables in frame offsets */
checkExprIsVarFree(pstate, node, constructName);
return node;
bool Transform_null_equals = false;
+static Node *transformExprRecurse(ParseState *pstate, Node *expr);
static Node *transformParamRef(ParseState *pstate, ParamRef *pref);
static Node *transformAExprOp(ParseState *pstate, A_Expr *a);
static Node *transformAExprAnd(ParseState *pstate, A_Expr *a);
* input and output of transformExpr; see SubLink for example.
*/
Node *
-transformExpr(ParseState *pstate, Node *expr)
+transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
{
- Node *result = NULL;
+ Node *result;
+ ParseExprKind sv_expr_kind;
+
+ /* Save and restore identity of expression type we're parsing */
+ Assert(exprKind != EXPR_KIND_NONE);
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = exprKind;
+
+ result = transformExprRecurse(pstate, expr);
+
+ pstate->p_expr_kind = sv_expr_kind;
+
+ return result;
+}
+
+static Node *
+transformExprRecurse(ParseState *pstate, Node *expr)
+{
+ Node *result;
if (expr == NULL)
return NULL;
{
A_Indirection *ind = (A_Indirection *) expr;
- result = transformExpr(pstate, ind->arg);
+ result = transformExprRecurse(pstate, ind->arg);
result = transformIndirection(pstate, result,
ind->indirection);
break;
break;
default:
elog(ERROR, "unrecognized A_Expr kind: %d", a->kind);
+ result = NULL; /* keep compiler quiet */
+ break;
}
break;
}
{
NamedArgExpr *na = (NamedArgExpr *) expr;
- na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg);
+ na->arg = (Expr *) transformExprRecurse(pstate, (Node *) na->arg);
result = expr;
break;
}
{
NullTest *n = (NullTest *) expr;
- n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg);
+ n->arg = (Expr *) transformExprRecurse(pstate, (Node *) n->arg);
/* the argument can be any type, so don't coerce it */
n->argisrow = type_is_rowtype(exprType((Node *) n->arg));
result = expr;
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
+ result = NULL; /* keep compiler quiet */
break;
}
else
n->arg = (Expr *) lexpr;
- result = transformExpr(pstate, (Node *) n);
+ result = transformExprRecurse(pstate, (Node *) n);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, SubLink) &&
s->testexpr = lexpr;
s->operName = a->name;
s->location = a->location;
- result = transformExpr(pstate, (Node *) s);
+ result = transformExprRecurse(pstate, (Node *) s);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
/* "row op row" */
- lexpr = transformExpr(pstate, lexpr);
- rexpr = transformExpr(pstate, rexpr);
+ lexpr = transformExprRecurse(pstate, lexpr);
+ rexpr = transformExprRecurse(pstate, rexpr);
Assert(IsA(lexpr, RowExpr));
Assert(IsA(rexpr, RowExpr));
else
{
/* Ordinary scalar operator */
- lexpr = transformExpr(pstate, lexpr);
- rexpr = transformExpr(pstate, rexpr);
+ lexpr = transformExprRecurse(pstate, lexpr);
+ rexpr = transformExprRecurse(pstate, rexpr);
result = (Node *) make_op(pstate,
a->name,
static Node *
transformAExprAnd(ParseState *pstate, A_Expr *a)
{
- Node *lexpr = transformExpr(pstate, a->lexpr);
- Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *lexpr = transformExprRecurse(pstate, a->lexpr);
+ Node *rexpr = transformExprRecurse(pstate, a->rexpr);
lexpr = coerce_to_boolean(pstate, lexpr, "AND");
rexpr = coerce_to_boolean(pstate, rexpr, "AND");
static Node *
transformAExprOr(ParseState *pstate, A_Expr *a)
{
- Node *lexpr = transformExpr(pstate, a->lexpr);
- Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *lexpr = transformExprRecurse(pstate, a->lexpr);
+ Node *rexpr = transformExprRecurse(pstate, a->rexpr);
lexpr = coerce_to_boolean(pstate, lexpr, "OR");
rexpr = coerce_to_boolean(pstate, rexpr, "OR");
static Node *
transformAExprNot(ParseState *pstate, A_Expr *a)
{
- Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *rexpr = transformExprRecurse(pstate, a->rexpr);
rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
static Node *
transformAExprOpAny(ParseState *pstate, A_Expr *a)
{
- Node *lexpr = transformExpr(pstate, a->lexpr);
- Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *lexpr = transformExprRecurse(pstate, a->lexpr);
+ Node *rexpr = transformExprRecurse(pstate, a->rexpr);
return (Node *) make_scalar_array_op(pstate,
a->name,
static Node *
transformAExprOpAll(ParseState *pstate, A_Expr *a)
{
- Node *lexpr = transformExpr(pstate, a->lexpr);
- Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *lexpr = transformExprRecurse(pstate, a->lexpr);
+ Node *rexpr = transformExprRecurse(pstate, a->rexpr);
return (Node *) make_scalar_array_op(pstate,
a->name,
static Node *
transformAExprDistinct(ParseState *pstate, A_Expr *a)
{
- Node *lexpr = transformExpr(pstate, a->lexpr);
- Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *lexpr = transformExprRecurse(pstate, a->lexpr);
+ Node *rexpr = transformExprRecurse(pstate, a->rexpr);
if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
static Node *
transformAExprNullIf(ParseState *pstate, A_Expr *a)
{
- Node *lexpr = transformExpr(pstate, a->lexpr);
- Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *lexpr = transformExprRecurse(pstate, a->lexpr);
+ Node *rexpr = transformExprRecurse(pstate, a->rexpr);
OpExpr *result;
result = (OpExpr *) make_op(pstate,
* Checking an expression for match to a list of type names. Will result
* in a boolean constant node.
*/
- Node *lexpr = transformExpr(pstate, a->lexpr);
+ Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Const *result;
ListCell *telem;
Oid ltype,
* First step: transform all the inputs, and detect whether any are
* RowExprs or contain Vars.
*/
- lexpr = transformExpr(pstate, a->lexpr);
+ lexpr = transformExprRecurse(pstate, a->lexpr);
haveRowExpr = (lexpr && IsA(lexpr, RowExpr));
rexprs = rvars = rnonvars = NIL;
foreach(l, (List *) a->rexpr)
{
- Node *rexpr = transformExpr(pstate, lfirst(l));
+ Node *rexpr = transformExprRecurse(pstate, lfirst(l));
haveRowExpr |= (rexpr && IsA(rexpr, RowExpr));
rexprs = lappend(rexprs, rexpr);
targs = NIL;
foreach(args, fn->args)
{
- targs = lappend(targs, transformExpr(pstate,
- (Node *) lfirst(args)));
+ targs = lappend(targs, transformExprRecurse(pstate,
+ (Node *) lfirst(args)));
}
/* ... and hand off to ParseFuncOrColumn */
newc = makeNode(CaseExpr);
/* transform the test expression, if any */
- arg = transformExpr(pstate, (Node *) c->arg);
+ arg = transformExprRecurse(pstate, (Node *) c->arg);
/* generate placeholder for test expression */
if (arg)
warg,
w->location);
}
- neww->expr = (Expr *) transformExpr(pstate, warg);
+ neww->expr = (Expr *) transformExprRecurse(pstate, warg);
neww->expr = (Expr *) coerce_to_boolean(pstate,
(Node *) neww->expr,
"CASE/WHEN");
warg = (Node *) w->result;
- neww->result = (Expr *) transformExpr(pstate, warg);
+ neww->result = (Expr *) transformExprRecurse(pstate, warg);
neww->location = w->location;
newargs = lappend(newargs, neww);
n->location = -1;
defresult = (Node *) n;
}
- newc->defresult = (Expr *) transformExpr(pstate, defresult);
+ newc->defresult = (Expr *) transformExprRecurse(pstate, defresult);
/*
* Note: default result is considered the most significant type in
{
Node *result = (Node *) sublink;
Query *qtree;
+ const char *err;
/* If we already transformed this node, do nothing */
if (IsA(sublink->subselect, Query))
return result;
+ /*
+ * Check to see if the sublink is in an invalid place within the query.
+ * We allow sublinks everywhere in SELECT/INSERT/UPDATE/DELETE, but
+ * generally not in utility statements.
+ */
+ err = NULL;
+ switch (pstate->p_expr_kind)
+ {
+ case EXPR_KIND_NONE:
+ Assert(false); /* can't happen */
+ break;
+ case EXPR_KIND_OTHER:
+ /* Accept sublink here; caller must throw error if wanted */
+ break;
+ case EXPR_KIND_JOIN_ON:
+ case EXPR_KIND_JOIN_USING:
+ case EXPR_KIND_FROM_SUBSELECT:
+ case EXPR_KIND_FROM_FUNCTION:
+ case EXPR_KIND_WHERE:
+ case EXPR_KIND_HAVING:
+ case EXPR_KIND_WINDOW_PARTITION:
+ case EXPR_KIND_WINDOW_ORDER:
+ case EXPR_KIND_WINDOW_FRAME_RANGE:
+ case EXPR_KIND_WINDOW_FRAME_ROWS:
+ case EXPR_KIND_SELECT_TARGET:
+ case EXPR_KIND_INSERT_TARGET:
+ case EXPR_KIND_UPDATE_SOURCE:
+ case EXPR_KIND_UPDATE_TARGET:
+ case EXPR_KIND_GROUP_BY:
+ case EXPR_KIND_ORDER_BY:
+ case EXPR_KIND_DISTINCT_ON:
+ case EXPR_KIND_LIMIT:
+ case EXPR_KIND_OFFSET:
+ case EXPR_KIND_RETURNING:
+ case EXPR_KIND_VALUES:
+ /* okay */
+ break;
+ case EXPR_KIND_CHECK_CONSTRAINT:
+ case EXPR_KIND_DOMAIN_CHECK:
+ err = _("cannot use subquery in CHECK constraint");
+ break;
+ case EXPR_KIND_COLUMN_DEFAULT:
+ case EXPR_KIND_FUNCTION_DEFAULT:
+ err = _("cannot use subquery in DEFAULT expression");
+ break;
+ case EXPR_KIND_INDEX_EXPRESSION:
+ err = _("cannot use subquery in index expression");
+ break;
+ case EXPR_KIND_INDEX_PREDICATE:
+ err = _("cannot use subquery in index predicate");
+ break;
+ case EXPR_KIND_ALTER_COL_TRANSFORM:
+ err = _("cannot use subquery in transform expression");
+ break;
+ case EXPR_KIND_EXECUTE_PARAMETER:
+ err = _("cannot use subquery in EXECUTE parameter");
+ break;
+ case EXPR_KIND_TRIGGER_WHEN:
+ err = _("cannot use subquery in trigger WHEN condition");
+ break;
+
+ /*
+ * There is intentionally no default: case here, so that the
+ * compiler will warn if we add a new ParseExprKind without
+ * extending this switch. If we do see an unrecognized value at
+ * runtime, the behavior will be the same as for EXPR_KIND_OTHER,
+ * which is sane anyway.
+ */
+ }
+ if (err)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg_internal("%s", err),
+ parser_errposition(pstate, sublink->location)));
+
pstate->p_hasSubLinks = true;
+
+ /*
+ * OK, let's transform the sub-SELECT.
+ */
qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
/*
/*
* Transform lefthand expression, and convert to a list
*/
- lefthand = transformExpr(pstate, sublink->testexpr);
+ lefthand = transformExprRecurse(pstate, sublink->testexpr);
if (lefthand && IsA(lefthand, RowExpr))
left_list = ((RowExpr *) lefthand)->args;
else
}
else
{
- newe = transformExpr(pstate, e);
+ newe = transformExprRecurse(pstate, e);
/*
* Check for sub-array expressions, if we haven't already found
ListCell *lc;
/* Transform the field expressions */
- newr->args = transformExpressionList(pstate, r->args);
+ newr->args = transformExpressionList(pstate, r->args, pstate->p_expr_kind);
/* Barring later casting, we consider the type RECORD */
newr->row_typeid = RECORDOID;
Node *e = (Node *) lfirst(args);
Node *newe;
- newe = transformExpr(pstate, e);
+ newe = transformExprRecurse(pstate, e);
newargs = lappend(newargs, newe);
}
Node *e = (Node *) lfirst(args);
Node *newe;
- newe = transformExpr(pstate, e);
+ newe = transformExprRecurse(pstate, e);
newargs = lappend(newargs, newe);
}
Assert(IsA(r, ResTarget));
- expr = transformExpr(pstate, r->val);
+ expr = transformExprRecurse(pstate, r->val);
if (r->name)
argname = map_sql_identifier_to_xml_name(r->name, false, false);
Node *e = (Node *) lfirst(lc);
Node *newe;
- newe = transformExpr(pstate, e);
+ newe = transformExprRecurse(pstate, e);
switch (x->op)
{
case IS_XMLCONCAT:
xexpr = makeNode(XmlExpr);
xexpr->op = IS_XMLSERIALIZE;
xexpr->args = list_make1(coerce_to_specific_type(pstate,
- transformExpr(pstate, xs->expr),
+ transformExprRecurse(pstate, xs->expr),
XMLOID,
"XMLSERIALIZE"));
clausename = NULL; /* keep compiler quiet */
}
- b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
+ b->arg = (Expr *) transformExprRecurse(pstate, (Node *) b->arg);
b->arg = (Expr *) coerce_to_boolean(pstate,
(Node *) b->arg,
transformTypeCast(ParseState *pstate, TypeCast *tc)
{
Node *result;
- Node *expr = transformExpr(pstate, tc->arg);
+ Node *expr = transformExprRecurse(pstate, tc->arg);
Oid inputType = exprType(expr);
Oid targetType;
int32 targetTypmod;
Oid argtype;
newc = makeNode(CollateExpr);
- newc->arg = (Expr *) transformExpr(pstate, c->arg);
+ newc->arg = (Expr *) transformExprRecurse(pstate, c->arg);
argtype = exprType((Node *) newc->arg);
return result;
}
+
+/*
+ * Produce a string identifying an expression by kind.
+ *
+ * Note: when practical, use a simple SQL keyword for the result. If that
+ * doesn't work well, check call sites to see whether custom error message
+ * strings are required.
+ */
+const char *
+ParseExprKindName(ParseExprKind exprKind)
+{
+ switch (exprKind)
+ {
+ case EXPR_KIND_NONE:
+ return "invalid expression context";
+ case EXPR_KIND_OTHER:
+ return "extension expression";
+ case EXPR_KIND_JOIN_ON:
+ return "JOIN/ON";
+ case EXPR_KIND_JOIN_USING:
+ return "JOIN/USING";
+ case EXPR_KIND_FROM_SUBSELECT:
+ return "sub-SELECT in FROM";
+ case EXPR_KIND_FROM_FUNCTION:
+ return "function in FROM";
+ case EXPR_KIND_WHERE:
+ return "WHERE";
+ case EXPR_KIND_HAVING:
+ return "HAVING";
+ case EXPR_KIND_WINDOW_PARTITION:
+ return "window PARTITION BY";
+ case EXPR_KIND_WINDOW_ORDER:
+ return "window ORDER BY";
+ case EXPR_KIND_WINDOW_FRAME_RANGE:
+ return "window RANGE";
+ case EXPR_KIND_WINDOW_FRAME_ROWS:
+ return "window ROWS";
+ case EXPR_KIND_SELECT_TARGET:
+ return "SELECT";
+ case EXPR_KIND_INSERT_TARGET:
+ return "INSERT";
+ case EXPR_KIND_UPDATE_SOURCE:
+ case EXPR_KIND_UPDATE_TARGET:
+ return "UPDATE";
+ case EXPR_KIND_GROUP_BY:
+ return "GROUP BY";
+ case EXPR_KIND_ORDER_BY:
+ return "ORDER BY";
+ case EXPR_KIND_DISTINCT_ON:
+ return "DISTINCT ON";
+ case EXPR_KIND_LIMIT:
+ return "LIMIT";
+ case EXPR_KIND_OFFSET:
+ return "OFFSET";
+ case EXPR_KIND_RETURNING:
+ return "RETURNING";
+ case EXPR_KIND_VALUES:
+ return "VALUES";
+ case EXPR_KIND_CHECK_CONSTRAINT:
+ case EXPR_KIND_DOMAIN_CHECK:
+ return "CHECK";
+ case EXPR_KIND_COLUMN_DEFAULT:
+ case EXPR_KIND_FUNCTION_DEFAULT:
+ return "DEFAULT";
+ case EXPR_KIND_INDEX_EXPRESSION:
+ return "index expression";
+ case EXPR_KIND_INDEX_PREDICATE:
+ return "index predicate";
+ case EXPR_KIND_ALTER_COL_TRANSFORM:
+ return "USING";
+ case EXPR_KIND_EXECUTE_PARAMETER:
+ return "EXECUTE";
+ case EXPR_KIND_TRIGGER_WHEN:
+ return "WHEN";
+
+ /*
+ * There is intentionally no default: case here, so that the
+ * compiler will warn if we add a new ParseExprKind without
+ * extending this switch. If we do see an unrecognized value at
+ * runtime, we'll fall through to the "unrecognized" return.
+ */
+ }
+ return "unrecognized expression kind";
+}
{
if (ai->lidx)
{
- subexpr = transformExpr(pstate, ai->lidx);
+ subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(pstate,
subexpr, exprType(subexpr),
}
lowerIndexpr = lappend(lowerIndexpr, subexpr);
}
- subexpr = transformExpr(pstate, ai->uidx);
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(pstate,
subexpr, exprType(subexpr),
Node *rhs,
int location);
static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
- bool targetlist);
+ bool make_target_entry);
static List *ExpandAllTables(ParseState *pstate, int location);
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
- bool targetlist);
+ bool make_target_entry, ParseExprKind exprKind);
static List *ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
- int location, bool targetlist);
+ int location, bool make_target_entry);
static List *ExpandRowReference(ParseState *pstate, Node *expr,
- bool targetlist);
+ bool make_target_entry);
static int FigureColnameInternal(Node *node, char **name);
*
* node the (untransformed) parse tree for the value expression.
* expr the transformed expression, or NULL if caller didn't do it yet.
+ * exprKind expression kind (EXPR_KIND_SELECT_TARGET, etc)
* colname the column name to be assigned, or NULL if none yet set.
* resjunk true if the target should be marked resjunk, ie, it is not
* wanted in the final projected tuple.
transformTargetEntry(ParseState *pstate,
Node *node,
Node *expr,
+ ParseExprKind exprKind,
char *colname,
bool resjunk)
{
/* Transform the node if caller didn't do it already */
if (expr == NULL)
- expr = transformExpr(pstate, node);
+ expr = transformExpr(pstate, node, exprKind);
if (colname == NULL && !resjunk)
{
* transformTargetList()
* Turns a list of ResTarget's into a list of TargetEntry's.
*
- * At this point, we don't care whether we are doing SELECT, INSERT,
- * or UPDATE; we just transform the given expressions (the "val" fields).
+ * At this point, we don't care whether we are doing SELECT, UPDATE,
+ * or RETURNING; we just transform the given expressions (the "val" fields).
+ * However, our subroutines care, so we need the exprKind parameter.
*/
List *
-transformTargetList(ParseState *pstate, List *targetlist)
+transformTargetList(ParseState *pstate, List *targetlist,
+ ParseExprKind exprKind)
{
List *p_target = NIL;
ListCell *o_target;
/* It is something.*, expand into multiple items */
p_target = list_concat(p_target,
ExpandIndirectionStar(pstate, ind,
- true));
+ true, exprKind));
continue;
}
}
transformTargetEntry(pstate,
res->val,
NULL,
+ exprKind,
res->name,
false));
}
* decoration. We use this for ROW() and VALUES() constructs.
*/
List *
-transformExpressionList(ParseState *pstate, List *exprlist)
+transformExpressionList(ParseState *pstate, List *exprlist,
+ ParseExprKind exprKind)
{
List *result = NIL;
ListCell *lc;
/* It is something.*, expand into multiple items */
result = list_concat(result,
ExpandIndirectionStar(pstate, ind,
- false));
+ false, exprKind));
continue;
}
}
* Not "something.*", so transform as a single expression
*/
result = lappend(result,
- transformExpr(pstate, e));
+ transformExpr(pstate, e, exprKind));
}
return result;
*
* pstate parse state
* expr expression to be modified
+ * exprKind indicates which type of statement we're dealing with
* colname target column name (ie, name of attribute to be assigned to)
* attrno target attribute number
* indirection subscripts/field names for target column, if any
Expr *
transformAssignedExpr(ParseState *pstate,
Expr *expr,
+ ParseExprKind exprKind,
char *colname,
int attrno,
List *indirection,
int location)
{
+ Relation rd = pstate->p_target_relation;
Oid type_id; /* type of value provided */
Oid attrtype; /* type of target column */
int32 attrtypmod;
Oid attrcollation; /* collation of target column */
- Relation rd = pstate->p_target_relation;
+ ParseExprKind sv_expr_kind;
+
+ /*
+ * Save and restore identity of expression type we're parsing. We must
+ * set p_expr_kind here because we can parse subscripts without going
+ * through transformExpr().
+ */
+ Assert(exprKind != EXPR_KIND_NONE);
+ sv_expr_kind = pstate->p_expr_kind;
+ pstate->p_expr_kind = exprKind;
Assert(rd != NULL);
if (attrno <= 0)
parser_errposition(pstate, exprLocation(orig_expr))));
}
+ pstate->p_expr_kind = sv_expr_kind;
+
return expr;
}
/* Fix up expression as needed */
tle->expr = transformAssignedExpr(pstate,
tle->expr,
+ EXPR_KIND_UPDATE_TARGET,
colname,
attrno,
indirection,
*/
static List *
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
- bool targetlist)
+ bool make_target_entry)
{
List *fields = cref->fields;
int numnames = list_length(fields);
* (e.g., SELECT * FROM emp, dept)
*
* Since the grammar only accepts bare '*' at top level of SELECT, we
- * need not handle the targetlist==false case here.
+ * need not handle the make_target_entry==false case here.
*/
- Assert(targetlist);
+ Assert(make_target_entry);
return ExpandAllTables(pstate, cref->location);
}
else
node = (*pstate->p_pre_columnref_hook) (pstate, cref);
if (node != NULL)
- return ExpandRowReference(pstate, node, targetlist);
+ return ExpandRowReference(pstate, node, make_target_entry);
}
switch (numnames)
errmsg("column reference \"%s\" is ambiguous",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
- return ExpandRowReference(pstate, node, targetlist);
+ return ExpandRowReference(pstate, node, make_target_entry);
}
}
/*
* OK, expand the RTE into fields.
*/
- return ExpandSingleTable(pstate, rte, cref->location, targetlist);
+ return ExpandSingleTable(pstate, rte, cref->location, make_target_entry);
}
}
* The code is shared between the case of foo.* at the top level in a SELECT
* target list (where we want TargetEntry nodes in the result) and foo.* in
* a ROW() or VALUES() construct (where we want just bare expressions).
+ * For robustness, we use a separate "make_target_entry" flag to control
+ * this rather than relying on exprKind.
*/
static List *
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
- bool targetlist)
+ bool make_target_entry, ParseExprKind exprKind)
{
Node *expr;
list_length(ind->indirection) - 1);
/* And transform that */
- expr = transformExpr(pstate, (Node *) ind);
+ expr = transformExpr(pstate, (Node *) ind, exprKind);
/* Expand the rowtype expression into individual fields */
- return ExpandRowReference(pstate, expr, targetlist);
+ return ExpandRowReference(pstate, expr, make_target_entry);
}
/*
*/
static List *
ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
- int location, bool targetlist)
+ int location, bool make_target_entry)
{
int sublevels_up;
int rtindex;
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
- if (targetlist)
+ if (make_target_entry)
{
/* expandRelAttrs handles permissions marking */
return expandRelAttrs(pstate, rte, rtindex, sublevels_up,
*/
static List *
ExpandRowReference(ParseState *pstate, Node *expr,
- bool targetlist)
+ bool make_target_entry)
{
List *result = NIL;
TupleDesc tupleDesc;
RangeTblEntry *rte;
rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
- return ExpandSingleTable(pstate, rte, var->location, targetlist);
+ return ExpandSingleTable(pstate, rte, var->location, make_target_entry);
}
/*
/* save attribute's collation for parse_collate.c */
fselect->resultcollid = att->attcollation;
- if (targetlist)
+ if (make_target_entry)
{
/* add TargetEntry decoration */
TargetEntry *te;
{
stmt->whereClause = transformWhereClause(pstate,
stmt->whereClause,
+ EXPR_KIND_INDEX_PREDICATE,
"WHERE");
/* we have to fix its collations too */
assign_expr_collations(pstate, stmt->whereClause);
ielem->indexcolname = FigureIndexColname(ielem->expr);
/* Now do parse transformation of the expression */
- ielem->expr = transformExpr(pstate, ielem->expr);
+ ielem->expr = transformExpr(pstate, ielem->expr,
+ EXPR_KIND_INDEX_EXPRESSION);
/* We have to fix its collations too */
assign_expr_collations(pstate, ielem->expr);
/*
- * We check only that the result type is legitimate; this is for
- * consistency with what transformWhereClause() checks for the
- * predicate. DefineIndex() will make more checks.
+ * transformExpr() should have already rejected subqueries,
+ * aggregates, and window functions, based on the EXPR_KIND_ for
+ * an index expression.
+ *
+ * Also reject expressions returning sets; this is for consistency
+ * with what transformWhereClause() checks for the predicate.
+ * DefineIndex() will make more checks.
*/
if (expression_returns_set(ielem->expr))
ereport(ERROR,
}
/*
- * Check that only the base rel is mentioned.
+ * Check that only the base rel is mentioned. (This should be dead code
+ * now that add_missing_from is history.)
*/
if (list_length(pstate->p_rtable) != 1)
ereport(ERROR,
/* take care of the where clause */
*whereClause = transformWhereClause(pstate,
(Node *) copyObject(stmt->whereClause),
+ EXPR_KIND_WHERE,
"WHERE");
/* we have to fix its collations too */
assign_expr_collations(pstate, *whereClause);
+ /* this is probably dead code without add_missing_from: */
if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("rule WHERE condition cannot contain references to other relations")));
- /* aggregates not allowed (but subselects are okay) */
- if (pstate->p_hasAggs)
- ereport(ERROR,
- (errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in rule WHERE condition")));
- if (pstate->p_hasWindowFuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("cannot use window function in rule WHERE condition")));
-
/*
* 'instead nothing' rules with a qualification need a query rangetable so
* the rewrite handler can add the negated rule qualification to the
static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
-/*
- * checkExprHasAggs -
- * Check if an expression contains an aggregate function call of the
- * current query level.
- */
-bool
-checkExprHasAggs(Node *node)
-{
- return contain_aggs_of_level(node, 0);
-}
-
/*
* contain_aggs_of_level -
* Check if an expression contains an aggregate function call of a
}
/*
- * checkExprHasWindowFuncs -
+ * contain_windowfuncs -
* Check if an expression contains a window function call of the
* current query level.
*/
bool
-checkExprHasWindowFuncs(Node *node)
+contain_windowfuncs(Node *node)
{
/*
* Must be prepared to start with a Query or a bare expression tree; if
/*
* We had better not have stuck an aggregate into the WHERE clause.
*/
- Assert(!checkExprHasAggs(copy));
+ Assert(!contain_aggs_of_level(copy, 0));
/*
* Make sure query is marked correctly if added qual has sublinks. Need
extern bool contain_var_clause(Node *node);
extern bool contain_vars_of_level(Node *node, int levelsup);
extern int locate_var_of_level(Node *node, int levelsup);
-extern int find_minimum_var_level(Node *node);
extern List *pull_var_clause(Node *node, PVCAggregateBehavior aggbehavior,
PVCPlaceHolderBehavior phbehavior);
extern Node *flatten_join_alias_vars(PlannerInfo *root, Node *node);
WindowDef *windef);
extern void parseCheckAggregates(ParseState *pstate, Query *qry);
-extern void parseCheckWindowFuncs(ParseState *pstate, Query *qry);
extern void build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
extern bool interpretOidsOption(List *defList);
extern Node *transformWhereClause(ParseState *pstate, Node *clause,
- const char *constructName);
+ ParseExprKind exprKind, const char *constructName);
extern Node *transformLimitClause(ParseState *pstate, Node *clause,
- const char *constructName);
+ ParseExprKind exprKind, const char *constructName);
extern List *transformGroupClause(ParseState *pstate, List *grouplist,
List **targetlist, List *sortClause,
- bool useSQL99);
+ ParseExprKind exprKind, bool useSQL99);
extern List *transformSortClause(ParseState *pstate, List *orderlist,
- List **targetlist, bool resolveUnknown, bool useSQL99);
+ List **targetlist, ParseExprKind exprKind,
+ bool resolveUnknown, bool useSQL99);
extern List *transformWindowDefinitions(ParseState *pstate,
List *windowdefs,
/* GUC parameters */
extern bool Transform_null_equals;
-extern Node *transformExpr(ParseState *pstate, Node *expr);
+extern Node *transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind);
+
+extern const char *ParseExprKindName(ParseExprKind exprKind);
#endif /* PARSE_EXPR_H */
#include "utils/relcache.h"
+/*
+ * Expression kinds distinguished by transformExpr(). Many of these are not
+ * semantically distinct so far as expression transformation goes; rather,
+ * we distinguish them so that context-specific error messages can be printed.
+ *
+ * Note: EXPR_KIND_OTHER is not used in the core code, but is left for use
+ * by extension code that might need to call transformExpr(). The core code
+ * will not enforce any context-driven restrictions on EXPR_KIND_OTHER
+ * expressions, so the caller would have to check for sub-selects, aggregates,
+ * and window functions if those need to be disallowed.
+ */
+typedef enum ParseExprKind
+{
+ EXPR_KIND_NONE = 0, /* "not in an expression" */
+ EXPR_KIND_OTHER, /* reserved for extensions */
+ EXPR_KIND_JOIN_ON, /* JOIN ON */
+ EXPR_KIND_JOIN_USING, /* JOIN USING */
+ EXPR_KIND_FROM_SUBSELECT, /* sub-SELECT in FROM clause */
+ EXPR_KIND_FROM_FUNCTION, /* function in FROM clause */
+ EXPR_KIND_WHERE, /* WHERE */
+ EXPR_KIND_HAVING, /* HAVING */
+ EXPR_KIND_WINDOW_PARTITION, /* window definition PARTITION BY */
+ EXPR_KIND_WINDOW_ORDER, /* window definition ORDER BY */
+ EXPR_KIND_WINDOW_FRAME_RANGE, /* window frame clause with RANGE */
+ EXPR_KIND_WINDOW_FRAME_ROWS, /* window frame clause with ROWS */
+ EXPR_KIND_SELECT_TARGET, /* SELECT target list item */
+ EXPR_KIND_INSERT_TARGET, /* INSERT target list item */
+ EXPR_KIND_UPDATE_SOURCE, /* UPDATE assignment source item */
+ EXPR_KIND_UPDATE_TARGET, /* UPDATE assignment target item */
+ EXPR_KIND_GROUP_BY, /* GROUP BY */
+ EXPR_KIND_ORDER_BY, /* ORDER BY */
+ EXPR_KIND_DISTINCT_ON, /* DISTINCT ON */
+ EXPR_KIND_LIMIT, /* LIMIT */
+ EXPR_KIND_OFFSET, /* OFFSET */
+ EXPR_KIND_RETURNING, /* RETURNING */
+ EXPR_KIND_VALUES, /* VALUES */
+ EXPR_KIND_CHECK_CONSTRAINT, /* CHECK constraint for a table */
+ EXPR_KIND_DOMAIN_CHECK, /* CHECK constraint for a domain */
+ EXPR_KIND_COLUMN_DEFAULT, /* default value for a table column */
+ EXPR_KIND_FUNCTION_DEFAULT, /* default parameter value for function */
+ EXPR_KIND_INDEX_EXPRESSION, /* index expression */
+ EXPR_KIND_INDEX_PREDICATE, /* index predicate */
+ EXPR_KIND_ALTER_COL_TRANSFORM, /* transform expr in ALTER COLUMN TYPE */
+ EXPR_KIND_EXECUTE_PARAMETER, /* parameter value in EXECUTE */
+ EXPR_KIND_TRIGGER_WHEN /* WHEN condition in CREATE TRIGGER */
+} ParseExprKind;
+
+
/*
* Function signatures for parser hooks
*/
List *p_future_ctes; /* common table exprs not yet in namespace */
CommonTableExpr *p_parent_cte; /* this query's containing CTE */
List *p_windowdefs; /* raw representations of window clauses */
+ ParseExprKind p_expr_kind; /* what kind of expression we're parsing */
int p_next_resno; /* next targetlist resno to assign */
List *p_locking_clause; /* raw FOR UPDATE/FOR SHARE info */
Node *p_value_substitute; /* what to replace VALUE with, if any */
#include "parser/parse_node.h"
-extern List *transformTargetList(ParseState *pstate, List *targetlist);
-extern List *transformExpressionList(ParseState *pstate, List *exprlist);
+extern List *transformTargetList(ParseState *pstate, List *targetlist,
+ ParseExprKind exprKind);
+extern List *transformExpressionList(ParseState *pstate, List *exprlist,
+ ParseExprKind exprKind);
extern void markTargetListOrigins(ParseState *pstate, List *targetlist);
extern TargetEntry *transformTargetEntry(ParseState *pstate,
- Node *node, Node *expr,
+ Node *node, Node *expr, ParseExprKind exprKind,
char *colname, bool resjunk);
extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
+ ParseExprKind exprKind,
char *colname,
int attrno,
List *indirection,
extern bool contain_aggs_of_level(Node *node, int levelsup);
extern int locate_agg_of_level(Node *node, int levelsup);
+extern bool contain_windowfuncs(Node *node);
extern int locate_windowfunc(Node *node);
-extern bool checkExprHasAggs(Node *node);
-extern bool checkExprHasWindowFuncs(Node *node);
extern bool checkExprHasSubLink(Node *node);
extern Node *replace_rte_variables(Node *node,
group by ten
having exists (select 1 from onek b
where sum(distinct a.four + b.four) = b.four);
-ERROR: aggregates not allowed in WHERE clause
+ERROR: aggregate functions are not allowed in WHERE
LINE 4: where sum(distinct a.four + b.four) = b.four)...
^
-- Test handling of sublinks within outer-level aggregates.
DETAIL: drop cascades to table minmaxtest1
drop cascades to table minmaxtest2
drop cascades to table minmaxtest3
+-- check for correct detection of nested-aggregate errors
+select max(min(unique1)) from tenk1;
+ERROR: aggregate function calls cannot be nested
+LINE 1: select max(min(unique1)) from tenk1;
+ ^
+select (select max(min(unique1)) from int8_tbl) from tenk1;
+ERROR: aggregate function calls cannot be nested
+LINE 1: select (select max(min(unique1)) from int8_tbl) from tenk1;
+ ^
--
-- Test combinations of DISTINCT and/or ORDER BY
--
DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference.
-- LATERAL can be used to put an aggregate into the FROM clause of its query
select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss;
-ERROR: aggregates not allowed in FROM clause
+ERROR: aggregate functions are not allowed in FROM clause of their own query level
LINE 1: select 1 from tenk1 a, lateral (select max(a.unique1) from i...
^
-- can't order by another window function
SELECT rank() OVER (ORDER BY rank() OVER (ORDER BY random()));
-ERROR: window functions not allowed in window definition
+ERROR: window functions are not allowed in window definitions
LINE 1: SELECT rank() OVER (ORDER BY rank() OVER (ORDER BY random())...
^
-- some other errors
SELECT * FROM empsalary WHERE row_number() OVER (ORDER BY salary) < 10;
-ERROR: window functions not allowed in WHERE clause
+ERROR: window functions are not allowed in WHERE
LINE 1: SELECT * FROM empsalary WHERE row_number() OVER (ORDER BY sa...
^
SELECT * FROM empsalary INNER JOIN tenk1 ON row_number() OVER (ORDER BY salary) < 10;
-ERROR: window functions not allowed in JOIN conditions
+ERROR: window functions are not allowed in JOIN conditions
LINE 1: SELECT * FROM empsalary INNER JOIN tenk1 ON row_number() OVE...
^
SELECT rank() OVER (ORDER BY 1), count(*) FROM empsalary GROUP BY 1;
-ERROR: window functions not allowed in GROUP BY clause
+ERROR: window functions are not allowed in GROUP BY
LINE 1: SELECT rank() OVER (ORDER BY 1), count(*) FROM empsalary GRO...
^
SELECT * FROM rank() OVER (ORDER BY random());
-ERROR: cannot use window function in function expression in FROM
+ERROR: window functions are not allowed in functions in FROM
LINE 1: SELECT * FROM rank() OVER (ORDER BY random());
^
DELETE FROM empsalary WHERE (rank() OVER (ORDER BY random())) > 10;
-ERROR: window functions not allowed in WHERE clause
+ERROR: window functions are not allowed in WHERE
LINE 1: DELETE FROM empsalary WHERE (rank() OVER (ORDER BY random())...
^
DELETE FROM empsalary RETURNING rank() OVER (ORDER BY random());
-ERROR: cannot use window function in RETURNING
+ERROR: window functions are not allowed in RETURNING
LINE 1: DELETE FROM empsalary RETURNING rank() OVER (ORDER BY random...
^
SELECT count(*) OVER w FROM tenk1 WINDOW w AS (ORDER BY unique1), w AS (ORDER BY unique1);
-- aggregate functions
WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x)
SELECT * FROM x;
-ERROR: aggregate functions not allowed in a recursive query's recursive term
+ERROR: aggregate functions are not allowed in a recursive query's recursive term
LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) F...
^
WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x)
SELECT * FROM x;
-ERROR: aggregate functions not allowed in a recursive query's recursive term
+ERROR: aggregate functions are not allowed in a recursive query's recursive term
LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FRO...
^
-- ORDER BY
drop table minmaxtest cascade;
+-- check for correct detection of nested-aggregate errors
+select max(min(unique1)) from tenk1;
+select (select max(min(unique1)) from int8_tbl) from tenk1;
+
--
-- Test combinations of DISTINCT and/or ORDER BY
--