mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
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.)
This commit is contained in:
@ -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);
|
||||
|
||||
@ -1948,24 +1920,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;
|
||||
}
|
||||
|
@ -20,11 +20,20 @@
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "parser/parse_clause.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ParseState *pstate;
|
||||
int min_varlevel;
|
||||
int min_agglevel;
|
||||
int sublevels_up;
|
||||
} check_agg_arguments_context;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ParseState *pstate;
|
||||
@ -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);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (min_varlevel == 0)
|
||||
{
|
||||
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))));
|
||||
}
|
||||
|
||||
/* It can't contain window functions either */
|
||||
if (pstate->p_hasWindowFuncs &&
|
||||
checkExprHasWindowFuncs((Node *) agg->args))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("aggregate function calls cannot contain window function calls"),
|
||||
parser_errposition(pstate,
|
||||
locate_windowfunc((Node *) agg->args))));
|
||||
|
||||
if (min_varlevel < 0)
|
||||
min_varlevel = 0;
|
||||
min_varlevel = check_agg_arguments(pstate, agg->args);
|
||||
agg->agglevelsup = min_varlevel;
|
||||
|
||||
/* Mark the correct pstate as having aggregates */
|
||||
/* Mark the correct pstate level as having aggregates */
|
||||
while (min_varlevel-- > 0)
|
||||
pstate = pstate->parentParseState;
|
||||
pstate->p_hasAggs = true;
|
||||
|
||||
/*
|
||||
* Complain if we are inside a LATERAL subquery of the aggregation query.
|
||||
* We must be in its FROM clause, so the aggregate is misplaced.
|
||||
* 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 (pstate->p_lateral_active)
|
||||
err = NULL;
|
||||
errkind = false;
|
||||
switch (pstate->p_expr_kind)
|
||||
{
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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("aggregates not allowed in FROM clause"),
|
||||
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)));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* If there's a nested aggregate of the same semantic level, complain.
|
||||
*/
|
||||
if (agglevel == context.min_agglevel)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
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,18 +429,136 @@ 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"),
|
||||
parser_errposition(pstate,
|
||||
locate_windowfunc((Node *) wfunc->args))));
|
||||
|
||||
/*
|
||||
* Check to see if the window function is in an invalid place within the
|
||||
* query.
|
||||
*
|
||||
* For brevity we support two schemes for reporting an error here: set
|
||||
* "err" to a custom message, or set "errkind" true if the error context
|
||||
* is sufficiently identified by what ParseExprKindName will return, *and*
|
||||
* what it will return is just a SQL keyword. (Otherwise, use a custom
|
||||
* message to avoid creating translation problems.)
|
||||
*/
|
||||
err = NULL;
|
||||
errkind = false;
|
||||
switch (pstate->p_expr_kind)
|
||||
{
|
||||
case EXPR_KIND_NONE:
|
||||
Assert(false); /* can't happen */
|
||||
break;
|
||||
case EXPR_KIND_OTHER:
|
||||
/* Accept window func here; caller must throw error if wanted */
|
||||
break;
|
||||
case EXPR_KIND_JOIN_ON:
|
||||
case EXPR_KIND_JOIN_USING:
|
||||
err = _("window functions are not allowed in JOIN conditions");
|
||||
break;
|
||||
case EXPR_KIND_FROM_SUBSELECT:
|
||||
/* can't get here, but just in case, throw an error */
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_FROM_FUNCTION:
|
||||
err = _("window functions are not allowed in functions in FROM");
|
||||
break;
|
||||
case EXPR_KIND_WHERE:
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_HAVING:
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_WINDOW_PARTITION:
|
||||
case EXPR_KIND_WINDOW_ORDER:
|
||||
case EXPR_KIND_WINDOW_FRAME_RANGE:
|
||||
case EXPR_KIND_WINDOW_FRAME_ROWS:
|
||||
err = _("window functions are not allowed in window definitions");
|
||||
break;
|
||||
case EXPR_KIND_SELECT_TARGET:
|
||||
/* okay */
|
||||
break;
|
||||
case EXPR_KIND_INSERT_TARGET:
|
||||
case EXPR_KIND_UPDATE_SOURCE:
|
||||
case EXPR_KIND_UPDATE_TARGET:
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_GROUP_BY:
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_ORDER_BY:
|
||||
/* okay */
|
||||
break;
|
||||
case EXPR_KIND_DISTINCT_ON:
|
||||
/* okay */
|
||||
break;
|
||||
case EXPR_KIND_LIMIT:
|
||||
case EXPR_KIND_OFFSET:
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_RETURNING:
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_VALUES:
|
||||
errkind = true;
|
||||
break;
|
||||
case EXPR_KIND_CHECK_CONSTRAINT:
|
||||
case EXPR_KIND_DOMAIN_CHECK:
|
||||
err = _("window functions are not allowed in CHECK constraints");
|
||||
break;
|
||||
case EXPR_KIND_COLUMN_DEFAULT:
|
||||
case EXPR_KIND_FUNCTION_DEFAULT:
|
||||
err = _("window functions are not allowed in DEFAULT expressions");
|
||||
break;
|
||||
case EXPR_KIND_INDEX_EXPRESSION:
|
||||
err = _("window functions are not allowed in index expressions");
|
||||
break;
|
||||
case EXPR_KIND_INDEX_PREDICATE:
|
||||
err = _("window functions are not allowed in index predicates");
|
||||
break;
|
||||
case EXPR_KIND_ALTER_COL_TRANSFORM:
|
||||
err = _("window functions are not allowed in transform expressions");
|
||||
break;
|
||||
case EXPR_KIND_EXECUTE_PARAMETER:
|
||||
err = _("window functions are not allowed in EXECUTE parameters");
|
||||
break;
|
||||
case EXPR_KIND_TRIGGER_WHEN:
|
||||
err = _("window functions are not allowed in trigger WHEN conditions");
|
||||
break;
|
||||
|
||||
/*
|
||||
* There is intentionally no default: case here, so that the
|
||||
* compiler will warn if we add a new ParseExprKind without
|
||||
* extending this switch. If we do see an unrecognized value at
|
||||
* runtime, the behavior will be the same as for EXPR_KIND_OTHER,
|
||||
* which is sane anyway.
|
||||
*/
|
||||
}
|
||||
if (err)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WINDOWING_ERROR),
|
||||
errmsg_internal("%s", err),
|
||||
parser_errposition(pstate, wfunc->location)));
|
||||
if (errkind)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WINDOWING_ERROR),
|
||||
/* translator: %s is name of a SQL construct, eg GROUP BY */
|
||||
errmsg("window functions are not allowed in %s",
|
||||
ParseExprKindName(pstate->p_expr_kind)),
|
||||
parser_errposition(pstate, wfunc->location)));
|
||||
|
||||
/*
|
||||
* If the OVER clause just specifies a window name, find that WINDOW
|
||||
* clause (which had better be present). Otherwise, try to match all the
|
||||
@ -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,96 +751,11 @@ 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
|
||||
|
@ -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;
|
||||
|
||||
@ -457,6 +447,14 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
if (r->alias == NULL)
|
||||
elog(ERROR, "subquery in FROM must have an alias");
|
||||
|
||||
/*
|
||||
* Set p_expr_kind to show this parse level is recursing to a subselect.
|
||||
* We can't be nested within any expression, so don't need save-restore
|
||||
* logic here.
|
||||
*/
|
||||
Assert(pstate->p_expr_kind == EXPR_KIND_NONE);
|
||||
pstate->p_expr_kind = EXPR_KIND_FROM_SUBSELECT;
|
||||
|
||||
/*
|
||||
* If the subselect is LATERAL, make lateral_only names of this level
|
||||
* visible to it. (LATERAL can't nest within a single pstate level, so we
|
||||
@ -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;
|
||||
|
||||
@ -533,25 +533,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.
|
||||
*/
|
||||
@ -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))
|
||||
{
|
||||
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))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user