From 219bd1e4fb920c6af5d789aab9e5eafa674c7364 Mon Sep 17 00:00:00 2001
From: Tatsuo Ishii <ishii@postgresql.org>
Date: Thu, 4 Oct 2001 02:15:47 +0000
Subject: [PATCH] Optimization for single byte encodings.

---
 src/backend/utils/adt/like.c       | 408 +++++++----------------------
 src/backend/utils/adt/like_match.c | 339 ++++++++++++++++++++++++
 2 files changed, 433 insertions(+), 314 deletions(-)
 create mode 100644 src/backend/utils/adt/like_match.c

diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c
index 4f6d6d0212b..f3f8b9854e1 100644
--- a/src/backend/utils/adt/like.c
+++ b/src/backend/utils/adt/like.c
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	$Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.46 2001/09/14 17:46:40 momjian Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.47 2001/10/04 02:15:47 ishii Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,9 +36,15 @@ static int MatchTextIC(unsigned char *t, int tlen,
 			unsigned char *p, int plen);
 static int MatchBytea(unsigned char *t, int tlen,
 		  unsigned char *p, int plen);
-
+static text *do_like_escape(text *, text *);
 
 #ifdef MULTIBYTE
+static int MBMatchText(unsigned char *t, int tlen,
+		  unsigned char *p, int plen);
+static int MBMatchTextIC(unsigned char *t, int tlen,
+			unsigned char *p, int plen);
+static text *MB_do_like_escape(text *, text *);
+
 /*--------------------
  * Support routine for MatchText. Compares given multibyte streams
  * as wide characters. If they match, returns 1 otherwise returns 0.
@@ -115,16 +121,29 @@ iwchareq(unsigned char *p1, unsigned char *p2)
 		 while (__l-- > 0) \
 			 *(dst)++ = *(src)++; \
 	   } while (0)
-#else
+
+#define MatchText	MBMatchText
+#define MatchTextIC	MBMatchTextIC
+#define do_like_escape	MB_do_like_escape
+#include "like_match.c"
+#undef CHAREQ
+#undef ICHAREQ
+#undef NextChar
+#undef CopyAdvChar
+#undef MatchText
+#undef MatchTextIC
+#undef do_like_escape
+#endif
+
 #define CHAREQ(p1, p2) (*(p1) == *(p2))
 #define ICHAREQ(p1, p2) (tolower(*(p1)) == tolower(*(p2)))
 #define NextChar(p, plen) ((p)++, (plen)--)
 #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
-#endif
 
 #define BYTEA_CHAREQ(p1, p2) (*(p1) == *(p2))
 #define BYTEA_NextChar(p, plen) ((p)++, (plen)--)
 #define BYTEA_CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
+#include "like_match.c"
 
 /*
  *	interface routines called by the function manager
@@ -146,7 +165,14 @@ namelike(PG_FUNCTION_ARGS)
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
+	else
+	    result = (MBMatchText(s, slen, p, plen) == LIKE_TRUE);	
+#else
+	result = (MatchText(s, slen, p, plen) == LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -167,7 +193,14 @@ namenlike(PG_FUNCTION_ARGS)
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
+	else
+	    result = (MBMatchText(s, slen, p, plen) != LIKE_TRUE);	
+#else
+	result = (MatchText(s, slen, p, plen) != LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -182,13 +215,19 @@ textlike(PG_FUNCTION_ARGS)
 			   *p;
 	int			slen,
 				plen;
-
 	s = VARDATA(str);
 	slen = (VARSIZE(str) - VARHDRSZ);
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
+	else
+	    result = (MBMatchText(s, slen, p, plen) == LIKE_TRUE);	
+#else
+	result = (MatchText(s, slen, p, plen) == LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -209,7 +248,14 @@ textnlike(PG_FUNCTION_ARGS)
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
+	else
+	    result = (MBMatchText(s, slen, p, plen) != LIKE_TRUE);	
+#else
+	result = (MatchText(s, slen, p, plen) != LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -276,7 +322,14 @@ nameiclike(PG_FUNCTION_ARGS)
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
+	else
+	    result = (MBMatchTextIC(s, slen, p, plen) == LIKE_TRUE);	
+#else
+	result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -297,7 +350,14 @@ nameicnlike(PG_FUNCTION_ARGS)
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
+	else
+	    result = (MBMatchTextIC(s, slen, p, plen) != LIKE_TRUE);	
+#else
+	result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -318,7 +378,14 @@ texticlike(PG_FUNCTION_ARGS)
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
+	else
+	    result = (MBMatchTextIC(s, slen, p, plen) == LIKE_TRUE);	
+#else
+	result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -339,7 +406,14 @@ texticnlike(PG_FUNCTION_ARGS)
 	p = VARDATA(pat);
 	plen = (VARSIZE(pat) - VARHDRSZ);
 
-	result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
+	else
+	    result = (MBMatchTextIC(s, slen, p, plen) != LIKE_TRUE);	
+#else
+	result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);	
+#endif
 
 	PG_RETURN_BOOL(result);
 }
@@ -354,90 +428,15 @@ like_escape(PG_FUNCTION_ARGS)
 	text	   *pat = PG_GETARG_TEXT_P(0);
 	text	   *esc = PG_GETARG_TEXT_P(1);
 	text	   *result;
-	unsigned char *p,
-			   *e,
-			   *r;
-	int			plen,
-				elen;
-	bool		afterescape;
-
-	p = VARDATA(pat);
-	plen = (VARSIZE(pat) - VARHDRSZ);
-	e = VARDATA(esc);
-	elen = (VARSIZE(esc) - VARHDRSZ);
 
-	/*
-	 * Worst-case pattern growth is 2x --- unlikely, but it's hardly worth
-	 * trying to calculate the size more accurately than that.
-	 */
-	result = (text *) palloc(plen * 2 + VARHDRSZ);
-	r = VARDATA(result);
-
-	if (elen == 0)
-	{
-
-		/*
-		 * No escape character is wanted.  Double any backslashes in the
-		 * pattern to make them act like ordinary characters.
-		 */
-		while (plen > 0)
-		{
-			if (*p == '\\')
-				*r++ = '\\';
-			CopyAdvChar(r, p, plen);
-		}
-	}
+#ifdef MULTIBYTE
+	if (pg_database_encoding_max_length() == 1)
+	    result = do_like_escape(pat, esc);
 	else
-	{
-
-		/*
-		 * The specified escape must be only a single character.
-		 */
-		NextChar(e, elen);
-		if (elen != 0)
-			elog(ERROR, "ESCAPE string must be empty or one character");
-		e = VARDATA(esc);
-
-		/*
-		 * If specified escape is '\', just copy the pattern as-is.
-		 */
-		if (*e == '\\')
-		{
-			memcpy(result, pat, VARSIZE(pat));
-			PG_RETURN_TEXT_P(result);
-		}
-
-		/*
-		 * Otherwise, convert occurrences of the specified escape
-		 * character to '\', and double occurrences of '\' --- unless they
-		 * immediately follow an escape character!
-		 */
-		afterescape = false;
-		while (plen > 0)
-		{
-			if (CHAREQ(p, e) && !afterescape)
-			{
-				*r++ = '\\';
-				NextChar(p, plen);
-				afterescape = true;
-			}
-			else if (*p == '\\')
-			{
-				*r++ = '\\';
-				if (!afterescape)
-					*r++ = '\\';
-				NextChar(p, plen);
-				afterescape = false;
-			}
-			else
-			{
-				CopyAdvChar(r, p, plen);
-				afterescape = false;
-			}
-		}
-	}
-
-	VARATT_SIZEP(result) = r - ((unsigned char *) result);
+	    result = MB_do_like_escape(pat, esc);
+#else
+	result = do_like_escape(pat, esc);
+#endif
 
 	PG_RETURN_TEXT_P(result);
 }
@@ -540,225 +539,6 @@ like_escape_bytea(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(result);
 }
 
-/*
-**	Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
-**	Rich $alz is now <rsalz@bbn.com>.
-**	Special thanks to Lars Mathiesen <thorinn@diku.dk> for the LABORT code.
-**
-**	This code was shamelessly stolen from the "pql" code by myself and
-**	slightly modified :)
-**
-**	All references to the word "star" were replaced by "percent"
-**	All references to the word "wild" were replaced by "like"
-**
-**	All the nice shell RE matching stuff was replaced by just "_" and "%"
-**
-**	As I don't have a copy of the SQL standard handy I wasn't sure whether
-**	to leave in the '\' escape character handling.
-**
-**	Keith Parks. <keith@mtcc.demon.co.uk>
-**
-**	SQL92 lets you specify the escape character by saying
-**	LIKE <pattern> ESCAPE <escape character>. We are a small operation
-**	so we force you to use '\'. - ay 7/95
-**
-**	Now we have the like_escape() function that converts patterns with
-**	any specified escape character (or none at all) to the internal
-**	default escape character, which is still '\'. - tgl 9/2000
-**
-** The code is rewritten to avoid requiring null-terminated strings,
-** which in turn allows us to leave out some memcpy() operations.
-** This code should be faster and take less memory, but no promises...
-** - thomas 2000-08-06
-**
-*/
-
-
-/*--------------------
- *	Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
- *
- *	LIKE_TRUE: they match
- *	LIKE_FALSE: they don't match
- *	LIKE_ABORT: not only don't they match, but the text is too short.
- *
- * If LIKE_ABORT is returned, then no suffix of the text can match the
- * pattern either, so an upper-level % scan can stop scanning now.
- *--------------------
- */
-
-static int
-MatchText(unsigned char *t, int tlen, unsigned char *p, int plen)
-{
-	/* Fast path for match-everything pattern */
-	if ((plen == 1) && (*p == '%'))
-		return LIKE_TRUE;
-
-	while ((tlen > 0) && (plen > 0))
-	{
-		if (*p == '\\')
-		{
-			/* Next pattern char must match literally, whatever it is */
-			NextChar(p, plen);
-			if ((plen <= 0) || !CHAREQ(t, p))
-				return LIKE_FALSE;
-		}
-		else if (*p == '%')
-		{
-			/* %% is the same as % according to the SQL standard */
-			/* Advance past all %'s */
-			while ((plen > 0) && (*p == '%'))
-				NextChar(p, plen);
-			/* Trailing percent matches everything. */
-			if (plen <= 0)
-				return LIKE_TRUE;
-
-			/*
-			 * Otherwise, scan for a text position at which we can match
-			 * the rest of the pattern.
-			 */
-			while (tlen > 0)
-			{
-
-				/*
-				 * Optimization to prevent most recursion: don't recurse
-				 * unless first pattern char might match this text char.
-				 */
-				if (CHAREQ(t, p) || (*p == '\\') || (*p == '_'))
-				{
-					int			matched = MatchText(t, tlen, p, plen);
-
-					if (matched != LIKE_FALSE)
-						return matched; /* TRUE or ABORT */
-				}
-
-				NextChar(t, tlen);
-			}
-
-			/*
-			 * End of text with no match, so no point in trying later
-			 * places to start matching this pattern.
-			 */
-			return LIKE_ABORT;
-		}
-		else if ((*p != '_') && !CHAREQ(t, p))
-		{
-
-			/*
-			 * Not the single-character wildcard and no explicit match?
-			 * Then time to quit...
-			 */
-			return LIKE_FALSE;
-		}
-
-		NextChar(t, tlen);
-		NextChar(p, plen);
-	}
-
-	if (tlen > 0)
-		return LIKE_FALSE;		/* end of pattern, but not of text */
-
-	/* End of input string.  Do we have matching pattern remaining? */
-	while ((plen > 0) && (*p == '%'))	/* allow multiple %'s at end of
-										 * pattern */
-		NextChar(p, plen);
-	if (plen <= 0)
-		return LIKE_TRUE;
-
-	/*
-	 * End of text with no match, so no point in trying later places to
-	 * start matching this pattern.
-	 */
-	return LIKE_ABORT;
-}	/* MatchText() */
-
-/*
- * Same as above, but ignore case
- */
-static int
-MatchTextIC(unsigned char *t, int tlen, unsigned char *p, int plen)
-{
-	/* Fast path for match-everything pattern */
-	if ((plen == 1) && (*p == '%'))
-		return LIKE_TRUE;
-
-	while ((tlen > 0) && (plen > 0))
-	{
-		if (*p == '\\')
-		{
-			/* Next pattern char must match literally, whatever it is */
-			NextChar(p, plen);
-			if ((plen <= 0) || !ICHAREQ(t, p))
-				return LIKE_FALSE;
-		}
-		else if (*p == '%')
-		{
-			/* %% is the same as % according to the SQL standard */
-			/* Advance past all %'s */
-			while ((plen > 0) && (*p == '%'))
-				NextChar(p, plen);
-			/* Trailing percent matches everything. */
-			if (plen <= 0)
-				return LIKE_TRUE;
-
-			/*
-			 * Otherwise, scan for a text position at which we can match
-			 * the rest of the pattern.
-			 */
-			while (tlen > 0)
-			{
-
-				/*
-				 * Optimization to prevent most recursion: don't recurse
-				 * unless first pattern char might match this text char.
-				 */
-				if (ICHAREQ(t, p) || (*p == '\\') || (*p == '_'))
-				{
-					int			matched = MatchTextIC(t, tlen, p, plen);
-
-					if (matched != LIKE_FALSE)
-						return matched; /* TRUE or ABORT */
-				}
-
-				NextChar(t, tlen);
-			}
-
-			/*
-			 * End of text with no match, so no point in trying later
-			 * places to start matching this pattern.
-			 */
-			return LIKE_ABORT;
-		}
-		else if ((*p != '_') && !ICHAREQ(t, p))
-		{
-
-			/*
-			 * Not the single-character wildcard and no explicit match?
-			 * Then time to quit...
-			 */
-			return LIKE_FALSE;
-		}
-
-		NextChar(t, tlen);
-		NextChar(p, plen);
-	}
-
-	if (tlen > 0)
-		return LIKE_FALSE;		/* end of pattern, but not of text */
-
-	/* End of input string.  Do we have matching pattern remaining? */
-	while ((plen > 0) && (*p == '%'))	/* allow multiple %'s at end of
-										 * pattern */
-		NextChar(p, plen);
-	if (plen <= 0)
-		return LIKE_TRUE;
-
-	/*
-	 * End of text with no match, so no point in trying later places to
-	 * start matching this pattern.
-	 */
-	return LIKE_ABORT;
-}	/* MatchTextIC() */
-
 /*
  * Same as above, but specifically for bytea (binary) datatype
  */
diff --git a/src/backend/utils/adt/like_match.c b/src/backend/utils/adt/like_match.c
new file mode 100644
index 00000000000..f13b8fea05c
--- /dev/null
+++ b/src/backend/utils/adt/like_match.c
@@ -0,0 +1,339 @@
+/*-------------------------------------------------------------------------
+ *
+ * like_match.c
+ *	  like expression handling internal code.
+ *
+ * This file is included by like.c *twice* if multibyte is enabled.
+ * This is for an optimization of single byte encodings.
+ * Before the inclusion, we need to define following macros:
+ *
+ * CHAREQ
+ * ICHAREQ
+ * NextChar
+ * CopyAdvChar
+ * MatchText (MBMatchText)
+ * MatchTextIC (MBMatchTextIC)
+ * do_like_escape (MB_do_like_escape)
+ *
+ * Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	$Header: /cvsroot/pgsql/src/backend/utils/adt/like_match.c,v 1.1 2001/10/04 02:15:47 ishii Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+**	Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+**	Rich $alz is now <rsalz@bbn.com>.
+**	Special thanks to Lars Mathiesen <thorinn@diku.dk> for the LABORT code.
+**
+**	This code was shamelessly stolen from the "pql" code by myself and
+**	slightly modified :)
+**
+**	All references to the word "star" were replaced by "percent"
+**	All references to the word "wild" were replaced by "like"
+**
+**	All the nice shell RE matching stuff was replaced by just "_" and "%"
+**
+**	As I don't have a copy of the SQL standard handy I wasn't sure whether
+**	to leave in the '\' escape character handling.
+**
+**	Keith Parks. <keith@mtcc.demon.co.uk>
+**
+**	SQL92 lets you specify the escape character by saying
+**	LIKE <pattern> ESCAPE <escape character>. We are a small operation
+**	so we force you to use '\'. - ay 7/95
+**
+**	Now we have the like_escape() function that converts patterns with
+**	any specified escape character (or none at all) to the internal
+**	default escape character, which is still '\'. - tgl 9/2000
+**
+** The code is rewritten to avoid requiring null-terminated strings,
+** which in turn allows us to leave out some memcpy() operations.
+** This code should be faster and take less memory, but no promises...
+** - thomas 2000-08-06
+**
+*/
+
+
+/*--------------------
+ *	Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
+ *
+ *	LIKE_TRUE: they match
+ *	LIKE_FALSE: they don't match
+ *	LIKE_ABORT: not only don't they match, but the text is too short.
+ *
+ * If LIKE_ABORT is returned, then no suffix of the text can match the
+ * pattern either, so an upper-level % scan can stop scanning now.
+ *--------------------
+ */
+
+static int
+MatchText(unsigned char *t, int tlen, unsigned char *p, int plen)
+{
+	/* Fast path for match-everything pattern */
+	if ((plen == 1) && (*p == '%'))
+		return LIKE_TRUE;
+
+	while ((tlen > 0) && (plen > 0))
+	{
+		if (*p == '\\')
+		{
+			/* Next pattern char must match literally, whatever it is */
+			NextChar(p, plen);
+			if ((plen <= 0) || !CHAREQ(t, p))
+				return LIKE_FALSE;
+		}
+		else if (*p == '%')
+		{
+			/* %% is the same as % according to the SQL standard */
+			/* Advance past all %'s */
+			while ((plen > 0) && (*p == '%'))
+				NextChar(p, plen);
+			/* Trailing percent matches everything. */
+			if (plen <= 0)
+				return LIKE_TRUE;
+
+			/*
+			 * Otherwise, scan for a text position at which we can match
+			 * the rest of the pattern.
+			 */
+			while (tlen > 0)
+			{
+
+				/*
+				 * Optimization to prevent most recursion: don't recurse
+				 * unless first pattern char might match this text char.
+				 */
+				if (CHAREQ(t, p) || (*p == '\\') || (*p == '_'))
+				{
+					int			matched = MatchText(t, tlen, p, plen);
+
+					if (matched != LIKE_FALSE)
+						return matched; /* TRUE or ABORT */
+				}
+
+				NextChar(t, tlen);
+			}
+
+			/*
+			 * End of text with no match, so no point in trying later
+			 * places to start matching this pattern.
+			 */
+			return LIKE_ABORT;
+		}
+		else if ((*p != '_') && !CHAREQ(t, p))
+		{
+
+			/*
+			 * Not the single-character wildcard and no explicit match?
+			 * Then time to quit...
+			 */
+			return LIKE_FALSE;
+		}
+
+		NextChar(t, tlen);
+		NextChar(p, plen);
+	}
+
+	if (tlen > 0)
+		return LIKE_FALSE;		/* end of pattern, but not of text */
+
+	/* End of input string.  Do we have matching pattern remaining? */
+	while ((plen > 0) && (*p == '%'))	/* allow multiple %'s at end of
+										 * pattern */
+		NextChar(p, plen);
+	if (plen <= 0)
+		return LIKE_TRUE;
+
+	/*
+	 * End of text with no match, so no point in trying later places to
+	 * start matching this pattern.
+	 */
+	return LIKE_ABORT;
+}	/* MatchText() */
+
+/*
+ * Same as above, but ignore case
+ */
+static int
+MatchTextIC(unsigned char *t, int tlen, unsigned char *p, int plen)
+{
+	/* Fast path for match-everything pattern */
+	if ((plen == 1) && (*p == '%'))
+		return LIKE_TRUE;
+
+	while ((tlen > 0) && (plen > 0))
+	{
+		if (*p == '\\')
+		{
+			/* Next pattern char must match literally, whatever it is */
+			NextChar(p, plen);
+			if ((plen <= 0) || !ICHAREQ(t, p))
+				return LIKE_FALSE;
+		}
+		else if (*p == '%')
+		{
+			/* %% is the same as % according to the SQL standard */
+			/* Advance past all %'s */
+			while ((plen > 0) && (*p == '%'))
+				NextChar(p, plen);
+			/* Trailing percent matches everything. */
+			if (plen <= 0)
+				return LIKE_TRUE;
+
+			/*
+			 * Otherwise, scan for a text position at which we can match
+			 * the rest of the pattern.
+			 */
+			while (tlen > 0)
+			{
+
+				/*
+				 * Optimization to prevent most recursion: don't recurse
+				 * unless first pattern char might match this text char.
+				 */
+				if (ICHAREQ(t, p) || (*p == '\\') || (*p == '_'))
+				{
+					int			matched = MatchTextIC(t, tlen, p, plen);
+
+					if (matched != LIKE_FALSE)
+						return matched; /* TRUE or ABORT */
+				}
+
+				NextChar(t, tlen);
+			}
+
+			/*
+			 * End of text with no match, so no point in trying later
+			 * places to start matching this pattern.
+			 */
+			return LIKE_ABORT;
+		}
+		else if ((*p != '_') && !ICHAREQ(t, p))
+		{
+
+			/*
+			 * Not the single-character wildcard and no explicit match?
+			 * Then time to quit...
+			 */
+			return LIKE_FALSE;
+		}
+
+		NextChar(t, tlen);
+		NextChar(p, plen);
+	}
+
+	if (tlen > 0)
+		return LIKE_FALSE;		/* end of pattern, but not of text */
+
+	/* End of input string.  Do we have matching pattern remaining? */
+	while ((plen > 0) && (*p == '%'))	/* allow multiple %'s at end of
+										 * pattern */
+		NextChar(p, plen);
+	if (plen <= 0)
+		return LIKE_TRUE;
+
+	/*
+	 * End of text with no match, so no point in trying later places to
+	 * start matching this pattern.
+	 */
+	return LIKE_ABORT;
+}	/* MatchTextIC() */
+
+/*
+ * like_escape() --- given a pattern and an ESCAPE string,
+ * convert the pattern to use Postgres' standard backslash escape convention.
+ */
+static text *
+do_like_escape(text *pat, text *esc)
+{
+	text	   *result;
+	unsigned char *p,
+			   *e,
+			   *r;
+	int			plen,
+				elen;
+	bool		afterescape;
+
+	p = VARDATA(pat);
+	plen = (VARSIZE(pat) - VARHDRSZ);
+	e = VARDATA(esc);
+	elen = (VARSIZE(esc) - VARHDRSZ);
+
+	/*
+	 * Worst-case pattern growth is 2x --- unlikely, but it's hardly worth
+	 * trying to calculate the size more accurately than that.
+	 */
+	result = (text *) palloc(plen * 2 + VARHDRSZ);
+	r = VARDATA(result);
+
+	if (elen == 0)
+	{
+
+		/*
+		 * No escape character is wanted.  Double any backslashes in the
+		 * pattern to make them act like ordinary characters.
+		 */
+		while (plen > 0)
+		{
+			if (*p == '\\')
+				*r++ = '\\';
+			CopyAdvChar(r, p, plen);
+		}
+	}
+	else
+	{
+
+		/*
+		 * The specified escape must be only a single character.
+		 */
+		NextChar(e, elen);
+		if (elen != 0)
+			elog(ERROR, "ESCAPE string must be empty or one character");
+		e = VARDATA(esc);
+
+		/*
+		 * If specified escape is '\', just copy the pattern as-is.
+		 */
+		if (*e == '\\')
+		{
+			memcpy(result, pat, VARSIZE(pat));
+			return result;
+		}
+
+		/*
+		 * Otherwise, convert occurrences of the specified escape
+		 * character to '\', and double occurrences of '\' --- unless they
+		 * immediately follow an escape character!
+		 */
+		afterescape = false;
+		while (plen > 0)
+		{
+			if (CHAREQ(p, e) && !afterescape)
+			{
+				*r++ = '\\';
+				NextChar(p, plen);
+				afterescape = true;
+			}
+			else if (*p == '\\')
+			{
+				*r++ = '\\';
+				if (!afterescape)
+					*r++ = '\\';
+				NextChar(p, plen);
+				afterescape = false;
+			}
+			else
+			{
+				CopyAdvChar(r, p, plen);
+				afterescape = false;
+			}
+		}
+	}
+
+	VARATT_SIZEP(result) = r - ((unsigned char *) result);
+
+	return result;
+}
-- 
GitLab