diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 138 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 540 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 202 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 274 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 4 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 77 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 29 |
7 files changed, 829 insertions, 435 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; } diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 5854f81005d..d1d835b8007 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -20,6 +20,7 @@ #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" @@ -28,6 +29,14 @@ typedef struct { ParseState *pstate; + int min_varlevel; + int min_agglevel; + int sublevels_up; +} check_agg_arguments_context; + +typedef struct +{ + ParseState *pstate; Query *qry; List *groupClauses; bool have_non_var_grouping; @@ -35,6 +44,9 @@ typedef struct 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); @@ -72,6 +84,8 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, 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 @@ -102,6 +116,7 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, torder = transformSortClause(pstate, aggorder, &tlist, + EXPR_KIND_ORDER_BY, true /* fix unknowns */ , true /* force SQL99 rules */ ); @@ -142,55 +157,261 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, 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); } /* @@ -208,12 +429,15 @@ void 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"), @@ -221,6 +445,121 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, 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 * properties of the OVER clause, and make a new entry in the p_windowdefs @@ -294,11 +633,14 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, /* * 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) @@ -331,31 +673,8 @@ 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) { @@ -365,12 +684,6 @@ parseCheckAggregates(ParseState *pstate, Query *qry) 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); } @@ -438,97 +751,12 @@ parseCheckAggregates(ParseState *pstate, Query *qry) 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 * that are not listed in the groupClauses list and are not within diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d354baf42fa..ee40b5547ee 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -41,17 +41,6 @@ /* 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); @@ -81,9 +70,9 @@ static void setNamespaceLateralState(List *namespace, 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, @@ -371,7 +360,7 @@ transformJoinUsingClause(ParseState *pstate, * 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"); @@ -401,7 +390,8 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, List *namespace) 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; @@ -458,6 +448,14 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) 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 * don't need save/restore logic here.) @@ -471,7 +469,9 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) 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 @@ -524,7 +524,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) /* * Transform the raw expression. */ - funcexpr = transformExpr(pstate, r->funccallnode); + funcexpr = transformExpr(pstate, r->funccallnode, EXPR_KIND_FROM_FUNCTION); pstate->p_lateral_active = false; @@ -534,25 +534,6 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) 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. */ rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, @@ -1182,14 +1163,14 @@ setNamespaceLateralState(List *namespace, bool lateral_only, bool lateral_ok) */ 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); @@ -1209,18 +1190,18 @@ transformWhereClause(ParseState *pstate, Node *clause, */ 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; @@ -1229,7 +1210,7 @@ transformLimitClause(ParseState *pstate, Node *clause, /* * 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 @@ -1251,31 +1232,57 @@ checkExprIsVarFree(ParseState *pstate, Node *n, const char *constructName) 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. @@ -1291,11 +1298,11 @@ checkExprIsVarFree(ParseState *pstate, Node *n, const char *constructName) * * 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; @@ -1344,7 +1351,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, 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 @@ -1386,7 +1393,8 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, /*------ 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 @@ -1395,7 +1403,11 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, } } 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)) @@ -1410,7 +1422,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, (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); @@ -1421,21 +1433,25 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, 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); } /* @@ -1449,9 +1465,11 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, * * 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; @@ -1464,7 +1482,7 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist) * resjunk target here, though the SQL92 cases above must ignore resjunk * targets. */ - expr = transformExpr(pstate, node); + expr = transformExpr(pstate, node, exprKind); foreach(tl, *tlist) { @@ -1491,7 +1509,8 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **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); @@ -1511,7 +1530,7 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist) List * transformGroupClause(ParseState *pstate, List *grouplist, List **targetlist, List *sortClause, - bool useSQL99) + ParseExprKind exprKind, bool useSQL99) { List *result = NIL; ListCell *gl; @@ -1523,10 +1542,11 @@ transformGroupClause(ParseState *pstate, List *grouplist, 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)) @@ -1588,6 +1608,7 @@ List * transformSortClause(ParseState *pstate, List *orderlist, List **targetlist, + ParseExprKind exprKind, bool resolveUnknown, bool useSQL99) { @@ -1600,10 +1621,11 @@ transformSortClause(ParseState *pstate, 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, @@ -1668,12 +1690,14 @@ transformWindowDefinitions(ParseState *pstate, 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 */ ); /* @@ -1861,7 +1885,7 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, TargetEntry *tle; tle = findTargetlistEntrySQL92(pstate, dexpr, targetlist, - DISTINCT_ON_CLAUSE); + EXPR_KIND_DISTINCT_ON); sortgroupref = assignSortGroupRef(tle, *targetlist); sortgrouprefs = lappend_int(sortgrouprefs, sortgroupref); } @@ -2270,11 +2294,11 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause) 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 */ @@ -2283,6 +2307,9 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause) } 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 @@ -2292,9 +2319,12 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause) 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; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 385f8e767e4..e9267c56fc5 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -37,6 +37,7 @@ 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); @@ -100,9 +101,27 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname, * 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; @@ -133,7 +152,7 @@ transformExpr(ParseState *pstate, Node *expr) { A_Indirection *ind = (A_Indirection *) expr; - result = transformExpr(pstate, ind->arg); + result = transformExprRecurse(pstate, ind->arg); result = transformIndirection(pstate, result, ind->indirection); break; @@ -230,6 +249,8 @@ transformExpr(ParseState *pstate, Node *expr) break; default: elog(ERROR, "unrecognized A_Expr kind: %d", a->kind); + result = NULL; /* keep compiler quiet */ + break; } break; } @@ -242,7 +263,7 @@ transformExpr(ParseState *pstate, Node *expr) { NamedArgExpr *na = (NamedArgExpr *) expr; - na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg); + na->arg = (Expr *) transformExprRecurse(pstate, (Node *) na->arg); result = expr; break; } @@ -279,7 +300,7 @@ transformExpr(ParseState *pstate, Node *expr) { 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; @@ -334,6 +355,7 @@ transformExpr(ParseState *pstate, Node *expr) default: /* should not reach here */ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); + result = NULL; /* keep compiler quiet */ break; } @@ -843,7 +865,7 @@ transformAExprOp(ParseState *pstate, A_Expr *a) 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) && @@ -860,14 +882,14 @@ transformAExprOp(ParseState *pstate, A_Expr *a) 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)); @@ -880,8 +902,8 @@ transformAExprOp(ParseState *pstate, A_Expr *a) 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, @@ -896,8 +918,8 @@ transformAExprOp(ParseState *pstate, A_Expr *a) 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"); @@ -910,8 +932,8 @@ transformAExprAnd(ParseState *pstate, A_Expr *a) 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"); @@ -924,7 +946,7 @@ transformAExprOr(ParseState *pstate, A_Expr *a) 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"); @@ -936,8 +958,8 @@ transformAExprNot(ParseState *pstate, A_Expr *a) 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, @@ -950,8 +972,8 @@ transformAExprOpAny(ParseState *pstate, A_Expr *a) 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, @@ -964,8 +986,8 @@ transformAExprOpAll(ParseState *pstate, A_Expr *a) 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)) @@ -990,8 +1012,8 @@ transformAExprDistinct(ParseState *pstate, A_Expr *a) 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, @@ -1029,7 +1051,7 @@ transformAExprOf(ParseState *pstate, A_Expr *a) * 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, @@ -1092,12 +1114,12 @@ transformAExprIn(ParseState *pstate, A_Expr *a) * 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); @@ -1222,8 +1244,8 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) 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 */ @@ -1258,7 +1280,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) 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) @@ -1311,14 +1333,14 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) 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); @@ -1337,7 +1359,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) 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 @@ -1380,12 +1402,92 @@ transformSubLink(ParseState *pstate, SubLink *sublink) { 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); /* @@ -1450,7 +1552,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) /* * 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 @@ -1557,7 +1659,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, } else { - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); /* * Check for sub-array expressions, if we haven't already found @@ -1679,7 +1781,7 @@ transformRowExpr(ParseState *pstate, RowExpr *r) 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; @@ -1712,7 +1814,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) Node *e = (Node *) lfirst(args); Node *newe; - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); newargs = lappend(newargs, newe); } @@ -1751,7 +1853,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) Node *e = (Node *) lfirst(args); Node *newe; - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); newargs = lappend(newargs, newe); } @@ -1805,7 +1907,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) 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); @@ -1851,7 +1953,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) Node *e = (Node *) lfirst(lc); Node *newe; - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); switch (x->op) { case IS_XMLCONCAT: @@ -1914,7 +2016,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs) 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")); @@ -1977,7 +2079,7 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b) 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, @@ -2082,7 +2184,7 @@ static Node * 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; @@ -2130,7 +2232,7 @@ transformCollateClause(ParseState *pstate, CollateClause *c) Oid argtype; newc = makeNode(CollateExpr); - newc->arg = (Expr *) transformExpr(pstate, c->arg); + newc->arg = (Expr *) transformExprRecurse(pstate, c->arg); argtype = exprType((Node *) newc->arg); @@ -2433,3 +2535,87 @@ make_distinct_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, 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"; +} diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 80dbdd19e48..6aaeae76b0a 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -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), diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index ccd97fc845d..053b3a0dad1 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -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; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index c22c6ed21f7..accda01f457 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -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 |