recursive-backtrack.diff.txt

text/plain

Filename: recursive-backtrack.diff.txt
Type: text/plain
Part: 0
Message: Re: Row pattern recognition
From fb3cbb6f99f0fe7b05027759454d7e0013225929 Mon Sep 17 00:00:00 2001
From: Jacob Champion <champion.p@gmail.com>
Date: Fri, 20 Oct 2023 16:11:14 -0700
Subject: [PATCH 2/2] squash! Row pattern recognition patch (executor).

- Extract pattern matching logic into match_pattern().
- Replace the str_set/initials implementation with a recursive
  backtracker.
- Rework match_pattern in preparation for * quantifier: separate success
  from number of matched rows.
- Handle `*` quantifier
- Simplify the evaluate_pattern() signature.
- Remove defineInitial and related code.
---
 src/backend/executor/nodeWindowAgg.c    | 871 +++---------------------
 src/backend/optimizer/plan/createplan.c |   4 -
 src/backend/parser/parse_clause.c       |  31 -
 src/include/nodes/execnodes.h           |   1 -
 src/include/nodes/parsenodes.h          |   2 -
 src/include/nodes/plannodes.h           |   3 -
 6 files changed, 91 insertions(+), 821 deletions(-)

diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 2e1baef7ea..30abe60159 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -161,40 +161,6 @@ typedef struct WindowStatePerAggData
 	bool		restart;		/* need to restart this agg in this cycle? */
 } WindowStatePerAggData;
 
-/*
- * Set of StringInfo. Used in RPR.
- */
-typedef struct StringSet {
-	StringInfo	*str_set;
-	Size		set_size;	/* current array allocation size in number of items */
-	int			set_index;	/* current used size */
-} StringSet;
-
-/*
- * Allowed subsequent PATTERN variables positions.
- * Used in RPR.
- *
- * pos represents the pattern variable defined order in DEFINE caluase.  For
- * example. "DEFINE START..., UP..., DOWN ..." and "PATTERN START UP DOWN UP"
- * will create:
- * VariablePos[0].pos[0] = 0;		START
- * VariablePos[1].pos[0] = 1;		UP
- * VariablePos[1].pos[1] = 3;		UP
- * VariablePos[2].pos[0] = 2;		DOWN
- *
- * Note that UP has two pos because UP appears in PATTERN twice.
- *
- * By using this strucrture, we can know which pattern variable can be followed
- * by which pattern variable(s). For example, START can be followed by UP and
- * DOWN since START's pos is 0, and UP's pos is 1 or 3, DOWN's pos is 2.
- * DOWN can be followed by UP since UP's pos is either 1 or 3. 
- * 
- */
-#define NUM_ALPHABETS	26	/* we allow [a-z] variable initials */
-typedef struct VariablePos {
-	int			pos[NUM_ALPHABETS];	/* postion(s) in PATTERN */
-} VariablePos;
-
 static void initialize_windowaggregate(WindowAggState *winstate,
 									   WindowStatePerFunc perfuncstate,
 									   WindowStatePerAgg peraggstate);
@@ -249,27 +215,11 @@ static void register_reduced_frame_map(WindowAggState *winstate, int64 pos, int
 static void clear_reduced_frame_map(WindowAggState *winstate);
 static void update_reduced_frame(WindowObject winobj, int64 pos);
 
-static int64 evaluate_pattern(WindowObject winobj, int64 current_pos,
-							  char *vname, StringInfo encoded_str, bool *result);
+static bool evaluate_pattern(WindowObject winobj, int64 current_pos,
+							 char *vname);
 
 static bool get_slots(WindowObject winobj, int64 current_pos);
 
-//static int	search_str_set(char *pattern, StringSet *str_set, int *initial_orders);
-static int search_str_set(char *pattern, StringSet *str_set, VariablePos *variable_pos);
-static char	pattern_initial(WindowAggState *winstate, char *vname);
-static int do_pattern_match(char *pattern, char *encoded_str);
-
-static StringSet *string_set_init(void);
-static void string_set_add(StringSet *string_set, StringInfo str);
-static StringInfo string_set_get(StringSet *string_set, int index);
-static int string_set_get_size(StringSet *string_set);
-static void string_set_discard(StringSet *string_set);
-static VariablePos *variable_pos_init(void);
-static void variable_pos_register(VariablePos *variable_pos, char initial, int pos);
-static bool variable_pos_compare(VariablePos *variable_pos, char initial1, char initial2);
-static int variable_pos_fetch(VariablePos *variable_pos, char initial, int index);
-static void variable_pos_discard(VariablePos *variable_pos);
-
 /*
  * initialize_windowaggregate
  * parallel to initialize_aggregates in nodeAgg.c
@@ -2839,7 +2789,6 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	winstate->patternRegexpList = node->patternRegexp;
 
 	/* Set up row pattern recognition DEFINE clause */
-	winstate->defineInitial = node->defineInitial;
 	winstate->defineVariableList = NIL;
 	winstate->defineClauseList = NIL;
 	if (node->defineClause != NIL)
@@ -4063,145 +4012,75 @@ void register_reduced_frame_map(WindowAggState *winstate, int64 pos, int val)
 }
 
 /*
- * update_reduced_frame
- *		Update reduced frame info.
+ * Match the pattern variables in the WindowObject against the rows at a given
+ * position. Returns true if the pattern matches successfully, and stores the
+ * number of matched rows. (*rows may be zero on success; consider for example
+ * the pattern `A*`.)
  */
-static
-void update_reduced_frame(WindowObject winobj, int64 pos)
+static bool
+match_pattern(WindowObject winobj, int64 pos, int pattern_index, int *rows)
 {
 	WindowAggState *winstate = winobj->winstate;
-	ListCell	*lc1, *lc2;
-	bool		expression_result;
-	int			num_matched_rows;
-	int64		original_pos;
-	bool		anymatch;
-	StringInfo	encoded_str;
-	StringInfo	pattern_str = makeStringInfo();
-	StringSet	*str_set;
-	int			str_set_index = 0;
-	int			initial_index;
-	VariablePos	*variable_pos;
+	int			submatch_rows = 0;
 
-	/*
-	 * Set of pattern variables evaluated to true.
-	 * Each character corresponds to pattern variable.
-	 * Example:
-	 * str_set[0] = "AB";
-	 * str_set[1] = "AC";
-	 * In this case at row 0 A and B are true, and A and C are true in row 1.
-	 */
+	char	   *vname = strVal(list_nth(winstate->patternVariableList, pattern_index));
+	char	   *quantifier = strVal(list_nth(winstate->patternRegexpList, pattern_index));
 
-	/* initialize pattern variables set */
-	str_set = string_set_init();
+	*rows = 0;
 
-	/* save original pos */
-	original_pos = pos;
-
-	/*
-	 * Loop over until none of pattern matches or encounters end of frame.
-	 */
-	for (;;)
+	/* Try to match the current row against the pattern variable. */
+	if (!evaluate_pattern(winobj, pos, vname))
 	{
-		int64	result_pos = -1;
-
-		/*
-		 * Loop over each PATTERN variable.
-		 */
-		anymatch = false;
-		encoded_str = makeStringInfo();
-
-		forboth(lc1, winstate->patternVariableList, lc2, winstate->patternRegexpList)
-		{
-			char	*vname = strVal(lfirst(lc1));
-			char	*quantifier = strVal(lfirst(lc2));
-
-			elog(DEBUG1, "pos: " INT64_FORMAT " pattern vname: %s quantifier: %s", pos, vname, quantifier);
-
-			expression_result = false;
-
-			/* evaluate row pattern against current row */
-			result_pos = evaluate_pattern(winobj, pos, vname, encoded_str, &expression_result);
-			if (expression_result)
-			{
-				elog(DEBUG1, "expression result is true");
-				anymatch = true;
-			}
-
-			/*
-			 * If out of frame, we are done.
-			 */
-			 if (result_pos < 0)
-				 break;
-		}
-
-		if (!anymatch)
-		{
-			/* none of patterns matched. */
-			break;
-		}
-
-		string_set_add(str_set, encoded_str);
-
-		elog(DEBUG1, "pos: " INT64_FORMAT " str_set_index: %d encoded_str: %s", pos, str_set_index, encoded_str->data);
-
-		/* move to next row */
+		/* A failed match is only okay if we have the proper quantifier. */
+		if (quantifier[0] != '*')
+			return false;
+	}
+	else
+	{
+		/* Matched. Advance to the next row. */
+		(*rows)++;
 		pos++;
 
-		if (result_pos < 0)
+		if (quantifier[0] == '+' || quantifier[0] == '*')
 		{
-			/* out of frame */
-			break;
+			/* Greedy. Try to rematch from the current variable first. */
+			if (match_pattern(winobj, pos, pattern_index, &submatch_rows))
+				goto success;
 		}
 	}
 
-	if (string_set_get_size(str_set) == 0)
+	/* If there's more to the pattern, try to match it now. */
+	if (pattern_index + 1 < list_length(winstate->patternVariableList))
 	{
-		/* no match found in the first row */
-		register_reduced_frame_map(winstate, original_pos, RF_UNMATCHED);
-		return;
-	}
-
-	elog(DEBUG2, "pos: " INT64_FORMAT " encoded_str: %s", pos, encoded_str->data);
-
-	/* build regular expression */
-	pattern_str = makeStringInfo();
-	appendStringInfoChar(pattern_str, '^');
-	initial_index = 0;
-
-	variable_pos = variable_pos_init();
-
-	forboth (lc1, winstate->patternVariableList, lc2, winstate->patternRegexpList)
-	{
-		char	*vname = strVal(lfirst(lc1));
-		char	*quantifier = strVal(lfirst(lc2));
-		char	 initial;
-
-		initial = pattern_initial(winstate, vname);
-		Assert(initial != 0);
-		appendStringInfoChar(pattern_str, initial);
-		if (quantifier[0])
-			appendStringInfoChar(pattern_str, quantifier[0]);
+		if (match_pattern(winobj, pos, pattern_index + 1, &submatch_rows))
+			goto success;
 
 		/*
-		 * Register the initial at initial_index. If the initial appears more
-		 * than once, all of it's initial_index will be recorded. This could
-		 * happen if a pattern variable appears in the PATTERN clause more
-		 * than once like "UP DOWN UP" "UP UP UP".
+		 * We're not to the end of the pattern, and there's no way to advance
+		 * the machine, so fail.
+		 *
+		 * TODO: reluctant qualifiers add another possible transition here
 		 */
-		variable_pos_register(variable_pos, initial, initial_index);
-
-		initial_index++;
+		*rows = 0;
+		return false;
 	}
 
-	elog(DEBUG2, "pos: " INT64_FORMAT " pattern: %s", pos, pattern_str->data);
+success:
+	*rows += submatch_rows;
+	return true;
+}
 
-	/* look for matching pattern variable sequence */
-	elog(DEBUG1, "search_str_set started");
-	num_matched_rows = search_str_set(pattern_str->data, str_set, variable_pos);
-	elog(DEBUG1, "search_str_set returns: %d", num_matched_rows);
+/*
+ * update_reduced_frame
+ *		Update reduced frame info.
+ */
+static
+void update_reduced_frame(WindowObject winobj, int64 pos)
+{
+	WindowAggState *winstate = winobj->winstate;
+	int			num_matched_rows;
 
-	variable_pos_discard(variable_pos);
-	string_set_discard(str_set);
+	match_pattern(winobj, pos, 0, &num_matched_rows);
 
 	/*
 	 * We are at the first row in the reduced frame.  Save the number of
@@ -4210,15 +4089,15 @@ void update_reduced_frame(WindowObject winobj, int64 pos)
 	if (num_matched_rows <= 0)
 	{
 		/* no match */
-		register_reduced_frame_map(winstate, original_pos, RF_UNMATCHED);
+		register_reduced_frame_map(winstate, pos, RF_UNMATCHED);
 	}
 	else
 	{
 		int64		i;
 
-		register_reduced_frame_map(winstate, original_pos, RF_FRAME_HEAD);
+		register_reduced_frame_map(winstate, pos, RF_FRAME_HEAD);
 
-		for (i = original_pos + 1; i < original_pos + num_matched_rows; i++)
+		for (i = pos + 1; i < pos + num_matched_rows; i++)
 		{
 			register_reduced_frame_map(winstate, i, RF_SKIPPED);
 		}
@@ -4228,436 +4107,76 @@ void update_reduced_frame(WindowObject winobj, int64 pos)
 }
 
 /*
- * Perform pattern matching using pattern against str_set. pattern is a
- * regular expression derived from PATTERN clause. Note that the regular
- * expression string is prefixed by '^' and followed by initials represented
- * in a same way as str_set. str_set is a set of StringInfo. Each StringInfo
- * has a string comprising initials of pattern variable strings being true in
- * a row. The initials are one of [a-y], parallel to the order of variable
- * names in DEFINE clause. For example, if DEFINE has variables START, UP and
- * DOWN, PATTERN HAS START, UP and DOWN, then the initials in PATTERN will be
- * 'a', 'b' and 'c'.
- *
- * variable_pos is an array representing the order of pattern variable string
- * initials in PATTERN clause.  For example initial 'a' potion is in
- * variable_pos[0].pos[0] = 0. Note that if the pattern is "START UP DOWN UP"
- * (UP appears twice), then "UP" (initial is 'b') has two position 1 and
- * 3. Thus variable_pos for b is variable_pos[1].pos[0] = 1 and
- * variable_pos[1].pos[1] = 3.
- *
- * Returns the longest number of the matching rows.
+ * Evaluate expression associated with PATTERN variable vname for the given row
+ * position, and return the result.
  */
-static
-int search_str_set(char *pattern, StringSet *str_set, VariablePos *variable_pos)
-{
-#define	MAX_CANDIDATE_NUM	10000	/* max pattern match candidate size */
-#define	FREEZED_CHAR	'Z'	/* a pattern is freezed if it ends with the char */
-#define	DISCARD_CHAR	'z'	/* a pattern is not need to keep */
-
-	int			set_size;	/* number of rows in the set */
-	int			resultlen;
-	int			index;
-	StringSet	*old_str_set, *new_str_set;
-	int			new_str_size;
-	int			len;
-
-	set_size = string_set_get_size(str_set);
-	new_str_set = string_set_init();
-	len = 0;
-	resultlen = 0;
-
-	/*
-	 * Generate all possible pattern variable name initials as a set of
-	 * StringInfo named "new_str_set".  For example, if we have two rows
-	 * having "ab" (row 0) and "ac" (row 1) in the input str_set, new_str_set
-	 * will have set of StringInfo "aa", "ac", "ba" and "bc" in the end.
-	 */
-	elog(DEBUG1, "pattern: %s set_size: %d", pattern, set_size);
-	for (index = 0; index < set_size; index++)
-	{
-		StringInfo	str;	/* search target row */
-		char	*p;
-		int		old_set_size;
-		int		i;
-
-		elog(DEBUG1, "index: %d", index);
-
-		if (index == 0)
-		{
-			/* copy variables in row 0 */
-			str = string_set_get(str_set, index);
-			p = str->data;
-
-			/*
-			 * Loop over each new pattern variable char.
-			 */
-			while (*p)
-			{
-				StringInfo	new = makeStringInfo();
-
-				/* add pattern variable char */
-				appendStringInfoChar(new, *p);
-				/* add new one to string set */
-				string_set_add(new_str_set, new);
-				elog(DEBUG1, "old_str: NULL new_str: %s", new->data);
-				p++;	/* next pattern variable */
-			}
-		}
-		else	/* index != 0 */
-		{
-			old_str_set = new_str_set;
-			new_str_set = string_set_init();
-			str = string_set_get(str_set, index);
-			old_set_size = string_set_get_size(old_str_set);
-
-			/*
-			 * Loop over each rows in the previous result set.
-			 */
-			for (i = 0; i < old_set_size ; i++)
-			{
-				StringInfo	new;
-				char	last_old_char;
-				int		old_str_len;
-				StringInfo	old = string_set_get(old_str_set, i);
-
-				p = old->data;
-				old_str_len = strlen(p);
-				if (old_str_len > 0)
-					last_old_char = p[old_str_len - 1];
-				else
-					last_old_char = '\0';
-
-				/* Is this old set freezed? */
-				if (last_old_char == FREEZED_CHAR)
-				{
-					/* if shorter match. we can discard it */
-					if ((old_str_len - 1) < resultlen)
-					{
-						elog(DEBUG1, "discard this old set because shorter match: %s", old->data);
-						continue;
-					}
-
-					elog(DEBUG1, "keep this old set: %s", old->data);
-					new = makeStringInfo();
-					appendStringInfoString(new, old->data);
-					string_set_add(new_str_set, new);
-					continue;
-				}
-				/* Can this old set be discarded? */
-				else if (last_old_char == DISCARD_CHAR)
-				{
-					elog(DEBUG1, "discard this old set: %s", old->data);
-					continue;
-				}
-
-				elog(DEBUG1, "str->data: %s", str->data);
-
-				/*
-				 * loop over each pattern variable initial char in the input
-				 * set.
-				 */
-				for (p = str->data; *p; p++)
-				{
-					/*
-					 * Optimization.  Check if the row's pattern variable
-					 * initial character position is greater than or equal to
-					 * the old set's last pattern variable initial character
-					 * position. For example, if the old set's last pattern
-					 * variable initials are "ab", then the new pattern
-					 * variable initial can be "b" or "c" but can not be "a",
-					 * if the initials in PATTERN is something like "a b c" or
-					 * "a b+ c+" etc.  This optimization is possible when we
-					 * only allow "+" quantifier.
-					 */
-					if (variable_pos_compare(variable_pos, last_old_char, *p))
-					{						
-						/* copy source string */
-						new = makeStringInfo();
-						appendStringInfoString(new, old->data);
-						/* add pattern variable char */
-						appendStringInfoChar(new, *p);
-						elog(DEBUG1, "old_str: %s new_str: %s", old->data, new->data);
-
-						/*
-						 * Adhoc optimization. If the first letter in the
-						 * input string is the first and second position one
-						 * and there's no associated quatifier '+', then we
-						 * can dicard the input because there's no chace to
-						 * expand the string further.
-						 *
-						 * For example, pattern "abc" cannot match "aa".
-						 */
-						elog(DEBUG1, "pattern[1]:%c pattern[2]:%c new[0]:%c new[1]:%c",
-							 pattern[1], pattern[2], new->data[0], new->data[1]);
-						if (pattern[1] == new->data[0] &&
-							pattern[1] == new->data[1] &&
-							pattern[2] != '+' &&
-							pattern[1] != pattern[2])
-						{
-							elog(DEBUG1, "discard this new data: %s",
-								new->data);
-							pfree(new->data);
-							pfree(new);
-							continue;
-						}
-
-						/* add new one to string set */
-						string_set_add(new_str_set, new);
-					}
-					else
-					{
-						/*
-						 * We are freezing this pattern string.  Since there's
-						 * no chance to expand the string further, we perform
-						 * pattern matching against the string. If it does not
-						 * match, we can discard it.
-						 */
-						len = do_pattern_match(pattern, old->data);
-
-						if (len <= 0)
-						{
-							/* no match. we can discard it */
-							continue;
-						}
-
-						else if (len <= resultlen)
-						{
-							/* shorter match. we can discard it */
-							continue;
-						}
-						else
-						{
-							/* match length is the longest so far */
-
-							int		new_index;
-
-							/* remember the longest match */
-							resultlen = len;
-
-							/* freeze the pattern string */
-							new = makeStringInfo();
-							appendStringInfoString(new, old->data);
-							/* add freezed mark */
-							appendStringInfoChar(new, FREEZED_CHAR);
-							elog(DEBUG1, "old_str: %s new_str: %s", old->data, new->data);
-							string_set_add(new_str_set, new);
-
-							/*
-							 * Search new_str_set to find out freezed
-							 * entries that have shorter match length.
-							 * Mark them as "discard" so that they are
-							 * discarded in the next round.
-							 */
-
-							/* new_index_size should be the one before */
-							new_str_size = string_set_get_size(new_str_set) - 1;
-
-							/* loop over new_str_set */
-							for (new_index = 0; new_index < new_str_size; new_index++)
-							{
-								char	new_last_char;
-								int		new_str_len;
-
-								new = string_set_get(new_str_set, new_index);
-								new_str_len = strlen(new->data);
-								if (new_str_len > 0)
-								{
-									new_last_char = new->data[new_str_len - 1];
-									if (new_last_char == FREEZED_CHAR &&
-										(new_str_len - 1) <= len)
-									{
-										/* mark this set to discard in the next round */
-										appendStringInfoChar(new, DISCARD_CHAR);
-										elog(DEBUG1, "add discard char: %s", new->data);
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-			/* we no longer need old string set */
-			string_set_discard(old_str_set);
-		}
-	}
-
-	/*
-	 * Perform pattern matching to find out the longest match.
-	 */
-	new_str_size = string_set_get_size(new_str_set);
-	elog(DEBUG1, "new_str_size: %d", new_str_size);
-	len = 0;
-	resultlen = 0;
-
-	for (index = 0; index < new_str_size; index++)
-	{
-		StringInfo	s;
-
-		s = string_set_get(new_str_set, index);
-		if (s == NULL)
-			continue;	/* no data */
-
-		elog(DEBUG1, "target string: %s", s->data);
-
-		len = do_pattern_match(pattern, s->data);
-		if (len > resultlen)
-		{
-			/* remember the longest match */
-			resultlen = len;
-
-			/*
-			 * If the size of result set is equal to the number of rows in the
-			 * set, we are done because it's not possible that the number of
-			 * matching rows exceeds the number of rows in the set.
-			 */
-			if (resultlen >= set_size)
-				break;
-		}
-	}
-
-	/* we no longer need new string set */
-	string_set_discard(new_str_set);
-
-	return resultlen;
-}
-
-/*
- * do_pattern_match
- * perform pattern match using pattern against encoded_str.
- * returns matching number of rows if matching is succeeded.
- * Otherwise returns 0.
- */
-static
-int do_pattern_match(char *pattern, char *encoded_str)
-{
-	Datum	d;
-	text	*res;
-	char	*substr;
-	int		len = 0;
-	text	*pattern_text, *encoded_str_text;
-
-	pattern_text = cstring_to_text(pattern);
-	encoded_str_text = cstring_to_text(encoded_str);
-
-	/*
-	 * We first perform pattern matching using regexp_instr, then call
-	 * textregexsubstr to get matched substring to know how long the
-	 * matched string is. That is the number of rows in the reduced window
-	 * frame.  The reason why we can't call textregexsubstr in the first
-	 * place is, it errors out if pattern does not match.
-	 */
-	if (DatumGetInt32(DirectFunctionCall2Coll(regexp_instr, DEFAULT_COLLATION_OID,
-											  PointerGetDatum(encoded_str_text),
-											  PointerGetDatum(pattern_text))))
-	{
-		d = DirectFunctionCall2Coll(textregexsubstr,
-									DEFAULT_COLLATION_OID,
-									PointerGetDatum(encoded_str_text),
-									PointerGetDatum(pattern_text));
-		if (d != 0)
-		{
-			res = DatumGetTextPP(d);
-			substr = text_to_cstring(res);
-			len = strlen(substr);
-			pfree(substr);
-		}
-	}
-	pfree(encoded_str_text);
-	pfree(pattern_text);
-
-	return len;
-}
-
-
-/*
- * Evaluate expression associated with PATTERN variable vname.
- * relpos is relative row position in a frame (starting from 0).
- * "quantifier" is the quatifier part of the PATTERN regular expression.
- * Currently only '+' is allowed.
- * result is out paramater representing the expression evaluation result
- * is true of false.
- * Return values are:
- * >=0: the last match absolute row position
- * other wise out of frame.
- */
-static
-int64 evaluate_pattern(WindowObject winobj, int64 current_pos,
-						char *vname, StringInfo encoded_str, bool *result)
+static bool
+evaluate_pattern(WindowObject winobj, int64 current_pos, char *vname)
 {
 	WindowAggState	*winstate = winobj->winstate;
 	ExprContext		*econtext = winstate->ss.ps.ps_ExprContext;
-	ListCell		*lc1, *lc2, *lc3;
+	ListCell		*lc1, *lc2;
 	ExprState		*pat;
 	Datum			eval_result;
-	bool			out_of_frame = false;
 	bool			isnull;
 	TupleTableSlot *slot;
+	bool			result;
 
-	forthree (lc1, winstate->defineVariableList, lc2, winstate->defineClauseList, lc3, winstate->defineInitial)
+	forboth (lc1, winstate->defineVariableList, lc2, winstate->defineClauseList)
 	{
-		char	initial;
 		char	*name = strVal(lfirst(lc1));
 
 		if (strcmp(vname, name))
 			continue;
 
-		initial = *(strVal(lfirst(lc3)));
-
 		/* set expression to evaluate */
 		pat = lfirst(lc2);
 
 		/* get current, previous and next tuples */
 		if (!get_slots(winobj, current_pos))
 		{
-			out_of_frame = true;
+			/* out of frame */
+			return false;
+		}
+
+		/* evaluate the expression */
+		eval_result = ExecEvalExpr(pat, econtext, &isnull);
+		if (isnull)
+		{
+			/* expression is NULL */
+			elog(DEBUG1, "expression for %s is NULL at row: " INT64_FORMAT, vname, current_pos);
+			result = false;
 		}
 		else
 		{
-			/* evaluate the expression */
-			eval_result = ExecEvalExpr(pat, econtext, &isnull);
-			if (isnull)
+			if (!DatumGetBool(eval_result))
 			{
-				/* expression is NULL */
-				elog(DEBUG1, "expression for %s is NULL at row: " INT64_FORMAT, vname, current_pos);
-				*result = false;
+				/* expression is false */
+				elog(DEBUG1, "expression for %s is false at row: " INT64_FORMAT, vname, current_pos);
+				result = false;
 			}
 			else
 			{
-				if (!DatumGetBool(eval_result))
-				{
-					/* expression is false */
-					elog(DEBUG1, "expression for %s is false at row: " INT64_FORMAT, vname, current_pos);
-					*result = false;
-				}
-				else
-				{
-					/* expression is true */
-					elog(DEBUG1, "expression for %s is true at row: " INT64_FORMAT, vname, current_pos);
-					appendStringInfoChar(encoded_str, initial);
-					*result = true;
-				}
+				/* expression is true */
+				elog(DEBUG1, "expression for %s is true at row: " INT64_FORMAT, vname, current_pos);
+				result = true;
 			}
-
-			slot = winstate->temp_slot_1;
-			if (slot != winstate->null_slot)
-				ExecClearTuple(slot);
-			slot = winstate->prev_slot;
-			if (slot != winstate->null_slot)
-				ExecClearTuple(slot);
-			slot = winstate->next_slot;
-			if (slot != winstate->null_slot)
-				ExecClearTuple(slot);
-
-			break;
 		}
 
-		if (out_of_frame)
-		{
-			*result = false;
-			return -1;
-		}
+		slot = winstate->temp_slot_1;
+		if (slot != winstate->null_slot)
+			ExecClearTuple(slot);
+		slot = winstate->prev_slot;
+		if (slot != winstate->null_slot)
+			ExecClearTuple(slot);
+		slot = winstate->next_slot;
+		if (slot != winstate->null_slot)
+			ExecClearTuple(slot);
+
+		return result;
 	}
-	return current_pos;
+
+	pg_unreachable();
 }
 
 /*
@@ -4738,211 +4257,3 @@ bool get_slots(WindowObject winobj, int64 current_pos)
 	}
 	return true;
 }
-
-/*
- * Return pattern variable initial character
- * matching with pattern variable name vname.
- * If not found, return 0.
- */
-static
-char	pattern_initial(WindowAggState *winstate, char *vname)
-{
-	char		initial;
-	char		*name;
-	ListCell	*lc1, *lc2;
-
-	forboth (lc1, winstate->defineVariableList, lc2, winstate->defineInitial)
-	{
-		name = strVal(lfirst(lc1));				/* DEFINE variable name */
-		initial = *(strVal(lfirst(lc2)));		/* DEFINE variable initial */
-
-
-		if (!strcmp(name, vname))
-				return initial;					/* found */
-	}
-	return 0;
-}
-
-/*
- * string_set_init
- * Create dynamic set of StringInfo.
- */
-static
-StringSet *string_set_init(void)
-{
-/* Initial allocation size of str_set */
-#define STRING_SET_ALLOC_SIZE	1024
-
-	StringSet	*string_set;
-	Size		set_size;
-
-	string_set = palloc0(sizeof(StringSet));
-	string_set->set_index = 0;
-	set_size = STRING_SET_ALLOC_SIZE;
-	string_set->str_set = palloc(set_size * sizeof(StringInfo));
-	string_set->set_size = set_size;
-
-	return string_set;
-}
-
-/*
- * Add StringInfo str to StringSet string_set.
- */
-static
-void string_set_add(StringSet *string_set, StringInfo str)
-{
-	Size	set_size;
-
-	set_size = string_set->set_size;
-	if (string_set->set_index >= set_size)
-	{
-		set_size *= 2;
-		string_set->str_set = repalloc(string_set->str_set,
-									   set_size * sizeof(StringInfo));
-		string_set->set_size = set_size;
-	}
-
-	string_set->str_set[string_set->set_index++] = str;
-
-	return;
-}
-
-/*
- * Returns StringInfo specified by index.
- * If there's no data yet, returns NULL.
- */
-static
-StringInfo string_set_get(StringSet *string_set, int index)
-{
-	/* no data? */
-	if (index == 0 && string_set->set_index == 0)
-		return NULL;
-
-	if (index < 0 ||index >= string_set->set_index)
-		elog(ERROR, "invalid index: %d", index);
-
-	return string_set->str_set[index];
-}
-
-/*
- * Returns the size of StringSet.
- */
-static
-int string_set_get_size(StringSet *string_set)
-{
-	return string_set->set_index;
-}
-
-/*
- * Discard StringSet.
- * All memory including StringSet itself is freed.
- */
-static
-void string_set_discard(StringSet *string_set)
-{
-	int		i;
-
-	for (i = 0; i < string_set->set_index; i++)
-	{
-		StringInfo str = string_set->str_set[i];
-		pfree(str->data);
-		pfree(str);
-	}
-	pfree(string_set->str_set);
-	pfree(string_set);
-}
-
-/*
- * Create and initialize variable postion structure
- */
-static
-VariablePos *variable_pos_init(void)
-{
-	VariablePos	*variable_pos;
-
-	variable_pos = palloc(sizeof(VariablePos) * NUM_ALPHABETS);
-	MemSet(variable_pos, -1, sizeof(VariablePos) * NUM_ALPHABETS);
-	return variable_pos;
-}
-
-/*
- * Register pattern variable whose initial is initial into postion index.
- * pos is position of initial.
- * If pos is already registered, register it at next empty slot.
- */
-static
-void variable_pos_register(VariablePos *variable_pos, char initial, int pos)
-{
-	int		index = initial - 'a';
-	int		slot;
-	int		i;
-
-	if (pos < 0 || pos > NUM_ALPHABETS)
-		elog(ERROR, "initial is not valid char: %c", initial);
-
-	for (i = 0; i < NUM_ALPHABETS; i++)
-	{
-		slot = variable_pos[index].pos[i];
-		if (slot < 0)
-		{
-			/* empty slot found */
-			variable_pos[index].pos[i] = pos;
-			return;
-		}
-	}
-	elog(ERROR, "no empty slot for initial: %c", initial);
-}
-
-/*
- * Returns true if initial1 can be followed by initial2
- */
-static
-bool variable_pos_compare(VariablePos *variable_pos, char initial1, char initial2)
-{
-	int	index1, index2;
-	int pos1, pos2;
-
-	for (index1 = 0; ; index1++)
-	{
-		pos1 = variable_pos_fetch(variable_pos, initial1, index1);
-		if (pos1 < 0)
-			break;
-
-		for (index2 = 0; ; index2++)
-		{
-			pos2 = variable_pos_fetch(variable_pos, initial2, index2);
-			if (pos2 < 0)
-				break;
-			if (pos1 <= pos2)
-				return true;
-		}
-	}
-	return false;
-}
-
-/*
- * Fetch position of pattern variable whose initial is initial, and whose index
- * is index. If no postion was registered by initial, index, returns -1.
- */
-static
-int variable_pos_fetch(VariablePos *variable_pos, char initial, int index)
-{
-	int		pos = initial - 'a';
-
-	if (pos < 0 || pos > NUM_ALPHABETS)
-		elog(ERROR, "initial is not valid char: %c", initial);
-
-	if (index < 0 || index > NUM_ALPHABETS)
-		elog(ERROR, "index is not valid: %d", index);
-
-	return variable_pos[pos].pos[index];
-}
-
-/*
- * Discard VariablePos
- */
-static
-void variable_pos_discard(VariablePos *variable_pos)
-{
-	pfree(variable_pos);
-}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 469fcd156b..c2aba23d17 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -288,7 +288,6 @@ static WindowAgg *make_windowagg(List *tlist, Index winref,
 								 Oid startInRangeFunc, Oid endInRangeFunc,
 								 Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst, List *runCondition,
 								 RPSkipTo rpSkipTo, List *patternVariable, List *patternRegexp, List *defineClause,
-								 List *defineInitial,
 								 List *qual, bool topWindow, Plan *lefttree);
 static Group *make_group(List *tlist, List *qual, int numGroupCols,
 						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
@@ -2703,7 +2702,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
 						  wc->patternVariable,
 						  wc->patternRegexp,
 						  wc->defineClause,
-						  wc->defineInitial,
 						  best_path->qual,
 						  best_path->topwindow,
 						  subplan);
@@ -6609,7 +6607,6 @@ make_windowagg(List *tlist, Index winref,
 			   Oid startInRangeFunc, Oid endInRangeFunc,
 			   Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst, List *runCondition,
 			   RPSkipTo rpSkipTo, List *patternVariable, List *patternRegexp, List *defineClause,
-			   List *defineInitial,
 			   List *qual, bool topWindow, Plan *lefttree)
 {
 	WindowAgg  *node = makeNode(WindowAgg);
@@ -6640,7 +6637,6 @@ make_windowagg(List *tlist, Index winref,
 	node->patternVariable = patternVariable;
 	node->patternRegexp = patternRegexp;
 	node->defineClause = defineClause;
-	node->defineInitial = defineInitial;
 
 	plan->targetlist = tlist;
 	plan->lefttree = lefttree;
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 9c347216f7..ab81f5cbd8 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3875,16 +3875,11 @@ transformRPR(ParseState *pstate, WindowClause *wc, WindowDef *windef, List **tar
 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.
@@ -3998,33 +3993,7 @@ transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef, L
 		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);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 28d098b1cf..8e77646e8e 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -2530,7 +2530,6 @@ typedef struct WindowAggState
 	List	   *defineVariableList;	/* list of row pattern definition variables (list of String) */
 	List	   *defineClauseList;	/* expression for row pattern definition
 									 * search conditions ExprState list */
-	List	   *defineInitial;		/* list of row pattern definition variable initials (list of String) */
 
 	MemoryContext partcontext;	/* context for partition-lifespan data */
 	MemoryContext aggcontext;	/* shared context for aggregate working data */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 31b04d6919..613f746861 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1564,8 +1564,6 @@ typedef struct WindowClause
 	bool		initial;		/* true if <row pattern initial or seek> is initial */
 	/* Row Pattern DEFINE clause (list of TargetEntry) */
 	List		*defineClause;
-	/* Row Pattern DEFINE variable initial names (list of String) */
-	List		*defineInitial;
 	/* Row Pattern PATTERN variable name (list of String) */
 	List		*patternVariable;
 	/* Row Pattern PATTERN regular expression quantifier ('+' or ''. list of String) */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index e48b59517d..33f4aa4b72 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -1108,9 +1108,6 @@ typedef struct WindowAgg
 	/* Row Pattern DEFINE clause (list of TargetEntry) */
 	List	   *defineClause;
 
-	/* Row Pattern DEFINE variable initial names (list of String) */
-	List		*defineInitial;
-
 	/*
 	 * false for all apart from the WindowAgg that's closest to the root of
 	 * the plan
-- 
2.39.2