From 4b7f8dfbfdefc342718c741416c3804b181aeb2b Mon Sep 17 00:00:00 2001 From: Tatsuo Ishii Date: Thu, 15 Jan 2026 13:26:44 +0900 Subject: [PATCH v38 2/8] Row pattern recognition patch (parse/analysis). --- src/backend/nodes/copyfuncs.c | 39 ++++ src/backend/nodes/equalfuncs.c | 33 +++ src/backend/nodes/outfuncs.c | 51 +++++ src/backend/nodes/readfuncs.c | 79 +++++++ src/backend/parser/Makefile | 1 + src/backend/parser/README | 1 + src/backend/parser/meson.build | 1 + src/backend/parser/parse_agg.c | 7 + src/backend/parser/parse_clause.c | 10 +- src/backend/parser/parse_expr.c | 6 + src/backend/parser/parse_func.c | 3 + src/backend/parser/parse_rpr.c | 340 ++++++++++++++++++++++++++++++ src/include/parser/parse_clause.h | 3 + src/include/parser/parse_rpr.h | 22 ++ 14 files changed, 592 insertions(+), 4 deletions(-) create mode 100644 src/backend/parser/parse_rpr.c create mode 100644 src/include/parser/parse_rpr.h diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index ff22a04abe5..5d69805fc24 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -16,6 +16,7 @@ #include "postgres.h" #include "miscadmin.h" +#include "nodes/plannodes.h" #include "utils/datum.h" @@ -166,6 +167,44 @@ _copyBitmapset(const Bitmapset *from) return bms_copy(from); } +static RPRPattern * +_copyRPRPattern(const RPRPattern *from) +{ + RPRPattern *newnode = makeNode(RPRPattern); + + COPY_SCALAR_FIELD(numVars); + COPY_SCALAR_FIELD(maxDepth); + COPY_SCALAR_FIELD(numElements); + + /* Deep copy the varNames array */ + if (from->numVars > 0) + { + newnode->varNames = palloc0(from->numVars * sizeof(char *)); + for (int i = 0; i < from->numVars; i++) + newnode->varNames[i] = pstrdup(from->varNames[i]); + } + else + { + newnode->varNames = NULL; + } + + /* Deep copy the elements array */ + if (from->numElements > 0) + { + newnode->elements = palloc(from->numElements * sizeof(RPRPatternElement)); + memcpy(newnode->elements, from->elements, + from->numElements * sizeof(RPRPatternElement)); + } + else + { + newnode->elements = NULL; + } + + COPY_SCALAR_FIELD(isAbsorbable); + + return newnode; +} + /* * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3d1a1adf86e..9410790e3d3 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -20,6 +20,7 @@ #include "postgres.h" #include "miscadmin.h" +#include "nodes/plannodes.h" #include "utils/datum.h" @@ -149,6 +150,38 @@ _equalBitmapset(const Bitmapset *a, const Bitmapset *b) return bms_equal(a, b); } +static bool +_equalRPRPattern(const RPRPattern *a, const RPRPattern *b) +{ + COMPARE_SCALAR_FIELD(numVars); + COMPARE_SCALAR_FIELD(maxDepth); + COMPARE_SCALAR_FIELD(numElements); + + /* Compare varNames array */ + if (a->numVars > 0) + { + if (a->varNames == NULL || b->varNames == NULL) + return false; + for (int i = 0; i < a->numVars; i++) + { + if (strcmp(a->varNames[i], b->varNames[i]) != 0) + return false; + } + } + + /* Compare elements array */ + if (a->numElements > 0) + { + if (a->elements == NULL || b->elements == NULL) + return false; + if (memcmp(a->elements, b->elements, + a->numElements * sizeof(RPRPatternElement)) != 0) + return false; + } + + return true; +} + /* * Lists are handled specially */ diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index c8eef2c75d2..1a70b016770 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -23,6 +23,7 @@ #include "nodes/bitmapset.h" #include "nodes/nodes.h" #include "nodes/pg_list.h" +#include "nodes/plannodes.h" #include "utils/datum.h" /* State flag that determines how nodeToStringInternal() should treat location fields */ @@ -718,6 +719,56 @@ _outA_Const(StringInfo str, const A_Const *node) WRITE_LOCATION_FIELD(location); } +static void +_outRPRPattern(StringInfo str, const RPRPattern *node) +{ + WRITE_NODE_TYPE("RPRPATTERN"); + + WRITE_INT_FIELD(numVars); + WRITE_INT_FIELD(maxDepth); + WRITE_INT_FIELD(numElements); + + /* Write varNames array as list of strings */ + appendStringInfoString(str, " :varNames"); + if (node->numVars > 0 && node->varNames != NULL) + { + appendStringInfoString(str, " ("); + for (int i = 0; i < node->numVars; i++) + { + if (i > 0) + appendStringInfoChar(str, ' '); + outToken(str, node->varNames[i]); + } + appendStringInfoChar(str, ')'); + } + else + appendStringInfoString(str, " <>"); + + /* Write elements array */ + appendStringInfoString(str, " :elements"); + if (node->numElements > 0 && node->elements != NULL) + { + appendStringInfoChar(str, ' '); + for (int i = 0; i < node->numElements; i++) + { + const RPRPatternElement *elem = &node->elements[i]; + + appendStringInfo(str, "(%d %u %d %d %d %d %d)", + (int) elem->varId, + (unsigned) elem->flags, + (int) elem->depth, + (int) elem->min, + (int) elem->max, + (int) elem->next, + (int) elem->jump); + } + } + else + appendStringInfoString(str, " <>"); + + WRITE_BOOL_FIELD(isAbsorbable); +} + /* * outNode - diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index c11728c0f17..3ed31ba539d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -30,6 +30,7 @@ #include "miscadmin.h" #include "nodes/bitmapset.h" +#include "nodes/plannodes.h" #include "nodes/readfuncs.h" @@ -560,6 +561,84 @@ _readExtensibleNode(void) READ_DONE(); } +static RPRPattern * +_readRPRPattern(void) +{ + READ_LOCALS(RPRPattern); + + READ_INT_FIELD(numVars); + READ_INT_FIELD(maxDepth); + READ_INT_FIELD(numElements); + + /* Read varNames array */ + token = pg_strtok(&length); /* skip :varNames */ + token = pg_strtok(&length); /* get '(' or '<>' */ + if (local_node->numVars > 0 && token[0] == '(') + { + local_node->varNames = palloc(local_node->numVars * sizeof(char *)); + for (int i = 0; i < local_node->numVars; i++) + { + token = pg_strtok(&length); + local_node->varNames[i] = debackslash(token, length); + } + token = pg_strtok(&length); /* skip ')' */ + } + else + { + local_node->varNames = NULL; + } + + /* Read elements array */ + token = pg_strtok(&length); /* skip :elements */ + token = pg_strtok(&length); /* get '(' or '<>' */ + if (local_node->numElements > 0 && token[0] == '(') + { + local_node->elements = palloc0(local_node->numElements * sizeof(RPRPatternElement)); + for (int i = 0; i < local_node->numElements; i++) + { + RPRPatternElement *elem = &local_node->elements[i]; + int varId, flags, depth, min, max, next, jump; + + /* Parse "(varId flags depth min max next jump)" */ + token = pg_strtok(&length); + varId = atoi(token); + token = pg_strtok(&length); + flags = atoi(token); + token = pg_strtok(&length); + depth = atoi(token); + token = pg_strtok(&length); + min = atoi(token); + token = pg_strtok(&length); + max = atoi(token); + token = pg_strtok(&length); + next = atoi(token); + token = pg_strtok(&length); + jump = atoi(token); + token = pg_strtok(&length); /* skip ')' */ + + elem->varId = (RPRVarId) varId; + elem->flags = (RPRElemFlags) flags; + elem->depth = (RPRDepth) depth; + elem->min = (RPRQuantity) min; + elem->max = (RPRQuantity) max; + elem->next = (RPRElemIdx) next; + elem->jump = (RPRElemIdx) jump; + + /* Read next element's '(' or end */ + if (i < local_node->numElements - 1) + token = pg_strtok(&length); /* get '(' */ + } + } + else + { + local_node->elements = NULL; + } + + READ_BOOL_FIELD(isAbsorbable); + + READ_DONE(); +} + /* * parseNodeString diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile index 8c0fe28d63f..f4c7d605fe3 100644 --- a/src/backend/parser/Makefile +++ b/src/backend/parser/Makefile @@ -29,6 +29,7 @@ OBJS = \ parse_oper.o \ parse_param.o \ parse_relation.o \ + parse_rpr.o \ parse_target.o \ parse_type.o \ parse_utilcmd.o \ diff --git a/src/backend/parser/README b/src/backend/parser/README index e26eb437a9f..2baffa9517e 100644 --- a/src/backend/parser/README +++ b/src/backend/parser/README @@ -26,6 +26,7 @@ parse_node.c create nodes for various structures parse_oper.c handle operators in expressions parse_param.c handle Params (for the cases used in the core backend) parse_relation.c support routines for tables and column handling +parse_rpr.c handle Row Pattern Recognition parse_target.c handle the result list of the query parse_type.c support routines for data type handling parse_utilcmd.c parse analysis for utility commands (done at execution time) diff --git a/src/backend/parser/meson.build b/src/backend/parser/meson.build index 924ee87a453..a07102b37a0 100644 --- a/src/backend/parser/meson.build +++ b/src/backend/parser/meson.build @@ -16,6 +16,7 @@ backend_sources += files( 'parse_oper.c', 'parse_param.c', 'parse_relation.c', + 'parse_rpr.c', 'parse_target.c', 'parse_type.c', 'parse_utilcmd.c', diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 25ee0f87d93..5ed785ea0d5 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -584,6 +584,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 @@ -1023,6 +1027,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 e35fd25c9bb..f98ac7cfcb5 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -37,6 +37,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" +#include "parser/parse_rpr.h" #include "parser/parse_target.h" #include "parser/parse_type.h" #include "parser/parser.h" @@ -84,8 +85,6 @@ static void checkExprIsVarFree(ParseState *pstate, Node *n, const char *constructName); static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, ParseExprKind exprKind); -static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node, - List **tlist, ParseExprKind exprKind); static int get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs); static List *resolve_unique_index_expr(ParseState *pstate, InferClause *infer, @@ -97,7 +96,6 @@ static Node *transformFrameOffset(ParseState *pstate, int frameOptions, Oid rangeopfamily, Oid rangeopcintype, Oid *inRangeFunc, Node *clause); - /* * transformFromClause - * Process the FROM clause and add items to the query's range table, @@ -2168,7 +2166,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, * tlist the target list (passed by reference so we can append to it) * exprKind identifies clause type being processed */ -static TargetEntry * +TargetEntry * findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist, ParseExprKind exprKind) { @@ -3019,6 +3017,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); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 56826db4c26..7ebcd15fa39 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; @@ -1862,6 +1863,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 @@ -3221,6 +3225,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 24f6745923b..357b236a818 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2783,6 +2783,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 diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c new file mode 100644 index 00000000000..de648293b27 --- /dev/null +++ b/src/backend/parser/parse_rpr.c @@ -0,0 +1,340 @@ +/*------------------------------------------------------------------------- + * + * parse_rpr.c + * handle Row Pattern Recognition in parser + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/parser/parse_rpr.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "parser/parse_clause.h" +#include "parser/parse_collate.h" +#include "parser/parse_expr.h" +#include "parser/parse_rpr.h" +#include "parser/parse_target.h" + +/* DEFINE variable name initials */ +static const char *defineVariableInitials = "abcdefghijklmnopqrstuvwxyz"; + +/* Forward declarations */ +static void collectRPRPatternVarNames(RPRPatternNode *node, List **varNames); +static List *transformDefineClause(ParseState *pstate, WindowClause *wc, + WindowDef *windef, List **targetlist); +static void transformPatternClause(ParseState *pstate, WindowClause *wc, + WindowDef *windef); +static RPRPatternNode *copyRPRPatternNode(RPRPatternNode *node); + +/* + * transformRPR + * Process Row Pattern Recognition related clauses + */ +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 pattern recognition is used"))); + + /* Transform AFTER MATCH SKIP TO clause */ + wc->rpSkipTo = windef->rpCommonSyntax->rpSkipTo; + + /* 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); +} + +/* + * collectRPRPatternVarNames + * Collect all variable names from RPRPatternNode tree. + */ +static void +collectRPRPatternVarNames(RPRPatternNode *node, List **varNames) +{ + ListCell *lc; + + if (node == NULL) + return; + + switch (node->nodeType) + { + case RPR_PATTERN_VAR: + /* Add variable name if not already in list */ + { + bool found = false; + + foreach(lc, *varNames) + { + if (strcmp(strVal(lfirst(lc)), node->varName) == 0) + { + found = true; + break; + } + } + if (!found) + *varNames = lappend(*varNames, makeString(pstrdup(node->varName))); + } + break; + + case RPR_PATTERN_SEQ: + case RPR_PATTERN_ALT: + case RPR_PATTERN_GROUP: + /* Recurse into children */ + foreach(lc, node->children) + { + collectRPRPatternVarNames((RPRPatternNode *) lfirst(lc), varNames); + } + break; + } +} + +/* + * 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". . is not supported, e.g. "A.price". + */ +static List * +transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef, + List **targetlist) +{ + ListCell *lc, + *l; + ResTarget *restarget, + *r; + List *restargets; + List *defineClause; + char *name; + int initialLen; + int numinitials; + List *patternVarNames = NIL; + + /* + * If Row Definition Common Syntax exists, DEFINE clause must exist. (the + * raw parser should have already checked it.) + */ + Assert(windef->rpCommonSyntax->rpDefs != NULL); + + /* + * Collect all pattern variable names from the pattern tree. + */ + collectRPRPatternVarNames(windef->rpCommonSyntax->rpPattern, &patternVarNames); + + /* + * Check and add "A AS A IS TRUE" if pattern variable is missing in DEFINE + * per the SQL standard. + */ + restargets = NIL; + foreach(lc, patternVarNames) + { + bool found = false; + + name = strVal(lfirst(lc)); + + 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; + numinitials = 0; + initialLen = strlen(defineVariableInitials); + foreach(lc, windef->rpCommonSyntax->rpDefs) + { + char initial[2]; + + 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)))); + } + + /* + * Create list of row pattern DEFINE variable name's initial. We + * assign [a-z] to them (up to 26 variable names are allowed). + */ + if (numinitials >= 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[numinitials++]; + initial[1] = '\0'; + wc->defineInitial = lappend(wc->defineInitial, + makeString(pstrdup(initial))); + + restargets = lappend(restargets, restarget); + } + list_free(restargets); + + /* turns a list of ResTarget's into a list of TargetEntry's */ + 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 + * + * windef->rpCommonSyntax must exist. + * + * The original (unoptimized) pattern is stored for deparsing (pg_get_viewdef). + * Optimization happens later in the planner phase (buildRPRPattern). + */ +static void +transformPatternClause(ParseState *pstate, WindowClause *wc, + WindowDef *windef) +{ + Assert(windef->rpCommonSyntax != NULL); + + /* Store original AST for deparsing (no optimization here) */ + wc->rpPattern = copyRPRPatternNode(windef->rpCommonSyntax->rpPattern); +} + +/* + * copyRPRPatternNode + * Deep copy an RPRPatternNode tree. + */ +static RPRPatternNode * +copyRPRPatternNode(RPRPatternNode *node) +{ + RPRPatternNode *copy; + ListCell *lc; + + if (node == NULL) + return NULL; + + copy = makeNode(RPRPatternNode); + copy->nodeType = node->nodeType; + copy->varName = node->varName ? pstrdup(node->varName) : NULL; + copy->min = node->min; + copy->max = node->max; + copy->reluctant = node->reluctant; + copy->children = NIL; + + foreach(lc, node->children) + { + copy->children = lappend(copy->children, + copyRPRPatternNode(lfirst(lc))); + } + + return copy; +} diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index fe234611007..8aaac881f2b 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -52,6 +52,9 @@ extern List *addTargetToSortList(ParseState *pstate, TargetEntry *tle, extern Index assignSortGroupRef(TargetEntry *tle, List *tlist); extern bool targetIsInSortList(TargetEntry *tle, Oid sortop, List *sortList); +extern TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node, + List **tlist, ParseExprKind exprKind); + /* functions in parse_jsontable.c */ extern ParseNamespaceItem *transformJsonTable(ParseState *pstate, JsonTable *jt); diff --git a/src/include/parser/parse_rpr.h b/src/include/parser/parse_rpr.h new file mode 100644 index 00000000000..40e0258d2e6 --- /dev/null +++ b/src/include/parser/parse_rpr.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * parse_rpr.h + * handle Row Pattern Recognition in parser + * + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/parser/parse_rpr.h + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_RPR_H +#define PARSE_RPR_H + +#include "parser/parse_node.h" + +extern void transformRPR(ParseState *pstate, WindowClause *wc, + WindowDef *windef, List **targetlist); + +#endif /* PARSE_RPR_H */ -- 2.43.0