From 41a3f7593c13f3c5e9b712a036d5365875eca4c1 Mon Sep 17 00:00:00 2001 From: jian he Date: Thu, 1 Oct 2026 15:02:37 +0800 Subject: [PATCH v54 1/1] Remove define_walker and validate navigation at ParseRPRNavCall Check PREV/NEXT/FIRST/LAST navigation in ParseRPRNavCall, where the arguments are already transformed, instead of in a separate walk over the finished DEFINE expression. This is way more neat than define_walker. --- src/backend/executor/README.rpr | 4 - src/backend/parser/parse_func.c | 268 +++++++++++++++++-------- src/backend/parser/parse_rpr.c | 242 ---------------------- src/test/regress/expected/rpr.out | 58 +++--- src/test/regress/expected/rpr_base.out | 77 ++++--- src/tools/pgindent/typedefs.list | 2 - 6 files changed, 250 insertions(+), 401 deletions(-) diff --git a/src/backend/executor/README.rpr b/src/backend/executor/README.rpr index 05bbcd76240..a852f4e077f 100644 --- a/src/backend/executor/README.rpr +++ b/src/backend/executor/README.rpr @@ -190,10 +190,6 @@ use. After that it processes each DEFINE variable as follows: present in the query targetlist, so the planner propagates the referenced columns through the plan tree -After all variables are processed: - (5) Validates navigation nesting and offsets (define_walker), marks - column origins and assigns collations - Variables that are used in PATTERN but not defined in DEFINE are implicitly evaluated as TRUE (matching all rows). diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 4f8bfde6f96..5d55705f5c0 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -22,6 +22,7 @@ #include "lib/stringinfo.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/optimizer.h" #include "parser/parse_agg.h" #include "parser/parse_clause.h" #include "parser/parse_coerce.h" @@ -48,6 +49,9 @@ static void unify_hypothetical_args(ParseState *pstate, List *fargs, int numAggregatedArgs, Oid *actual_arg_types, Oid *declared_arg_types); static Oid FuncNameAsType(List *funcname); +static const char *rpr_nav_lookup(const char *name, RPRNavKind *kind); +static const char *rpr_nav_kind_form(RPRNavKind kind); +static bool find_rpr_nav_walker(Node *node, RPRNavExpr **found); static Node *ParseRPRNavCall(ParseState *pstate, List *funcname, List *fargs, List *argnames, FuncCall *fn, int location); @@ -232,16 +236,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, */ if (!is_column && !proc_call && pstate->p_expr_kind == EXPR_KIND_RPR_DEFINE && - list_length(funcname) == 1) - { - const char *name = strVal(linitial(funcname)); - - if (strcmp(name, "prev") == 0 || - strcmp(name, "next") == 0 || - strcmp(name, "first") == 0 || - strcmp(name, "last") == 0) - could_be_rpr_nav = true; - } + list_length(funcname) == 1 && + rpr_nav_lookup(strVal(linitial(funcname)), NULL) != NULL) + could_be_rpr_nav = true; /* * Decide whether it's legitimate to consider the construct to be a column @@ -2139,62 +2136,120 @@ FuncNameAsType(List *funcname) return result; } +/* + * If name (as downcased by the parser) is a row pattern navigation function, + * return its upper-case spelling for messages and set *kind if kind is not + * NULL; otherwise return NULL. + */ +static const char * +rpr_nav_lookup(const char *name, RPRNavKind *kind) +{ + RPRNavKind k; + const char *navname; + + if (strcmp(name, "prev") == 0) + { + k = RPR_NAV_PREV; + navname = "PREV"; + } + else if (strcmp(name, "next") == 0) + { + k = RPR_NAV_NEXT; + navname = "NEXT"; + } + else if (strcmp(name, "first") == 0) + { + k = RPR_NAV_FIRST; + navname = "FIRST"; + } + else if (strcmp(name, "last") == 0) + { + k = RPR_NAV_LAST; + navname = "LAST"; + } + else + return NULL; + + if (kind) + *kind = k; + return navname; +} + +/* Return how a navigation call of the given kind is spelled in messages. */ +static const char * +rpr_nav_kind_form(RPRNavKind kind) +{ + switch (kind) + { + case RPR_NAV_PREV: + return "PREV()"; + case RPR_NAV_NEXT: + return "NEXT()"; + case RPR_NAV_FIRST: + return "FIRST()"; + case RPR_NAV_LAST: + return "LAST()"; + case RPR_NAV_PREV_FIRST: + return "PREV(FIRST())"; + case RPR_NAV_PREV_LAST: + return "PREV(LAST())"; + case RPR_NAV_NEXT_FIRST: + return "NEXT(FIRST())"; + case RPR_NAV_NEXT_LAST: + return "NEXT(LAST())"; + } + return "???"; /* keep compiler quiet */ +} + +/* Find the first RPRNavExpr in an expression tree, if any. */ +static bool +find_rpr_nav_walker(Node *node, RPRNavExpr **found) +{ + if (node == NULL) + return false; + if (IsA(node, RPRNavExpr)) + { + *found = (RPRNavExpr *) node; + return true; + } + return expression_tree_walker(node, find_rpr_nav_walker, found); +} + /* * ParseRPRNavCall * Recognize a row pattern navigation operation in a DEFINE clause. * + * The argument expressions (in fargs) must have been transformed already. + * However, nothing in *fn has been transformed. + * * Inside an EXPR_KIND_RPR_DEFINE clause an unqualified call to one of the * names PREV/NEXT/FIRST/LAST denotes the corresponding row pattern navigation * operation (ISO/IEC 19075-5 Subclause 5.6), not an ordinary function call. - * The name is matched here, before any catalog lookup, with no fallback to - * function resolution: once it matches, decoration and argument-count - * violations are dedicated errors rather than letting an ordinary function of - * the same name take over. A schema-qualified call (the caller restricts us - * to unqualified names) is the documented way to reach such a function - * instead. * * The caller routes here only after the name has matched one of the four * navigation names and the common decoration/wrong-kind checks in - * ParseFuncOrColumn have run, so this always returns an RPRNavExpr. + * ParseFuncOrColumn have run. */ static Node * ParseRPRNavCall(ParseState *pstate, List *funcname, List *fargs, List *argnames, FuncCall *fn, int location) { - const char *name = strVal(linitial(funcname)); RPRNavKind kind; const char *navname; int nargs = list_length(fargs); Node *arg; + Node *offset = NULL; RPRNavExpr *navexpr; + int loc; + Oid offtype; + RPRNavExpr *inner = NULL; + Node *rawoffset = NULL; - /* match the parser-downcased identifier; otherwise not a navigation name */ - if (strcmp(name, "prev") == 0) - { - kind = RPR_NAV_PREV; - navname = "PREV"; - } - else if (strcmp(name, "next") == 0) - { - kind = RPR_NAV_NEXT; - navname = "NEXT"; - } - else if (strcmp(name, "first") == 0) - { - kind = RPR_NAV_FIRST; - navname = "FIRST"; - } - else if (strcmp(name, "last") == 0) - { - kind = RPR_NAV_LAST; - navname = "LAST"; - } - else - { - /* the caller only routes here after matching one of the four names */ - pg_unreachable(); - return NULL; - } + navexpr = makeNode(RPRNavExpr); + + /* the caller only routes here after matching one of the four names */ + navname = rpr_nav_lookup(strVal(linitial(funcname)), &kind); + Assert(navname != NULL); /* * Once the name matches we never fall back to function resolution, so any @@ -2236,49 +2291,100 @@ ParseRPRNavCall(ParseState *pstate, List *funcname, List *fargs, navname), parser_errposition(pstate, location))); - /* - * Resolve a still-unknown first argument to text, the same way the - * anycompatible family does. A navigation operation is not a polymorphic - * function, so the old "could not determine polymorphic type" error does - * not apply; an unknown literal cannot contain a column reference, so the - * walker still rejects it later. - */ arg = linitial(fargs); if (exprType(arg) == UNKNOWNOID) arg = coerce_to_common_type(pstate, arg, TEXTOID, navname); - navexpr = makeNode(RPRNavExpr); + if (nargs == 2) + rawoffset = lsecond(fargs); + + /* an explicit offset is coerced to int8, which the executor reads */ + if (rawoffset != NULL) + { + loc = exprLocation(rawoffset); + offtype = exprType(rawoffset); + + offset = coerce_to_target_type(pstate, rawoffset, offtype, + INT8OID, -1, COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, -1); + if (offset == NULL) + ereport(ERROR, + errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("offset argument of %s must be type %s, not type %s", + navname, format_type_be(INT8OID), format_type_be(offtype)), + parser_errposition(pstate, loc)); + + /* offset must be a run-time constant */ + if (contain_var_clause(offset)) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("row pattern navigation offset must be a run-time constant"), + parser_errposition(pstate, loc)); + } + + (void) find_rpr_nav_walker(arg, &inner); + + if (inner != NULL) + { + if ((kind != RPR_NAV_PREV && kind != RPR_NAV_NEXT) || + (inner->kind != RPR_NAV_FIRST && inner->kind != RPR_NAV_LAST)) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid row pattern navigation function form: %s(%s)", + navname, rpr_nav_kind_form(inner->kind)), + errhint("Row pattern navigation operations can only be nested as \"%s\", \"%s\", \"%s\", or \"%s\".", + "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", "NEXT(LAST())"), + parser_errposition(pstate, inner->location)); + + /* Reject an inner nav that is not the whole argument */ + if ((Node *) inner != arg) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("nested row pattern navigation operation must be the direct argument of %s", + navname), + errhint("Row pattern navigation operations can only be nested as \"%s\", \"%s\", \"%s\", or \"%s\".", + "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", "NEXT(LAST())"), + parser_errposition(pstate, location)); + + /* + * transform PREV/NEXT(FIRST/LAST(arg, inner_off), outer_off) into one + * node + */ + if (kind == RPR_NAV_PREV) + { + if (inner->kind == RPR_NAV_FIRST) + kind = RPR_NAV_PREV_FIRST; + else + kind = RPR_NAV_PREV_LAST; + } + else + { + Assert(kind == RPR_NAV_NEXT); + + if (inner->kind == RPR_NAV_FIRST) + kind = RPR_NAV_NEXT_FIRST; + else + kind = RPR_NAV_NEXT_LAST; + } + + /* the outer offset becomes the compound offset; take the inner's rest */ + navexpr->compound_offset_arg = (Expr *) offset; + offset = (Node *) inner->offset_arg; + arg = (Node *) inner->arg; + } + else + { + if (!contain_var_clause(arg)) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("argument of row pattern navigation operation must include at least one column reference"), + parser_errposition(pstate, exprLocation(arg))); + } + navexpr->kind = kind; navexpr->arg = (Expr *) arg; + navexpr->offset_arg = (Expr *) offset; navexpr->navno = -1; /* assigned while planning */ - - /* an explicit offset is coerced to int8, which the executor reads */ - if (nargs == 2) - { - Node *offset = lsecond(fargs); - Oid offtype = exprType(offset); - - if (offtype != INT8OID) - { - Node *newoffset; - - newoffset = coerce_to_target_type(pstate, offset, offtype, - INT8OID, -1, COERCION_IMPLICIT, - COERCE_IMPLICIT_CAST, -1); - if (newoffset == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("offset argument of %s must be type %s, not type %s", - navname, "bigint", format_type_be(offtype)), - parser_errposition(pstate, exprLocation(offset)))); - offset = newoffset; - } - navexpr->offset_arg = (Expr *) offset; - } - else - navexpr->offset_arg = NULL; - - /* compound_offset_arg stays NULL; define_walker flattening fills it in */ navexpr->resulttype = exprType(arg); /* resultcollid will be set by parse_collate.c */ navexpr->location = location; diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c index 6292cd0547f..9d57bf5a195 100644 --- a/src/backend/parser/parse_rpr.c +++ b/src/backend/parser/parse_rpr.c @@ -34,30 +34,11 @@ #include "parser/parse_rpr.h" #include "parser/parse_target.h" -/* DEFINE clause walker context -- see define_walker for usage. */ -typedef enum -{ - DEFINE_PHASE_BODY, /* top-level DEFINE expression */ - DEFINE_PHASE_NAV_ARG, /* inside an outer nav's arg subtree */ - DEFINE_PHASE_NAV_OFFSET, /* inside an outer nav's offset_arg / - * compound_offset_arg */ -} DefinePhase; - -typedef struct -{ - ParseState *pstate; - DefinePhase phase; - int nav_count; /* RPRNavExpr nodes seen in current nav.arg */ - bool has_column_ref; /* Var seen in current nav scope */ - RPRNavKind inner_kind; /* kind of first nested nav in current arg */ -} DefineWalkCtx; - /* Forward declarations */ static void validateRPRPatternVarCount(ParseState *pstate, RPRPatternNode *node, List **varNames); static List *transformDefineClause(ParseState *pstate, WindowDef *windef, List **targetlist); -static bool define_walker(Node *node, void *context); /* * transformRPR @@ -398,22 +379,6 @@ transformDefineClause(ParseState *pstate, WindowDef *windef, } pstate->p_rpr_pattern_vars = NIL; - /* - * Validate DEFINE expressions: nested PREV/NEXT, column references, - * compound flatten -- all in a single walk per variable. - */ - foreach_ptr(TargetEntry, te, defineClause) - { - DefineWalkCtx ctx; - - ctx.pstate = pstate; - ctx.phase = DEFINE_PHASE_BODY; - ctx.nav_count = 0; - ctx.has_column_ref = false; - ctx.inner_kind = 0; - (void) define_walker((Node *) te->expr, &ctx); - } - /* mark column origins */ markTargetListOrigins(pstate, defineClause); @@ -422,210 +387,3 @@ transformDefineClause(ParseState *pstate, WindowDef *windef, return defineClause; } - -/* - * define_walker - * Single-pass DEFINE clause validator. At each node, enforces: - * - * [1] for each outer RPRNavExpr (PHASE_BODY -> PHASE_NAV_ARG): - * - nav.arg must contain at least one column reference - * - PREV/NEXT wrapping FIRST/LAST is flattened in place - * to a compound kind (PREV_FIRST, PREV_LAST, NEXT_FIRST, - * NEXT_LAST) - * - an inner navigation that is not nav.arg itself is - * rejected as not being a direct argument - * - any other nesting is rejected (FIRST(PREV()), - * PREV(PREV()), FIRST(FIRST()), three-or-more deep) - * [2] for each nav offset (PHASE_NAV_OFFSET): - * - must be a run-time constant (no column references) - * - must not contain a row pattern navigation operation - * - * Entering an outer nav, the walker walks nav.arg in PHASE_NAV_ARG to collect - * nesting and column-ref state, flattens a compound form or raises a nesting - * error, then walks the post-flatten offset(s) in PHASE_NAV_OFFSET. A - * compound form's inner offset is walked in both passes: PHASE_NAV_ARG only - * asks whether nav.arg as a whole holds a column reference, so the offset is - * walked again to catch one it would have leaked. - * - * Var sightings feed the column-ref rule for the enclosing nav scope; - * RPRNavExpr sightings inside PHASE_NAV_ARG feed the nesting decision. - * The phases themselves are described where DefinePhase is declared. - */ -static bool -define_walker(Node *node, void *context) -{ - DefineWalkCtx *ctx = (DefineWalkCtx *) context; - - if (node == NULL) - return false; - - /* Var sighting feeds the column-ref rule for the enclosing nav scope. */ - if (IsA(node, Var) && - (ctx->phase == DEFINE_PHASE_NAV_ARG || - ctx->phase == DEFINE_PHASE_NAV_OFFSET)) - ctx->has_column_ref = true; - - if (IsA(node, RPRNavExpr)) - { - RPRNavExpr *nav = (RPRNavExpr *) node; - - if (ctx->phase == DEFINE_PHASE_NAV_ARG) - { - /* - * Nested nav inside an outer nav.arg: record for the outer's - * compound / nesting decision, then keep recursing so deeper Vars - * are still observed. - */ - if (ctx->nav_count == 0) - ctx->inner_kind = nav->kind; - ctx->nav_count++; - return expression_tree_walker(node, define_walker, ctx); - } - else if (ctx->phase == DEFINE_PHASE_NAV_OFFSET) - { - /* - * A navigation offset must be a run-time constant, so it cannot - * contain a navigation operation. - */ - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("row pattern navigation offset cannot contain a row pattern navigation operation"), - errdetail("A navigation offset must be a run-time constant."), - parser_errposition(ctx->pstate, nav->location)); - } - else - { - /* - * PHASE_BODY: this is an outer nav at top level. Walk arg first - * to collect nesting / column-ref state, then validate and (for - * compound forms) flatten, then walk offset(s). - */ - DefineWalkCtx saved = *ctx; - bool outer_phys = (nav->kind == RPR_NAV_PREV || - nav->kind == RPR_NAV_NEXT); - bool flattened = false; - - ctx->phase = DEFINE_PHASE_NAV_ARG; - ctx->nav_count = 0; - ctx->has_column_ref = false; - ctx->inner_kind = 0; - (void) define_walker((Node *) nav->arg, ctx); - - if (ctx->nav_count > 0) - { - bool inner_phys = (ctx->inner_kind == RPR_NAV_PREV || - ctx->inner_kind == RPR_NAV_NEXT); - - if (outer_phys && !inner_phys) - { - RPRNavExpr *inner; - - /* Reject an inner nav that is not the whole argument */ - if (!IsA(nav->arg, RPRNavExpr)) - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("row pattern navigation operation must be a direct argument of the outer navigation"), - errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."), - parser_errposition(ctx->pstate, nav->location)); - - /* Reject triple-or-deeper nesting; siblings caught above */ - if (ctx->nav_count > 1) - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("cannot nest row pattern navigation more than two levels deep"), - errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."), - parser_errposition(ctx->pstate, nav->location)); - - inner = (RPRNavExpr *) nav->arg; - - if (nav->kind == RPR_NAV_PREV && inner->kind == RPR_NAV_FIRST) - nav->kind = RPR_NAV_PREV_FIRST; - else if (nav->kind == RPR_NAV_PREV && inner->kind == RPR_NAV_LAST) - nav->kind = RPR_NAV_PREV_LAST; - else if (nav->kind == RPR_NAV_NEXT && inner->kind == RPR_NAV_FIRST) - nav->kind = RPR_NAV_NEXT_FIRST; - else if (nav->kind == RPR_NAV_NEXT && inner->kind == RPR_NAV_LAST) - nav->kind = RPR_NAV_NEXT_LAST; - - nav->compound_offset_arg = nav->offset_arg; - nav->offset_arg = inner->offset_arg; - nav->arg = inner->arg; - flattened = true; - - /* - * The flattened argument must include a column reference, - * just like the simple-nav case below. - */ - if (!ctx->has_column_ref) - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("argument of row pattern navigation operation must include at least one column reference"), - parser_errposition(ctx->pstate, nav->location)); - } - else if (!outer_phys && inner_phys) - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("FIRST and LAST cannot contain PREV or NEXT"), - errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."), - parser_errposition(ctx->pstate, nav->location)); - else if (outer_phys && inner_phys) - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("PREV and NEXT cannot contain PREV or NEXT"), - errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."), - parser_errposition(ctx->pstate, nav->location)); - else - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("FIRST and LAST cannot contain FIRST or LAST"), - errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."), - parser_errposition(ctx->pstate, nav->location)); - } - else if (!ctx->has_column_ref) - { - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("argument of row pattern navigation operation must include at least one column reference"), - parser_errposition(ctx->pstate, nav->location)); - } - - /* - * Walk offset arg(s) in PHASE_NAV_OFFSET to enforce the - * constant-offset rule. For compound forms, both the inner - * (post-flatten nav->offset_arg) and outer (compound_offset_arg) - * offsets must be constants; the inner's column-ref status was - * not separately tracked during the PHASE_NAV_ARG walk (which - * only checks that nav.arg as a whole has at least one Var), so - * it is re-walked here to catch column references the inner - * offset would have leaked. - */ - ctx->phase = DEFINE_PHASE_NAV_OFFSET; - - if (nav->offset_arg != NULL) - { - ctx->has_column_ref = false; - (void) define_walker((Node *) nav->offset_arg, ctx); - if (ctx->has_column_ref) - ereport(ERROR, - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row pattern navigation offset must be a run-time constant"), - parser_errposition(ctx->pstate, exprLocation((Node *) nav->offset_arg))); - } - if (flattened && nav->compound_offset_arg != NULL) - { - ctx->has_column_ref = false; - (void) define_walker((Node *) nav->compound_offset_arg, ctx); - if (ctx->has_column_ref) - ereport(ERROR, - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("row pattern navigation offset must be a run-time constant"), - parser_errposition(ctx->pstate, exprLocation((Node *) nav->compound_offset_arg))); - } - - *ctx = saved; - return false; - } - } - - return expression_tree_walker(node, define_walker, ctx); -} diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out index c4958c1b8d8..b789b729210 100644 --- a/src/test/regress/expected/rpr.out +++ b/src/test/regress/expected/rpr.out @@ -1017,10 +1017,10 @@ WINDOW w AS ( PATTERN (A) DEFINE A AS price > PREV(PREV(price)) ); -ERROR: PREV and NEXT cannot contain PREV or NEXT +ERROR: invalid row pattern navigation function form: PREV(PREV()) LINE 7: DEFINE A AS price > PREV(PREV(price)) - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- Nested NEXT SELECT price FROM stock WINDOW w AS ( @@ -1030,10 +1030,10 @@ WINDOW w AS ( PATTERN (A) DEFINE A AS price > NEXT(NEXT(price)) ); -ERROR: PREV and NEXT cannot contain PREV or NEXT +ERROR: invalid row pattern navigation function form: NEXT(NEXT()) LINE 7: DEFINE A AS price > NEXT(NEXT(price)) - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- PREV nested inside NEXT SELECT price FROM stock WINDOW w AS ( @@ -1043,10 +1043,10 @@ WINDOW w AS ( PATTERN (A) DEFINE A AS price > NEXT(PREV(price)) ); -ERROR: PREV and NEXT cannot contain PREV or NEXT +ERROR: invalid row pattern navigation function form: NEXT(PREV()) LINE 7: DEFINE A AS price > NEXT(PREV(price)) - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- PREV nested inside expression inside NEXT SELECT price FROM stock WINDOW w AS ( @@ -1056,10 +1056,10 @@ WINDOW w AS ( PATTERN (A) DEFINE A AS price > NEXT(price * PREV(price)) ); -ERROR: PREV and NEXT cannot contain PREV or NEXT +ERROR: invalid row pattern navigation function form: NEXT(PREV()) LINE 7: DEFINE A AS price > NEXT(price * PREV(price)) - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- Triple nesting: error reported at outermost PREV SELECT price FROM stock WINDOW w AS ( @@ -1069,10 +1069,10 @@ WINDOW w AS ( PATTERN (A) DEFINE A AS price > PREV(PREV(PREV(price))) ); -ERROR: PREV and NEXT cannot contain PREV or NEXT +ERROR: invalid row pattern navigation function form: PREV(PREV()) LINE 7: DEFINE A AS price > PREV(PREV(PREV(price))) - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- No column reference in PREV/NEXT argument -- PREV(1): constant only, no column reference SELECT price FROM stock @@ -1085,7 +1085,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS PREV(1) > 0 - ^ + ^ -- NEXT(1 + 2): constant expression, no column reference SELECT price FROM stock WINDOW w AS ( @@ -1097,7 +1097,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS NEXT(1 + 2) > 0 - ^ + ^ -- 2-arg form: PREV(1, 1): constant expression as first arg SELECT price FROM stock WINDOW w AS ( @@ -1109,7 +1109,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS PREV(1, 1) > 0 - ^ + ^ -- Compound navigation without a column reference must be rejected too, -- consistent with the simple forms above. -- PREV(FIRST(1)): compound, constant only, no column reference @@ -1123,7 +1123,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS PREV(FIRST(1)) > 0 - ^ + ^ -- NEXT(LAST(1 + 2)): compound, constant expression, no column reference SELECT price FROM stock WINDOW w AS ( @@ -1135,7 +1135,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS NEXT(LAST(1 + 2)) > 0 - ^ + ^ -- PREV(FIRST(1, 2)): compound, two-arg inner, no column reference SELECT price FROM stock WINDOW w AS ( @@ -1147,7 +1147,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS PREV(FIRST(1, 2)) > 0 - ^ + ^ -- PREV(FIRST(1), 2): compound, outer offset only, no column reference SELECT price FROM stock WINDOW w AS ( @@ -1159,7 +1159,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS PREV(FIRST(1), 2) > 0 - ^ + ^ -- PREV(FIRST(1, 2), 3): compound, inner and outer offsets, no column reference SELECT price FROM stock WINDOW w AS ( @@ -1171,7 +1171,7 @@ WINDOW w AS ( ); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS PREV(FIRST(1, 2), 3) > 0 - ^ + ^ -- Non-constant offset: column reference as offset SELECT price FROM stock WINDOW w AS ( @@ -2631,10 +2631,10 @@ SELECT id, val FROM rpr_nav WINDOW w AS ( PATTERN (A B) DEFINE A AS TRUE, B AS FIRST(PREV(val)) > 0 ); -ERROR: FIRST and LAST cannot contain PREV or NEXT +ERROR: invalid row pattern navigation function form: FIRST(PREV()) LINE 5: DEFINE A AS TRUE, B AS FIRST(PREV(val)) > 0 - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- Reverse nesting: LAST wrapping NEXT is prohibited SELECT id, val FROM rpr_nav WINDOW w AS ( ORDER BY id @@ -2642,10 +2642,10 @@ SELECT id, val FROM rpr_nav WINDOW w AS ( PATTERN (A B) DEFINE A AS TRUE, B AS LAST(NEXT(val)) > 0 ); -ERROR: FIRST and LAST cannot contain PREV or NEXT +ERROR: invalid row pattern navigation function form: LAST(NEXT()) LINE 5: DEFINE A AS TRUE, B AS LAST(NEXT(val)) > 0 - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". DROP TABLE rpr_nav; -- -- SKIP TO / Backtracking / Frame boundary diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out index ed93bda2cfc..86f4a3f164f 100644 --- a/src/test/regress/expected/rpr_base.out +++ b/src/test/regress/expected/rpr_base.out @@ -2749,10 +2749,9 @@ SELECT id, val PATTERN (A+) DEFINE A AS PREV(val, FIRST(1)) > 0) ORDER BY id; -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 6: DEFINE A AS PREV(val, FIRST(1)) > 0) - ^ -DETAIL: A navigation offset must be a run-time constant. + ^ DROP SCHEMA rpr_navns CASCADE; RESET search_path; -- ============================================================ @@ -4860,10 +4859,10 @@ WINDOW w AS ( PATTERN (A+) DEFINE A AS PREV(v + FIRST(v)) > 0 ); -ERROR: row pattern navigation operation must be a direct argument of the outer navigation +ERROR: nested row pattern navigation operation must be the direct argument of PREV LINE 6: DEFINE A AS PREV(v + FIRST(v)) > 0 ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- FIRST/LAST wrapping FIRST/LAST: prohibited SELECT count(*) OVER w FROM generate_series(1,10) s(v) @@ -4872,10 +4871,10 @@ WINDOW w AS ( PATTERN (A+) DEFINE A AS FIRST(FIRST(v)) > 0 ); -ERROR: FIRST and LAST cannot contain FIRST or LAST +ERROR: invalid row pattern navigation function form: FIRST(FIRST()) LINE 6: DEFINE A AS FIRST(FIRST(v)) > 0 - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- Triple nesting: prohibited (3-level deep navigation) SELECT count(*) OVER w FROM generate_series(1,10) s(v) @@ -4884,10 +4883,10 @@ WINDOW w AS ( PATTERN (A+) DEFINE A AS PREV(FIRST(PREV(v))) > 0 ); -ERROR: cannot nest row pattern navigation more than two levels deep +ERROR: invalid row pattern navigation function form: FIRST(PREV()) LINE 6: DEFINE A AS PREV(FIRST(PREV(v))) > 0 - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- Sibling navigations: prohibited, but they are not a deeper nesting, -- so the inner navigation must be reported as not being the direct -- argument rather than as a third level. @@ -4898,10 +4897,10 @@ WINDOW w AS ( PATTERN (A+) DEFINE A AS PREV(FIRST(v) + LAST(v)) > 0 ); -ERROR: row pattern navigation operation must be a direct argument of the outer navigation +ERROR: nested row pattern navigation operation must be the direct argument of PREV LINE 6: DEFINE A AS PREV(FIRST(v) + LAST(v)) > 0 ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- Three navigations, but the inner one is again not the whole argument, so -- that is what gets reported and the depth is not reached SELECT count(*) OVER w @@ -4911,75 +4910,67 @@ WINDOW w AS ( PATTERN (A+) DEFINE A AS PREV(FIRST(PREV(v)) + 1) > 0 ); -ERROR: row pattern navigation operation must be a direct argument of the outer navigation +ERROR: invalid row pattern navigation function form: FIRST(PREV()) LINE 6: DEFINE A AS PREV(FIRST(PREV(v)) + 1) > 0 - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ +HINT: Row pattern navigation operations can only be nested as "PREV(FIRST())", "PREV(LAST())", "NEXT(FIRST())", or "NEXT(LAST())". -- A navigation offset must be a run-time constant, not a navigation operation SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(v, FIRST(1)) > 0); -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV(v, FIRST(1)) > 0); - ^ -DETAIL: A navigation offset must be a run-time constant. + ^ SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(v, FIRST(1) + 1) > 0); -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV(v, FIRST(1) + 1) > 0); - ^ -DETAIL: A navigation offset must be a run-time constant. + ^ SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(v, NEXT(1, 0)) > 0); -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV(v, NEXT(1, 0)) > 0); - ^ -DETAIL: A navigation offset must be a run-time constant. + ^ SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(FIRST(v), LAST(1)) > 0); -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV(FIRST(v), LAST(1)) > 0); - ^ -DETAIL: A navigation offset must be a run-time constant. + ^ SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(v, FIRST(v)) > 0); -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: row pattern navigation offset must be a run-time constant LINE 4: PATTERN (A+) DEFINE A AS PREV(v, FIRST(v)) > 0); ^ -DETAIL: A navigation offset must be a run-time constant. SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS NEXT(v, PREV(v, 1)) > 0); -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: row pattern navigation offset must be a run-time constant LINE 4: PATTERN (A+) DEFINE A AS NEXT(v, PREV(v, 1)) > 0); ^ -DETAIL: A navigation offset must be a run-time constant. SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(FIRST(v, LAST(1)), 2) > 0); -ERROR: cannot nest row pattern navigation more than two levels deep +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV(FIRST(v, LAST(1)), 2) > 0)... - ^ -HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. + ^ SELECT count(*) OVER w FROM generate_series(1,10) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(v, FIRST(1::bigint)) > 0); -ERROR: row pattern navigation offset cannot contain a row pattern navigation operation +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV(v, FIRST(1::bigint)) > 0); - ^ -DETAIL: A navigation offset must be a run-time constant. + ^ -- An unknown literal argument resolves to text; it must still reference a column SELECT count(*) OVER w FROM generate_series(1,5) s(v) @@ -4987,28 +4978,28 @@ WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV('foo') = 'bar'); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV('foo') = 'bar'); - ^ + ^ SELECT count(*) OVER w FROM generate_series(1,5) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV('foo')); -ERROR: argument of DEFINE must be type boolean, not type text +ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV('foo')); - ^ + ^ SELECT count(*) OVER w FROM generate_series(1,5) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV(NULL) IS NULL); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV(NULL) IS NULL); - ^ + ^ PREPARE rpr_navarg AS SELECT count(*) OVER w FROM generate_series(1,5) s(v) WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A+) DEFINE A AS PREV($1) IS NULL); ERROR: argument of row pattern navigation operation must include at least one column reference LINE 4: PATTERN (A+) DEFINE A AS PREV($1) IS NULL); - ^ + ^ -- An int2 offset is coerced to int8 like any implicit cast (same as plain 0) SELECT count(*) OVER w FROM generate_series(1,5) s(v) diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 57b1ef7ab6c..704762d0690 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -665,9 +665,7 @@ DefElem DefElemAction DefaultACLInfo DefineMetadataContext -DefinePhase DefineStmt -DefineWalkCtx DefnDumperPtr DeleteStmt DependenciesParseState -- 2.34.1