aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2012-08-10 11:35:33 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2012-08-10 11:36:15 -0400
commiteaccfded98a9c677d3a2e849c1747ec90e8596a6 (patch)
treec341448f69a2e67484de7a0a43e12848c20eb414 /src/backend/parser/analyze.c
parentb3055ab4fb5839a872bfe354b2b5ac31e6903ed6 (diff)
downloadpostgresql-eaccfded98a9c677d3a2e849c1747ec90e8596a6.tar.gz
postgresql-eaccfded98a9c677d3a2e849c1747ec90e8596a6.zip
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.)
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c138
1 files changed, 33 insertions, 105 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 93ef724ffff..6c3d89a14f6 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -368,7 +368,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
*/
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);
@@ -378,8 +379,6 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
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);
@@ -597,7 +596,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
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*
@@ -680,16 +679,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
}
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;
@@ -698,7 +692,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* 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,
@@ -758,19 +753,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
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);
@@ -845,6 +827,7 @@ transformInsertRow(ParseState *pstate, List *exprlist,
Assert(IsA(col, ResTarget));
expr = transformAssignedExpr(pstate, expr,
+ EXPR_KIND_INSERT_TARGET,
col->name,
lfirst_int(attnos),
col->indirection,
@@ -945,19 +928,19 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
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
@@ -968,6 +951,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
&qry->targetList,
+ EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
false /* allow SQL92 rules */ );
@@ -975,6 +959,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
stmt->groupClause,
&qry->targetList,
qry->sortClause,
+ EXPR_KIND_GROUP_BY,
false /* allow SQL92 rules */ );
if (stmt->distinctClause == NIL)
@@ -1003,9 +988,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
/* 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,
@@ -1017,8 +1002,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
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);
@@ -1090,7 +1073,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
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
@@ -1217,13 +1200,14 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
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,
@@ -1249,19 +1233,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
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);
@@ -1460,6 +1431,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
qry->sortClause = transformSortClause(pstate,
sortClause,
&qry->targetList,
+ EXPR_KIND_ORDER_BY,
false /* no unknowns expected */ ,
false /* allow SQL92 rules */ );
@@ -1477,17 +1449,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
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);
@@ -1937,9 +1907,11 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
*/
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);
@@ -1949,24 +1921,6 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
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.
*/
@@ -2040,8 +1994,6 @@ transformReturningList(ParseState *pstate, List *returningList)
{
List *rlist;
int save_next_resno;
- bool save_hasAggs;
- bool save_hasWindowFuncs;
if (returningList == NIL)
return NIL; /* nothing to do */
@@ -2054,38 +2006,14 @@ transformReturningList(ParseState *pstate, List *returningList)
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;
}