public inbox for [email protected]help / color / mirror / Atom feed
[PATCH v5] Simplify immutable RTE_FUNCTION to RTE_RESULT. 3+ messages / 3 participants [nested] [flat]
* [PATCH v5] Simplify immutable RTE_FUNCTION to RTE_RESULT. @ 2019-03-20 16:56 Alexander Kuzmenkov <[email protected]> 0 siblings, 0 replies; 3+ messages in thread From: Alexander Kuzmenkov @ 2019-03-20 16:56 UTC (permalink / raw) --- src/backend/optimizer/plan/planner.c | 6 +- src/backend/optimizer/prep/prepjointree.c | 85 ++++++++++++++++++++++ src/test/regress/expected/join.out | 113 +++++++++++++++++++++++++++--- src/test/regress/sql/join.sql | 44 ++++++++++-- 4 files changed, 234 insertions(+), 14 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index e408e77..fe2f426 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1050,7 +1050,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind) expr = flatten_join_alias_vars(root->parse, expr); /* - * Simplify constant expressions. + * Simplify constant expressions. For function RTEs, this was already done + * by pullup_simple_subqueries. * * Note: an essential effect of this is to convert named-argument function * calls to positional notation and insert the current actual values of @@ -1064,7 +1065,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind) * careful to maintain AND/OR flatness --- that is, do not generate a tree * with AND directly under AND, nor OR directly under OR. */ - expr = eval_const_expressions(root, expr); + if (kind != EXPRKIND_RTFUNC && kind != EXPRKIND_RTFUNC_LATERAL) + expr = eval_const_expressions(root, expr); /* * If it's a qual or havingQual, canonicalize it. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index aebe162..313c2bb 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -79,6 +79,9 @@ static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset); +static void transform_const_function_to_result(PlannerInfo *root, Node *jtnode, + RangeTblEntry *rte, + JoinExpr *lowest_nulling_outer_join); static void make_setop_translation_list(Query *query, Index newvarno, List **translated_vars); static bool is_simple_subquery(Query *subquery, RangeTblEntry *rte, @@ -754,6 +757,18 @@ pull_up_subqueries_recurse(PlannerInfo *root, Node *jtnode, is_simple_values(root, rte)) return pull_up_simple_values(root, jtnode, rte); + /* + * Or is it an immutable function that evaluated to a single Const? + */ + if (rte->rtekind == RTE_FUNCTION) + { + rte->functions = (List *) + eval_const_expressions(root, (Node *) rte->functions); + transform_const_function_to_result(root, jtnode, rte, + lowest_nulling_outer_join); + return jtnode; + } + /* Otherwise, do nothing at this node. */ } else if (IsA(jtnode, FromExpr)) @@ -1783,6 +1798,76 @@ is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes) } /* + * If the function of this RTE_FUNCTION entry evaluated to a single Const + * after eval_const_expressions, transform it to RTE_RESULT. + */ +static void +transform_const_function_to_result(PlannerInfo *root, Node *jtnode, + RangeTblEntry *rte, + JoinExpr *lowest_nulling_outer_join) +{ + ListCell *lc; + pullup_replace_vars_context rvcontext; + RangeTblFunction *rtf = (RangeTblFunction *) linitial(rte->functions); + Query *parse = root->parse; + + if (!IsA(rtf->funcexpr, Const)) + return; + + if (rte->funcordinality) + return; + + if (list_length(rte->functions) != 1) + return; + + rvcontext.targetlist = list_make1(makeTargetEntry((Expr *) rtf->funcexpr, + 1 /* resno */, NULL /* resname */, false /* resjunk */)); + rvcontext.root = root; + rvcontext.target_rte = rte; + + /* + * Since this function was reduced to Const, it can't really have lateral + * references, even if it's marked as LATERAL. This means we don't need + * to fill relids. + */ + rvcontext.relids = NULL; + + rvcontext.outer_hasSubLinks = &parse->hasSubLinks; + rvcontext.varno = ((RangeTblRef *) jtnode)->rtindex; + + /* + * If this RTE is on a nullable side of an outer join, we have to insert + * PHVs around our Consts so that they go to null when needed. + */ + rvcontext.need_phvs = lowest_nulling_outer_join != NULL; + + rvcontext.wrap_non_vars = false; + rvcontext.rv_cache = palloc0((list_length(rvcontext.targetlist) + 1) + * sizeof(Node *)); + + parse->targetList = (List *) + pullup_replace_vars((Node *) parse->targetList, &rvcontext); + parse->returningList = (List *) + pullup_replace_vars((Node *) parse->returningList, &rvcontext); + replace_vars_in_jointree((Node *) parse->jointree, &rvcontext, NULL); + + foreach(lc, parse->rtable) + { + RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(lc); + + if (otherrte->rtekind == RTE_JOIN) + otherrte->joinaliasvars = (List *) + pullup_replace_vars((Node *) otherrte->joinaliasvars, + &rvcontext); + } + + rte->rtekind = RTE_RESULT; + rte->functions = NIL; + + return; +} + +/* * is_safe_append_member * Check a subquery that is a leaf of a UNION ALL appendrel to see if it's * safe to pull up. diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 88fcd52..200bdde 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3060,11 +3060,14 @@ select * from int4_tbl a full join int4_tbl b on false; -- -- test for ability to use a cartesian join when necessary -- +create temp table q1 as select 1 q1; +create temp table q2 as select 0 q2; +analyze q1; +analyze q2; explain (costs off) select * from tenk1 join int4_tbl on f1 = twothousand, - int4(sin(1)) q1, - int4(sin(0)) q2 + q1, q2 where q1 = thousand or q2 = thousand; QUERY PLAN ------------------------------------------------------------------------ @@ -3072,8 +3075,8 @@ where q1 = thousand or q2 = thousand; Hash Cond: (tenk1.twothousand = int4_tbl.f1) -> Nested Loop -> Nested Loop - -> Function Scan on q1 - -> Function Scan on q2 + -> Seq Scan on q1 + -> Seq Scan on q2 -> Bitmap Heap Scan on tenk1 Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand)) -> BitmapOr @@ -3088,8 +3091,7 @@ where q1 = thousand or q2 = thousand; explain (costs off) select * from tenk1 join int4_tbl on f1 = twothousand, - int4(sin(1)) q1, - int4(sin(0)) q2 + q1, q2 where thousand = (q1 + q2); QUERY PLAN -------------------------------------------------------------- @@ -3097,8 +3099,8 @@ where thousand = (q1 + q2); Hash Cond: (tenk1.twothousand = int4_tbl.f1) -> Nested Loop -> Nested Loop - -> Function Scan on q1 - -> Function Scan on q2 + -> Seq Scan on q1 + -> Seq Scan on q2 -> Bitmap Heap Scan on tenk1 Recheck Cond: (thousand = (q1.q1 + q2.q2)) -> Bitmap Index Scan on tenk1_thous_tenthous @@ -3241,6 +3243,101 @@ where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; (1 row) -- +-- test inlining of immutable functions +-- +explain (costs off) +select unique1 from tenk1, (select int4(1) x) x where x = unique1; + QUERY PLAN +---------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = 1) +(2 rows) + +explain (costs off) +select unique1 from tenk1, (select * from int4(1) x) x where x = unique1; + QUERY PLAN +---------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = 1) +(2 rows) + +explain (costs off) +select unique1 from tenk1 join (select * from int4(1) x) x on x = unique1; + QUERY PLAN +---------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = 1) +(2 rows) + +explain (costs off) +select unique1, x.* from tenk1, (select *, random() from int4(1) x) x where x = unique1; + QUERY PLAN +---------------------------------------------------- + Nested Loop + -> Result + -> Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = (1)) +(4 rows) + +explain (costs off) +select unique1 from tenk1, int4(1) x where x = unique1; + QUERY PLAN +---------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = 1) +(2 rows) + +explain (costs off) +select unique1 from tenk1, lateral int4(1) x where x = unique1; + QUERY PLAN +---------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = 1) +(2 rows) + +explain (costs off) +select unique1, x from tenk1 join int4(1) x on unique1 = x; + QUERY PLAN +---------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = 1) +(2 rows) + +explain (costs off) +select unique1, x from tenk1 left join int4(1) x on unique1 = x; + QUERY PLAN +---------------------------------------------------- + Merge Left Join + Merge Cond: (tenk1.unique1 = (1)) + -> Index Only Scan using tenk1_unique1 on tenk1 + -> Sort + Sort Key: (1) + -> Result +(6 rows) + +explain (costs off) +select unique1, x from tenk1 right join int4(1) x on unique1 = x; + QUERY PLAN +---------------------------------------------------- + Nested Loop Left Join + -> Result + -> Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: (unique1 = 1) +(4 rows) + +explain (costs off) +select unique1, x from tenk1 full join int4(1) x on unique1 = x; + QUERY PLAN +---------------------------------------------------- + Merge Full Join + Merge Cond: (tenk1.unique1 = (1)) + -> Index Only Scan using tenk1_unique1 on tenk1 + -> Sort + Sort Key: (1) + -> Result +(6 rows) + +-- -- test extraction of restriction OR clauses from join OR clause -- (we used to only do this for indexable clauses) -- diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index c247509..b05be86 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -914,18 +914,21 @@ select * from int4_tbl a full join int4_tbl b on false; -- test for ability to use a cartesian join when necessary -- +create temp table q1 as select 1 q1; +create temp table q2 as select 0 q2; +analyze q1; +analyze q2; + explain (costs off) select * from tenk1 join int4_tbl on f1 = twothousand, - int4(sin(1)) q1, - int4(sin(0)) q2 + q1, q2 where q1 = thousand or q2 = thousand; explain (costs off) select * from tenk1 join int4_tbl on f1 = twothousand, - int4(sin(1)) q1, - int4(sin(0)) q2 + q1, q2 where thousand = (q1 + q2); -- @@ -1016,6 +1019,39 @@ select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; -- +-- test inlining of immutable functions +-- +explain (costs off) +select unique1 from tenk1, (select int4(1) x) x where x = unique1; + +explain (costs off) +select unique1 from tenk1, (select * from int4(1) x) x where x = unique1; + +explain (costs off) +select unique1 from tenk1 join (select * from int4(1) x) x on x = unique1; + +explain (costs off) +select unique1, x.* from tenk1, (select *, random() from int4(1) x) x where x = unique1; + +explain (costs off) +select unique1 from tenk1, int4(1) x where x = unique1; + +explain (costs off) +select unique1 from tenk1, lateral int4(1) x where x = unique1; + +explain (costs off) +select unique1, x from tenk1 join int4(1) x on unique1 = x; + +explain (costs off) +select unique1, x from tenk1 left join int4(1) x on unique1 = x; + +explain (costs off) +select unique1, x from tenk1 right join int4(1) x on unique1 = x; + +explain (costs off) +select unique1, x from tenk1 full join int4(1) x on unique1 = x; + +-- -- test extraction of restriction OR clauses from join OR clause -- (we used to only do this for indexable clauses) -- -- 2.7.4 --------------6CF956F72F1A58485BF937FB-- ^ permalink raw reply [nested|flat] 3+ messages in thread
* Re: commitfest.postgresql.org is no longer fit for purpose @ 2024-05-16 22:03 Tom Lane <[email protected]> 0 siblings, 0 replies; 3+ messages in thread From: Tom Lane @ 2024-05-16 22:03 UTC (permalink / raw) To: Daniel Gustafsson <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; Melanie Plageman <[email protected]>; Robert Haas <[email protected]>; pgsql-hackers Daniel Gustafsson <[email protected]> writes: > Pulling in the information from the CFBot regarding test failures and whether > the patch applies at all, and automatically altering the state to WOA for at > least the latter category would be nice. +1. There are enough intermittent test failures that I don't think changing state for that would be good, but patch apply failure is usually persistent. I gather that the CFBot is still a kluge that is web-scraping the CF app rather than being actually integrated with it, and that there are plans to make that better that haven't been moving fast. Probably that would have to happen first, but there would be a lot of benefit from having the info flow be two-way. regards, tom lane ^ permalink raw reply [nested|flat] 3+ messages in thread
* [PATCH v22 2/8] Row pattern recognition patch (parse/analysis). @ 2024-09-19 04:48 Tatsuo Ishii <[email protected]> 0 siblings, 0 replies; 3+ messages in thread From: Tatsuo Ishii @ 2024-09-19 04:48 UTC (permalink / raw) --- src/backend/parser/parse_agg.c | 7 + src/backend/parser/parse_clause.c | 296 +++++++++++++++++++++++++++++- src/backend/parser/parse_expr.c | 6 + src/backend/parser/parse_func.c | 3 + 4 files changed, 311 insertions(+), 1 deletion(-) diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index efa730c167..a80263f90d 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -580,6 +580,10 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) errkind = true; break; + case EXPR_KIND_RPR_DEFINE: + errkind = true; + break; + /* * There is intentionally no default: case here, so that the * compiler will warn if we add a new ParseExprKind without @@ -970,6 +974,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, case EXPR_KIND_CYCLE_MARK: errkind = true; break; + case EXPR_KIND_RPR_DEFINE: + errkind = true; + break; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 8118036495..9762dce81f 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -98,7 +98,14 @@ static WindowClause *findWindowClause(List *wclist, const char *name); static Node *transformFrameOffset(ParseState *pstate, int frameOptions, Oid rangeopfamily, Oid rangeopcintype, Oid *inRangeFunc, Node *clause); - +static void transformRPR(ParseState *pstate, WindowClause *wc, WindowDef *windef, + List **targetlist); +static List *transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef, + List **targetlist); +static void transformPatternClause(ParseState *pstate, WindowClause *wc, + WindowDef *windef); +static List *transformMeasureClause(ParseState *pstate, WindowClause *wc, + WindowDef *windef); /* * transformFromClause - @@ -2956,6 +2963,10 @@ transformWindowDefinitions(ParseState *pstate, rangeopfamily, rangeopcintype, &wc->endInRangeFunc, windef->endOffset); + + /* Process Row Pattern Recognition related clauses */ + transformRPR(pstate, wc, windef, targetlist); + wc->winref = winref; result = lappend(result, wc); @@ -3820,3 +3831,286 @@ transformFrameOffset(ParseState *pstate, int frameOptions, return node; } + +/* + * transformRPR + * Process Row Pattern Recognition related clauses + */ +static void +transformRPR(ParseState *pstate, WindowClause *wc, WindowDef *windef, + List **targetlist) +{ + /* + * Window definition exists? + */ + if (windef == NULL) + return; + + /* + * Row Pattern Common Syntax clause exists? + */ + if (windef->rpCommonSyntax == NULL) + return; + + /* Check Frame option. Frame must start at current row */ + if ((wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW) == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("FRAME must start at current row when row patttern recognition is used"))); + + /* Transform AFTER MACH SKIP TO clause */ + wc->rpSkipTo = windef->rpCommonSyntax->rpSkipTo; + + /* Transform AFTER MACH SKIP TO variable */ + wc->rpSkipVariable = windef->rpCommonSyntax->rpSkipVariable; + + /* Transform SEEK or INITIAL clause */ + wc->initial = windef->rpCommonSyntax->initial; + + /* Transform DEFINE clause into list of TargetEntry's */ + wc->defineClause = transformDefineClause(pstate, wc, windef, targetlist); + + /* Check PATTERN clause and copy to patternClause */ + transformPatternClause(pstate, wc, windef); + + /* Transform MEASURE clause */ + transformMeasureClause(pstate, wc, windef); +} + +/* + * transformDefineClause Process DEFINE clause and transform ResTarget into + * list of TargetEntry. + * + * XXX we only support column reference in row pattern definition search + * condition, e.g. "price". <row pattern definition variable name>.<column + * reference> is not supported, e.g. "A.price". + */ +static List * +transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef, + List **targetlist) +{ + /* DEFINE variable name initials */ + static char *defineVariableInitials = "abcdefghijklmnopqrstuvwxyz"; + + ListCell *lc, + *l; + ResTarget *restarget, + *r; + List *restargets; + List *defineClause; + char *name; + int initialLen; + int i; + + /* + * If Row Definition Common Syntax exists, DEFINE clause must exist. (the + * raw parser should have already checked it.) + */ + Assert(windef->rpCommonSyntax->rpDefs != NULL); + + /* + * Check and add "A AS A IS TRUE" if pattern variable is missing in DEFINE + * per the SQL standard. + */ + restargets = NIL; + foreach(lc, windef->rpCommonSyntax->rpPatterns) + { + A_Expr *a; + bool found = false; + + if (!IsA(lfirst(lc), A_Expr)) + ereport(ERROR, + errmsg("node type is not A_Expr")); + + a = (A_Expr *) lfirst(lc); + name = strVal(a->lexpr); + + foreach(l, windef->rpCommonSyntax->rpDefs) + { + restarget = (ResTarget *) lfirst(l); + + if (!strcmp(restarget->name, name)) + { + found = true; + break; + } + } + + if (!found) + { + /* + * "name" is missing. So create "name AS name IS TRUE" ResTarget + * node and add it to the temporary list. + */ + A_Const *n; + + restarget = makeNode(ResTarget); + n = makeNode(A_Const); + n->val.boolval.type = T_Boolean; + n->val.boolval.boolval = true; + n->location = -1; + restarget->name = pstrdup(name); + restarget->indirection = NIL; + restarget->val = (Node *) n; + restarget->location = -1; + restargets = lappend((List *) restargets, restarget); + } + } + + if (list_length(restargets) >= 1) + { + /* add missing DEFINEs */ + windef->rpCommonSyntax->rpDefs = + list_concat(windef->rpCommonSyntax->rpDefs, restargets); + list_free(restargets); + } + + /* + * Check for duplicate row pattern definition variables. The standard + * requires that no two row pattern definition variable names shall be + * equivalent. + */ + restargets = NIL; + foreach(lc, windef->rpCommonSyntax->rpDefs) + { + restarget = (ResTarget *) lfirst(lc); + name = restarget->name; + + /* + * Add DEFINE expression (Restarget->val) to the targetlist as a + * TargetEntry if it does not exist yet. Planner will add the column + * ref var node to the outer plan's target list later on. This makes + * DEFINE expression could access the outer tuple while evaluating + * PATTERN. + * + * XXX: adding whole expressions of DEFINE to the plan.targetlist is + * not so good, because it's not necessary to evalute the expression + * in the target list while running the plan. We should extract the + * var nodes only then add them to the plan.targetlist. + */ + findTargetlistEntrySQL99(pstate, (Node *) restarget->val, + targetlist, EXPR_KIND_RPR_DEFINE); + + /* + * Make sure that the row pattern definition search condition is a + * boolean expression. + */ + transformWhereClause(pstate, restarget->val, + EXPR_KIND_RPR_DEFINE, "DEFINE"); + + foreach(l, restargets) + { + char *n; + + r = (ResTarget *) lfirst(l); + n = r->name; + + if (!strcmp(n, name)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("row pattern definition variable name \"%s\" appears more than once in DEFINE clause", + name), + parser_errposition(pstate, exprLocation((Node *) r)))); + } + restargets = lappend(restargets, restarget); + } + list_free(restargets); + + /* + * Create list of row pattern DEFINE variable name's initial. We assign + * [a-z] to them (up to 26 variable names are allowed). + */ + restargets = NIL; + i = 0; + initialLen = strlen(defineVariableInitials); + + foreach(lc, windef->rpCommonSyntax->rpDefs) + { + char initial[2]; + + restarget = (ResTarget *) lfirst(lc); + name = restarget->name; + + if (i >= initialLen) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("number of row pattern definition variable names exceeds %d", + initialLen), + parser_errposition(pstate, + exprLocation((Node *) restarget)))); + } + initial[0] = defineVariableInitials[i++]; + initial[1] = '\0'; + wc->defineInitial = lappend(wc->defineInitial, + makeString(pstrdup(initial))); + } + + defineClause = transformTargetList(pstate, windef->rpCommonSyntax->rpDefs, + EXPR_KIND_RPR_DEFINE); + + /* mark column origins */ + markTargetListOrigins(pstate, defineClause); + + /* mark all nodes in the DEFINE clause tree with collation information */ + assign_expr_collations(pstate, (Node *) defineClause); + + return defineClause; +} + +/* + * transformPatternClause + * Process PATTERN clause and return PATTERN clause in the raw parse tree + */ +static void +transformPatternClause(ParseState *pstate, WindowClause *wc, + WindowDef *windef) +{ + ListCell *lc; + + /* + * Row Pattern Common Syntax clause exists? + */ + if (windef->rpCommonSyntax == NULL) + return; + + wc->patternVariable = NIL; + wc->patternRegexp = NIL; + foreach(lc, windef->rpCommonSyntax->rpPatterns) + { + A_Expr *a; + char *name; + char *regexp; + + if (!IsA(lfirst(lc), A_Expr)) + ereport(ERROR, + errmsg("node type is not A_Expr")); + + a = (A_Expr *) lfirst(lc); + name = strVal(a->lexpr); + + wc->patternVariable = lappend(wc->patternVariable, makeString(pstrdup(name))); + regexp = strVal(lfirst(list_head(a->name))); + + wc->patternRegexp = lappend(wc->patternRegexp, makeString(pstrdup(regexp))); + } +} + +/* + * transformMeasureClause + * Process MEASURE clause + * XXX MEASURE clause is not supported yet + */ +static List * +transformMeasureClause(ParseState *pstate, WindowClause *wc, + WindowDef *windef) +{ + if (windef->rowPatternMeasures == NIL) + return NIL; + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", "MEASURE clause is not supported yet"), + parser_errposition(pstate, exprLocation((Node *) windef->rowPatternMeasures)))); + return NIL; +} diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 36c1b7a88f..fe154bcaa0 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -577,6 +577,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) case EXPR_KIND_COPY_WHERE: case EXPR_KIND_GENERATED_COLUMN: case EXPR_KIND_CYCLE_MARK: + case EXPR_KIND_RPR_DEFINE: /* okay */ break; @@ -1860,6 +1861,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_GENERATED_COLUMN: err = _("cannot use subquery in column generation expression"); break; + case EXPR_KIND_RPR_DEFINE: + err = _("cannot use subquery in DEFINE expression"); + break; /* * There is intentionally no default: case here, so that the @@ -3199,6 +3203,8 @@ ParseExprKindName(ParseExprKind exprKind) return "GENERATED AS"; case EXPR_KIND_CYCLE_MARK: return "CYCLE"; + case EXPR_KIND_RPR_DEFINE: + return "DEFINE"; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 9b23344a3b..4c482abb30 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2658,6 +2658,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) case EXPR_KIND_CYCLE_MARK: errkind = true; break; + case EXPR_KIND_RPR_DEFINE: + errkind = true; + break; /* * There is intentionally no default: case here, so that the -- 2.25.1 ----Next_Part(Thu_Sep_19_13_59_47_2024_608)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v22-0003-Row-pattern-recognition-patch-rewriter.patch" ^ permalink raw reply [nested|flat] 3+ messages in thread
end of thread, other threads:[~2024-09-19 04:48 UTC | newest] Thread overview: 3+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2019-03-20 16:56 [PATCH v5] Simplify immutable RTE_FUNCTION to RTE_RESULT. Alexander Kuzmenkov <[email protected]> 2024-05-16 22:03 Re: commitfest.postgresql.org is no longer fit for purpose Tom Lane <[email protected]> 2024-09-19 04:48 [PATCH v22 2/8] Row pattern recognition patch (parse/analysis). Tatsuo Ishii <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox