Commit eaccfded authored by Tom Lane's avatar Tom Lane

Centralize the logic for detecting misplaced aggregates, window funcs, etc.

Formerly we relied on checking after-the-fact to see if an expression
contained aggregates, window functions, or sub-selects when it shouldn't.
This is grotty, easily forgotten (indeed, we had forgotten to teach
DefineIndex about rejecting window functions), and none too efficient
since it requires extra traversals of the parse tree.  To improve matters,
define an enum type that classifies all SQL sub-expressions, store it in
ParseState to show what kind of expression we are currently parsing, and
make transformAggregateCall, transformWindowFuncCall, and transformSubLink
check the expression type and throw error if the type indicates the
construct is disallowed.  This allows removal of a large number of ad-hoc
checks scattered around the code base.  The enum type is sufficiently
fine-grained that we can still produce error messages of at least the
same specificity as before.

Bringing these error checks together revealed that we'd been none too
consistent about phrasing of the error messages, so standardize the wording
a bit.

Also, rewrite checking of aggregate arguments so that it requires only one
traversal of the arguments, rather than up to three as before.

In passing, clean up some more comments left over from add_missing_from
support, and annotate some tests that I think are dead code now that that's
gone.  (I didn't risk actually removing said dead code, though.)
parent b3055ab4
......@@ -2415,10 +2415,11 @@ cookDefault(ParseState *pstate,
/*
* 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,
......@@ -2426,6 +2427,9 @@ cookDefault(ParseState *pstate,
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))
......@@ -2433,22 +2437,6 @@ cookDefault(ParseState *pstate,
(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 ---
......@@ -2499,7 +2487,7 @@ cookConstraint(ParseState *pstate,
/*
* 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.
......@@ -2512,7 +2500,8 @@ cookConstraint(ParseState *pstate,
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,
......@@ -2520,22 +2509,6 @@ cookConstraint(ParseState *pstate,
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;
}
......
......@@ -344,12 +344,14 @@ examine_parameter_list(List *parameters, Oid languageOid,
(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))
......@@ -358,28 +360,18 @@ examine_parameter_list(List *parameters, Oid languageOid,
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;
......
......@@ -941,17 +941,9 @@ static void
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
......@@ -1072,18 +1064,10 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
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,
......
......@@ -354,21 +354,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
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);
......
......@@ -7178,27 +7178,14 @@ ATPrepAlterColumnType(List **wqueue,
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
{
......
......@@ -286,26 +286,11 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
/* 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.
*
......
......@@ -2871,7 +2871,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
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.
......@@ -2884,38 +2884,15 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
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.
*/
......
......@@ -596,7 +596,7 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
bool
contain_window_function(Node *clause)
{
return checkExprHasWindowFuncs(clause);
return contain_windowfuncs(clause);
}
/*
......
......@@ -52,12 +52,6 @@ typedef struct
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;
......@@ -81,8 +75,6 @@ static bool contain_var_clause_walker(Node *node, void *context);
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,
......@@ -488,159 +480,6 @@ locate_var_of_level_walker(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.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -328,7 +328,7 @@ transformArraySubscripts(ParseState *pstate,
{
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),
......@@ -355,7 +355,7 @@ transformArraySubscripts(ParseState *pstate,
}
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),
......
......@@ -57,14 +57,14 @@ static Node *transformAssignmentSubscripts(ParseState *pstate,
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);
......@@ -76,6 +76,7 @@ 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.
......@@ -84,12 +85,13 @@ TargetEntry *
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)
{
......@@ -111,11 +113,13 @@ transformTargetEntry(ParseState *pstate,
* 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;
......@@ -151,7 +155,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
/* It is something.*, expand into multiple items */
p_target = list_concat(p_target,
ExpandIndirectionStar(pstate, ind,
true));
true, exprKind));
continue;
}
}
......@@ -163,6 +167,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
transformTargetEntry(pstate,
res->val,
NULL,
exprKind,
res->name,
false));
}
......@@ -180,7 +185,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
* 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;
......@@ -216,7 +222,7 @@ transformExpressionList(ParseState *pstate, List *exprlist)
/* It is something.*, expand into multiple items */
result = list_concat(result,
ExpandIndirectionStar(pstate, ind,
false));
false, exprKind));
continue;
}
}
......@@ -225,7 +231,7 @@ transformExpressionList(ParseState *pstate, List *exprlist)
* Not "something.*", so transform as a single expression
*/
result = lappend(result,
transformExpr(pstate, e));
transformExpr(pstate, e, exprKind));
}
return result;
......@@ -350,6 +356,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
*
* 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
......@@ -365,16 +372,27 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
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)
......@@ -491,6 +509,8 @@ transformAssignedExpr(ParseState *pstate,
parser_errposition(pstate, exprLocation(orig_expr))));
}
pstate->p_expr_kind = sv_expr_kind;
return expr;
}
......@@ -521,6 +541,7 @@ updateTargetListEntry(ParseState *pstate,
/* Fix up expression as needed */
tle->expr = transformAssignedExpr(pstate,
tle->expr,
EXPR_KIND_UPDATE_TARGET,
colname,
attrno,
indirection,
......@@ -947,7 +968,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
*/
static List *
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
bool targetlist)
bool make_target_entry)
{
List *fields = cref->fields;
int numnames = list_length(fields);
......@@ -960,9 +981,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
* (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
......@@ -1002,7 +1023,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
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)
......@@ -1065,7 +1086,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
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);
}
}
......@@ -1100,7 +1121,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
/*
* OK, expand the RTE into fields.
*/
return ExpandSingleTable(pstate, rte, cref->location, targetlist);
return ExpandSingleTable(pstate, rte, cref->location, make_target_entry);
}
}
......@@ -1166,10 +1187,12 @@ ExpandAllTables(ParseState *pstate, int location)
* 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;
......@@ -1179,10 +1202,10 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
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);
}
/*
......@@ -1196,14 +1219,14 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
*/
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,
......@@ -1245,7 +1268,7 @@ ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
*/
static List *
ExpandRowReference(ParseState *pstate, Node *expr,
bool targetlist)
bool make_target_entry)
{
List *result = NIL;
TupleDesc tupleDesc;
......@@ -1268,7 +1291,7 @@ ExpandRowReference(ParseState *pstate, Node *expr,
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);
}
/*
......@@ -1313,7 +1336,7 @@ ExpandRowReference(ParseState *pstate, Node *expr,
/* save attribute's collation for parse_collate.c */
fselect->resultcollid = att->attcollation;
if (targetlist)
if (make_target_entry)
{
/* add TargetEntry decoration */
TargetEntry *te;
......
......@@ -1917,6 +1917,7 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
{
stmt->whereClause = transformWhereClause(pstate,
stmt->whereClause,
EXPR_KIND_INDEX_PREDICATE,
"WHERE");
/* we have to fix its collations too */
assign_expr_collations(pstate, stmt->whereClause);
......@@ -1934,15 +1935,20 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
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,
......@@ -1952,7 +1958,8 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
}
/*
* 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,
......@@ -2047,25 +2054,17 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
/* 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
......
......@@ -52,17 +52,6 @@ static Relids offset_relid_set(Relids relids, int offset);
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
......@@ -185,12 +174,12 @@ locate_agg_of_level_walker(Node *node,
}
/*
* 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
......@@ -1049,7 +1038,7 @@ AddQual(Query *parsetree, Node *qual)
/*
* 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
......
......@@ -37,7 +37,6 @@ extern List *pull_vars_of_level(Node *node, int levelsup);
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);
......
......@@ -22,7 +22,6 @@ extern void transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
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,
......
......@@ -23,14 +23,15 @@ extern bool interpretInhOption(InhOption inhOpt);
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,
......
......@@ -18,6 +18,8 @@
/* 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 */
......@@ -18,6 +18,54 @@
#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
*/
......@@ -93,6 +141,7 @@ struct ParseState
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 */
......
......@@ -17,13 +17,16 @@
#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,
......
......@@ -52,9 +52,8 @@ extern void AddInvertedQual(Query *parsetree, Node *qual);
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,
......
......@@ -292,7 +292,7 @@ select ten, sum(distinct four) from onek a
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.
......@@ -745,6 +745,15 @@ NOTICE: drop cascades to 3 other objects
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
--
......
......@@ -3143,6 +3143,6 @@ LINE 1: ...m int4_tbl a full join lateral generate_series(0, a.f1) g on...
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...
^
......@@ -958,32 +958,32 @@ SELECT rank() OVER (ORDER BY length('abc'));
-- 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);
......
......@@ -937,12 +937,12 @@ LINE 2: WHERE n IN (SELECT * FROM x))
-- 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
......
......@@ -280,6 +280,10 @@ select min(f1), max(f1) from minmaxtest;
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
--
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment