From bad3b3068d97c8acf68958176fa017d5033fe773 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 7 Jun 1999 14:28:26 +0000
Subject: [PATCH] Repair recently-introduced error in makeIndexable for LIKE: a
 non-leading % would be put into the >=/<= patterns.  Also, repair
 longstanding confusion about whether %% means a literal %%.  The SQL92
 doesn't say any such thing, and textlike() knows that, but gram.y didn't.

---
 src/backend/parser/gram.y    | 37 ++++++++++++++++++++++++------------
 src/backend/utils/adt/like.c |  6 ++++--
 2 files changed, 29 insertions(+), 14 deletions(-)

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c038f43caae..bb36c6f0422 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.83 1999/05/22 05:06:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.84 1999/06/07 14:28:25 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -5357,6 +5357,7 @@ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr)
 			int pos, match_pos=0;
 			bool found_special = false;
 
+			/* Cannot optimize if unquoted | { } is present in pattern */
 			for (pos = 1; n->val.val.str[pos]; pos++)
 			{
 				if (n->val.val.str[pos] == '|' ||
@@ -5367,12 +5368,16 @@ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr)
 					break;
 				}
 		     	if (n->val.val.str[pos] == '\\')
+				{
 					pos++;
+					if (n->val.val.str[pos] == '\0')
+						break;
+				}
 			}
 
-			/* skip leading ^ */
 			if (!found_special)
 			{
+				/* note start at pos 1 to skip leading ^ */
 				for (pos = 1; n->val.val.str[pos]; pos++)
 				{
 					if (n->val.val.str[pos] == '.' ||
@@ -5383,9 +5388,11 @@ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr)
 						(strcmp(opname,"~*") == 0 && isalpha(n->val.val.str[pos])))
 			     		break;
 			     	if (n->val.val.str[pos] == '\\')
+					{
 						pos++;
-					if (n->val.val.str[pos] == '\0')
-						break;
+						if (n->val.val.str[pos] == '\0')
+							break;
+					}
 					match_least[match_pos] = n->val.val.str[pos];
 					match_most[match_pos++] = n->val.val.str[pos];
 				}
@@ -5430,16 +5437,22 @@ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr)
 	
 			for (pos = 0; n->val.val.str[pos]; pos++)
 			{
-				if (n->val.val.str[pos] == '%' &&
-					 n->val.val.str[pos+1] != '%')
-					break;
-				if(n->val.val.str[pos] == '_')
+				/* % and _ are wildcard characters in LIKE */
+				if (n->val.val.str[pos] == '%' ||
+					n->val.val.str[pos] == '_')
 					break;
-		     	if (n->val.val.str[pos] == '\\' ||
-			     	n->val.val.str[pos+1] == '%')
+				/* Backslash quotes the next character */
+				if (n->val.val.str[pos] == '\\')
+				{
 					pos++;
-				if (n->val.val.str[pos] == '\0')
-					break;
+					if (n->val.val.str[pos] == '\0')
+						break;
+				}
+				/*
+				 * NOTE: this code used to think that %% meant a literal %,
+				 * but textlike() itself does not think that, and the SQL92
+				 * spec doesn't say any such thing either.
+				 */
 				match_least[match_pos] = n->val.val.str[pos];
 				match_most[match_pos++] = n->val.val.str[pos];
 			}
diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c
index 14fc5314d0c..2d35bfabaca 100644
--- a/src/backend/utils/adt/like.c
+++ b/src/backend/utils/adt/like.c
@@ -111,7 +111,7 @@ textnlike(struct varlena * s, struct varlena * p)
 }
 
 
-/*	$Revision: 1.24 $
+/*	$Revision: 1.25 $
 **	"like.c" A first attempt at a LIKE operator for Postgres95.
 **
 **	Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
@@ -191,7 +191,9 @@ DoMatch(pg_wchar * text, pg_wchar * p)
 	else
 	{
 		/* End of input string.  Do we have matching string remaining? */
-		if (p[0] == '\0' || (p[0] == '%' && p[1] == '\0'))
+		while (*p == '%')		/* allow multiple %'s at end of pattern */
+			p++;
+		if (*p == '\0')
 			return LIKE_TRUE;
 		else
 			return LIKE_ABORT;
-- 
GitLab