Tom Lane wrote:
Andrew Dunstan <[EMAIL PROTECTED]> writes:
  do { (t)++; (tlen)--}  while ((*(t) & 0xC0) == 0x80 && tlen > 0)

The while *must* test those two conditions in the other order.
(Don't laugh --- we've had reproducible bugs before in which the backend
dumped core because of running off the end of memory due to this type
of mistake.)

In fact, I'm wondering if that might make the other UTF8 stuff redundant - the whole point of what we're doing is to avoid expensive calls to NextChar;

+1 I think.  This test will be approximately the same expense as what
the outer loop would otherwise be (tlen > 0 and *t != firstpat), and
doing it this way removes an entire layer of intellectual complexity.
Even though the code is hardly different, we are no longer dealing in
misaligned pointers anywhere in the match algorithm.



OK, here is a patch that I think incorporates all the ideas discussed (including part of Mark Mielke's suggestion about optimising %_). There is now no special treatment of UTF8 other than its use of a faster NextChar macro.

cheers

andrew
Index: src/backend/utils/adt/like.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/like.c,v
retrieving revision 1.68
diff -c -r1.68 like.c
*** src/backend/utils/adt/like.c	27 Feb 2007 23:48:08 -0000	1.68
--- src/backend/utils/adt/like.c	1 Jun 2007 02:21:59 -0000
***************
*** 28,48 ****
  #define LIKE_ABORT						(-1)
  
  
! static int	MatchText(char *t, int tlen, char *p, int plen);
! static int	MatchTextIC(char *t, int tlen, char *p, int plen);
! static int	MatchBytea(char *t, int tlen, char *p, int plen);
! static text *do_like_escape(text *, text *);
  
! static int	MBMatchText(char *t, int tlen, char *p, int plen);
! static int	MBMatchTextIC(char *t, int tlen, 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.
   *--------------------
   */
! static int
  wchareq(char *p1, char *p2)
  {
  	int			p1_len;
--- 28,50 ----
  #define LIKE_ABORT						(-1)
  
  
! static int	SB_MatchText(char *t, int tlen, char *p, int plen);
! static text *SB_do_like_escape(text *, text *);
  
! static int	MB_MatchText(char *t, int tlen, char *p, int plen);
  static text *MB_do_like_escape(text *, text *);
  
+ static int	UTF8_MatchText(char *t, int tlen, char *p, int plen);
+ 
+ static int	GenericMatchText(char *s, int slen, char* p, int plen);
+ static int	Generic_Text_IC_like(text *str, text *pat);
+ 
  /*--------------------
   * Support routine for MatchText. Compares given multibyte streams
   * as wide characters. If they match, returns 1 otherwise returns 0.
   *--------------------
   */
! static inline int
  wchareq(char *p1, char *p2)
  {
  	int			p1_len;
***************
*** 72,86 ****
   * of getting a single character transformed to the system's wchar_t format.
   * So now, we just downcase the strings using lower() and apply regular LIKE
   * comparison.	This should be revisited when we install better locale support.
-  *
-  * Note that MBMatchText and MBMatchTextIC do exactly the same thing now.
-  * Is it worth refactoring to avoid duplicated code?  They might become
-  * different again in the future.
   */
  
  /* Set up to compile like_match.c for multibyte characters */
! #define CHAREQ(p1, p2) wchareq(p1, p2)
! #define ICHAREQ(p1, p2) wchareq(p1, p2)
  #define NextChar(p, plen) \
  	do { int __l = pg_mblen(p); (p) +=__l; (plen) -=__l; } while (0)
  #define CopyAdvChar(dst, src, srclen) \
--- 74,85 ----
   * of getting a single character transformed to the system's wchar_t format.
   * So now, we just downcase the strings using lower() and apply regular LIKE
   * comparison.	This should be revisited when we install better locale support.
   */
  
+ #define NextByte(p, plen)	((p)++, (plen)--)
+ 
  /* Set up to compile like_match.c for multibyte characters */
! #define CHAREQ(p1, p2) wchareq((p1), (p2))
  #define NextChar(p, plen) \
  	do { int __l = pg_mblen(p); (p) +=__l; (plen) -=__l; } while (0)
  #define CopyAdvChar(dst, src, srclen) \
***************
*** 90,122 ****
  			 *(dst)++ = *(src)++; \
  	   } while (0)
  
! #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
- 
  /* Set up to compile like_match.c for single-byte characters */
  #define CHAREQ(p1, p2) (*(p1) == *(p2))
! #define ICHAREQ(p1, p2) (tolower((unsigned char) *(p1)) == tolower((unsigned char) *(p2)))
! #define NextChar(p, plen) ((p)++, (plen)--)
  #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
  
  #include "like_match.c"
  
- /* And some support for BYTEA */
- #define BYTEA_CHAREQ(p1, p2) (*(p1) == *(p2))
- #define BYTEA_NextChar(p, plen) ((p)++, (plen)--)
- #define BYTEA_CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
  
  
  /*
   *	interface routines called by the function manager
--- 89,147 ----
  			 *(dst)++ = *(src)++; \
  	   } while (0)
  
! #define MatchText	MB_MatchText
  #define do_like_escape	MB_do_like_escape
  
  #include "like_match.c"
  
  /* Set up to compile like_match.c for single-byte characters */
  #define CHAREQ(p1, p2) (*(p1) == *(p2))
! #define NextChar(p, plen) NextByte((p), (plen))
  #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
  
+ #define MatchText	SB_MatchText
+ #define do_like_escape	SB_do_like_escape
+ 
  #include "like_match.c"
  
  
+ /* setup to compile like_match.c for UTF8 encoding, using fast NextChar */
+ 
+ #define NextChar(p, plen) \
+ 	do { (p)++; (plen)--; } while ((plen) > 0 && (*(p) & 0xC0) == 0x80 ) 
+ #define MatchText	UTF8_MatchText
+ 
+ #include "like_match.c"
+ 
+ static inline int
+ GenericMatchText(char *s, int slen, char* p, int plen)
+ {
+ 	if (pg_database_encoding_max_length() == 1)
+ 		return SB_MatchText(s, slen, p, plen);
+ 	else if (GetDatabaseEncoding() == PG_UTF8)
+ 		return UTF8_MatchText(s, slen, p, plen);
+ 	else
+ 		return MB_MatchText(s, slen, p, plen);
+ }
+ 
+ static inline int
+ Generic_Text_IC_like(text *str, text *pat)
+ {
+ 	char	   *s,
+ 			   *p;
+ 	int			slen,
+ 				plen;
+ 
+ 	/* Force inputs to lower case to achieve case insensitivity */
+ 	str = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(str)));
+ 	pat = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(pat)));
+ 	s = VARDATA(str);
+ 	slen = (VARSIZE(str) - VARHDRSZ);
+ 	p = VARDATA(pat);
+ 	plen = (VARSIZE(pat) - VARHDRSZ);
+ 
+ 	return GenericMatchText(s, slen, p, plen);
+ }
  
  /*
   *	interface routines called by the function manager
***************
*** 138,147 ****
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	if (pg_database_encoding_max_length() == 1)
! 		result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
! 	else
! 		result = (MBMatchText(s, slen, p, plen) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
--- 163,169 ----
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (GenericMatchText(s, slen, p, plen) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 162,171 ****
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	if (pg_database_encoding_max_length() == 1)
! 		result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
! 	else
! 		result = (MBMatchText(s, slen, p, plen) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
--- 184,190 ----
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (GenericMatchText(s, slen, p, plen) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 186,195 ****
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	if (pg_database_encoding_max_length() == 1)
! 		result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
! 	else
! 		result = (MBMatchText(s, slen, p, plen) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
--- 205,211 ----
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (GenericMatchText(s, slen, p, plen) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 210,219 ****
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	if (pg_database_encoding_max_length() == 1)
! 		result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
! 	else
! 		result = (MBMatchText(s, slen, p, plen) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
--- 226,232 ----
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (GenericMatchText(s, slen, p, plen) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 234,240 ****
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (MatchBytea(s, slen, p, plen) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
--- 247,253 ----
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (SB_MatchText(s, slen, p, plen) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 255,261 ****
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (MatchBytea(s, slen, p, plen) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
--- 268,274 ----
  	p = VARDATA(pat);
  	plen = (VARSIZE(pat) - VARHDRSZ);
  
! 	result = (SB_MatchText(s, slen, p, plen) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 270,306 ****
  	Name		str = PG_GETARG_NAME(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
! 	char	   *s,
! 			   *p;
! 	int			slen,
! 				plen;
! 
! 	if (pg_database_encoding_max_length() == 1)
! 	{
! 		s = NameStr(*str);
! 		slen = strlen(s);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
! 	}
! 	else
! 	{
! 		/* Force inputs to lower case to achieve case insensitivity */
! 		text	   *strtext;
  
! 		strtext = DatumGetTextP(DirectFunctionCall1(name_text,
  													NameGetDatum(str)));
! 		strtext = DatumGetTextP(DirectFunctionCall1(lower,
! 												  PointerGetDatum(strtext)));
! 		pat = DatumGetTextP(DirectFunctionCall1(lower,
! 												PointerGetDatum(pat)));
! 
! 		s = VARDATA(strtext);
! 		slen = (VARSIZE(strtext) - VARHDRSZ);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MBMatchTextIC(s, slen, p, plen) == LIKE_TRUE);
! 	}
  
  	PG_RETURN_BOOL(result);
  }
--- 283,293 ----
  	Name		str = PG_GETARG_NAME(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
! 	text	   *strtext;
  
! 	strtext = DatumGetTextP(DirectFunctionCall1(name_text,
  													NameGetDatum(str)));
! 	result = (Generic_Text_IC_like(strtext, pat) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 311,347 ****
  	Name		str = PG_GETARG_NAME(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
! 	char	   *s,
! 			   *p;
! 	int			slen,
! 				plen;
! 
! 	if (pg_database_encoding_max_length() == 1)
! 	{
! 		s = NameStr(*str);
! 		slen = strlen(s);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
! 	}
! 	else
! 	{
! 		/* Force inputs to lower case to achieve case insensitivity */
! 		text	   *strtext;
  
! 		strtext = DatumGetTextP(DirectFunctionCall1(name_text,
  													NameGetDatum(str)));
! 		strtext = DatumGetTextP(DirectFunctionCall1(lower,
! 												  PointerGetDatum(strtext)));
! 		pat = DatumGetTextP(DirectFunctionCall1(lower,
! 												PointerGetDatum(pat)));
! 
! 		s = VARDATA(strtext);
! 		slen = (VARSIZE(strtext) - VARHDRSZ);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MBMatchTextIC(s, slen, p, plen) != LIKE_TRUE);
! 	}
  
  	PG_RETURN_BOOL(result);
  }
--- 298,308 ----
  	Name		str = PG_GETARG_NAME(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
! 	text	   *strtext;
  
! 	strtext = DatumGetTextP(DirectFunctionCall1(name_text,
  													NameGetDatum(str)));
! 	result = (Generic_Text_IC_like(strtext, pat) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 352,383 ****
  	text	   *str = PG_GETARG_TEXT_P(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
- 	char	   *s,
- 			   *p;
- 	int			slen,
- 				plen;
  
! 	if (pg_database_encoding_max_length() == 1)
! 	{
! 		s = VARDATA(str);
! 		slen = (VARSIZE(str) - VARHDRSZ);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
! 	}
! 	else
! 	{
! 		/* Force inputs to lower case to achieve case insensitivity */
! 		str = DatumGetTextP(DirectFunctionCall1(lower,
! 												PointerGetDatum(str)));
! 		pat = DatumGetTextP(DirectFunctionCall1(lower,
! 												PointerGetDatum(pat)));
! 		s = VARDATA(str);
! 		slen = (VARSIZE(str) - VARHDRSZ);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MBMatchTextIC(s, slen, p, plen) == LIKE_TRUE);
! 	}
  
  	PG_RETURN_BOOL(result);
  }
--- 313,320 ----
  	text	   *str = PG_GETARG_TEXT_P(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
  
! 	result = (Generic_Text_IC_like(str, pat) == LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 388,419 ****
  	text	   *str = PG_GETARG_TEXT_P(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
- 	char	   *s,
- 			   *p;
- 	int			slen,
- 				plen;
  
! 	if (pg_database_encoding_max_length() == 1)
! 	{
! 		s = VARDATA(str);
! 		slen = (VARSIZE(str) - VARHDRSZ);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
! 	}
! 	else
! 	{
! 		/* Force inputs to lower case to achieve case insensitivity */
! 		str = DatumGetTextP(DirectFunctionCall1(lower,
! 												PointerGetDatum(str)));
! 		pat = DatumGetTextP(DirectFunctionCall1(lower,
! 												PointerGetDatum(pat)));
! 		s = VARDATA(str);
! 		slen = (VARSIZE(str) - VARHDRSZ);
! 		p = VARDATA(pat);
! 		plen = (VARSIZE(pat) - VARHDRSZ);
! 		result = (MBMatchTextIC(s, slen, p, plen) != LIKE_TRUE);
! 	}
  
  	PG_RETURN_BOOL(result);
  }
--- 325,332 ----
  	text	   *str = PG_GETARG_TEXT_P(0);
  	text	   *pat = PG_GETARG_TEXT_P(1);
  	bool		result;
  
! 	result = (Generic_Text_IC_like(str, pat) != LIKE_TRUE);
  
  	PG_RETURN_BOOL(result);
  }
***************
*** 430,436 ****
  	text	   *result;
  
  	if (pg_database_encoding_max_length() == 1)
! 		result = do_like_escape(pat, esc);
  	else
  		result = MB_do_like_escape(pat, esc);
  
--- 343,349 ----
  	text	   *result;
  
  	if (pg_database_encoding_max_length() == 1)
! 		result = SB_do_like_escape(pat, esc);
  	else
  		result = MB_do_like_escape(pat, esc);
  
***************
*** 446,624 ****
  {
  	bytea	   *pat = PG_GETARG_BYTEA_P(0);
  	bytea	   *esc = PG_GETARG_BYTEA_P(1);
! 	bytea	   *result;
! 	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++ = '\\';
! 			BYTEA_CopyAdvChar(r, p, plen);
! 		}
! 	}
! 	else
! 	{
! 		/*
! 		 * The specified escape must be only a single character.
! 		 */
! 		BYTEA_NextChar(e, elen);
! 		if (elen != 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
! 					 errmsg("invalid escape string"),
! 				  errhint("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_BYTEA_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 (BYTEA_CHAREQ(p, e) && !afterescape)
! 			{
! 				*r++ = '\\';
! 				BYTEA_NextChar(p, plen);
! 				afterescape = true;
! 			}
! 			else if (*p == '\\')
! 			{
! 				*r++ = '\\';
! 				if (!afterescape)
! 					*r++ = '\\';
! 				BYTEA_NextChar(p, plen);
! 				afterescape = false;
! 			}
! 			else
! 			{
! 				BYTEA_CopyAdvChar(r, p, plen);
! 				afterescape = false;
! 			}
! 		}
! 	}
! 
! 	SET_VARSIZE(result, r - ((char *) result));
  
! 	PG_RETURN_BYTEA_P(result);
  }
  
- /*
-  * Same as above, but specifically for bytea (binary) datatype
-  */
- static int
- MatchBytea(char *t, int tlen, 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 */
- 			BYTEA_NextChar(p, plen);
- 			if ((plen <= 0) || !BYTEA_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 == '%'))
- 				BYTEA_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 (BYTEA_CHAREQ(t, p) || (*p == '\\') || (*p == '_'))
- 				{
- 					int			matched = MatchBytea(t, tlen, p, plen);
- 
- 					if (matched != LIKE_FALSE)
- 						return matched; /* TRUE or ABORT */
- 				}
- 
- 				BYTEA_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 != '_') && !BYTEA_CHAREQ(t, p))
- 		{
- 			/*
- 			 * Not the single-character wildcard and no explicit match? Then
- 			 * time to quit...
- 			 */
- 			return LIKE_FALSE;
- 		}
- 
- 		BYTEA_NextChar(t, tlen);
- 		BYTEA_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 */
- 		BYTEA_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;
- }	/* MatchBytea() */
--- 359,366 ----
  {
  	bytea	   *pat = PG_GETARG_BYTEA_P(0);
  	bytea	   *esc = PG_GETARG_BYTEA_P(1);
! 	bytea	   *result = SB_do_like_escape((text *)pat, (text *)esc);
  
! 	PG_RETURN_BYTEA_P((bytea *)result);
  }
  
Index: src/backend/utils/adt/like_match.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/like_match.c,v
retrieving revision 1.15
diff -c -r1.15 like_match.c
*** src/backend/utils/adt/like_match.c	27 Feb 2007 23:48:08 -0000	1.15
--- src/backend/utils/adt/like_match.c	1 Jun 2007 02:21:59 -0000
***************
*** 3,20 ****
   * like_match.c
   *	  like expression handling internal code.
   *
!  * This file is included by like.c *twice*, to provide an optimization
!  * for 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-2007, PostgreSQL Global Development Group
   *
--- 3,18 ----
   * like_match.c
   *	  like expression handling internal code.
   *
!  * This file is included by like.c three times, to provide natching code for
!  * single-byte encodings, UTF8, and for other multi-byte encodings.
!  * UTF8 is a special case because we can use a much more efficient version
!  * of NextChar than can be used for other multi-byte encodings.
   *
   * Before the inclusion, we need to define following macros:
   *
!  * NextChar 
!  * MatchText - to name of function wanted
!  * do_like_escape - name of function if wanted - needs CHAREQ and CopyAdvChar
   *
   * Copyright (c) 1996-2007, PostgreSQL Global Development Group
   *
***************
*** 81,97 ****
  	{
  		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;
--- 79,101 ----
  	{
  		if (*p == '\\')
  		{
! 			/* Next byte must match literally, whatever it is */
! 			NextByte(p, plen);
! 			if ((plen <= 0) || *p != *t )
  				return LIKE_FALSE;
  		}
  		else if (*p == '%')
  		{
+ 			/*
+ 			 * % processing is essentially a search for a match for what 
+ 			 * follows the %, plus a recursive match of the remainder.
+ 			 * We succeed if and only if both conditions are met.
+ 			 */
+ 
  			/* %% is the same as % according to the SQL standard */
  			/* Advance past all %'s */
  			while ((plen > 0) && (*p == '%'))
! 				NextByte(p, plen);
  			/* Trailing percent matches everything. */
  			if (plen <= 0)
  				return LIKE_TRUE;
***************
*** 100,206 ****
  			 * 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(char *t, int tlen, 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);
  			}
  
  			/*
--- 104,165 ----
  			 * Otherwise, scan for a text position at which we can match the
  			 * rest of the pattern.
  			 */
! 			if (*p == '_')
  
! 			{
! 				/* %_ is the same as _% - avoid matching _ repeatedly */
  
  				NextChar(t, tlen);
! 				NextByte(p, plen);
  
! 				if (tlen == 0)
! 				{
! 					return (plen == 0) ? LIKE_TRUE : LIKE_ABORT;
! 				}
! 				else if (plen == 0)
! 				{
! 					return LIKE_FALSE;
! 				}
  
! 				while (tlen > 0)
! 				{
! 					int			matched = MatchText(t, tlen, p, plen);
! 						
! 					if (matched != LIKE_FALSE)
! 							return matched; /* TRUE or ABORT */
  
! 					NextChar(t, tlen);
! 				}
! 			}
! 			else
! 			{
  
! 				char firstpat = *p ;
  
! 				if (*p == '\\')
! 				{
! 					if (plen < 2)
! 						return LIKE_FALSE;
! 					firstpat = p[1];
! 				}
  
! 				while (tlen > 0)
  				{
! 					/*
! 					 * Optimization to prevent most recursion: don't recurse
! 					 * unless first pattern byte matches first text byte.
! 					 */
! 					if (*t == firstpat)
! 					{
! 						int			matched = MatchText(t, tlen, p, plen);
! 						
! 						if (matched != LIKE_FALSE)
! 							return matched; /* TRUE or ABORT */
! 					}
  
! 					NextChar(t, tlen);
  
! 				}
  			}
  
  			/*
***************
*** 209,215 ****
  			 */
  			return LIKE_ABORT;
  		}
! 		else if ((*p != '_') && !ICHAREQ(t, p))
  		{
  			/*
  			 * Not the single-character wildcard and no explicit match? Then
--- 168,180 ----
  			 */
  			return LIKE_ABORT;
  		}
! 		else if (*p == '_')
! 		{
! 			NextChar(t, tlen);
! 			NextByte(p, plen);
! 			continue;
! 		}
! 		else if (*t != *p)
  		{
  			/*
  			 * Not the single-character wildcard and no explicit match? Then
***************
*** 217,225 ****
  			 */
  			return LIKE_FALSE;
  		}
! 
! 		NextChar(t, tlen);
! 		NextChar(p, plen);
  	}
  
  	if (tlen > 0)
--- 182,194 ----
  			 */
  			return LIKE_FALSE;
  		}
! 		/*
! 		 * It is safe to use NextByte instead of NextChar here, even for
! 		 * multi-byte character sets, because we are not following 
! 		 * immediately after a wildcard character.
! 		 */
! 		NextByte(t, tlen);
! 		NextByte(p, plen);
  	}
  
  	if (tlen > 0)
***************
*** 228,234 ****
  	/* 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;
  
--- 197,204 ----
  	/* End of input string.  Do we have matching pattern remaining? */
  	while ((plen > 0) && (*p == '%'))	/* allow multiple %'s at end of
  										 * pattern */
! 		NextByte(p, plen);
! 
  	if (plen <= 0)
  		return LIKE_TRUE;
  
***************
*** 237,248 ****
  	 * 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)
  {
--- 207,220 ----
  	 * matching this pattern.
  	 */
  	return LIKE_ABORT;
! }	/* MatchText() */
  
  /*
   * like_escape() --- given a pattern and an ESCAPE string,
   * convert the pattern to use Postgres' standard backslash escape convention.
   */
+ #ifdef do_like_escape
+ 
  static text *
  do_like_escape(text *pat, text *esc)
  {
***************
*** 336,338 ****
--- 308,324 ----
  
  	return result;
  }
+ #endif /* do_like_escape */
+ 
+ #ifdef CHAREQ
+ #undef CHAREQ
+ #endif
+ 
+ #undef NextChar
+ #undef CopyAdvChar
+ #undef MatchText
+ 
+ #ifdef do_like_escape
+ #undef do_like_escape
+ #endif
+ 
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to