Re: UTF8MatchText - Mailing list pgsql-patches
From | Andrew Dunstan |
---|---|
Subject | Re: UTF8MatchText |
Date | |
Msg-id | 4651CFF5.6010709@dunslane.net Whole thread Raw |
In response to | Re: UTF8MatchText (Tom Lane <tgl@sss.pgh.pa.us>) |
List | pgsql-patches |
Tom Lane wrote: > Andrew Dunstan <andrew@dunslane.net> writes: > >> But why are we doing that CHAREQ? >> > > To avoid the cost of the recursive call, just like it says. > > >> If it succeeds we'll >> just do it again when we recurse, I think. >> > > If you move the other two cases then you could advance t and p before > entering the recursion. > > > Yeah. Since I have removed the "_" case I believe it's now safe there to use BYTEEQ/NextByte, and since they are sufficiently cheap it's not worth worrying about. Attached is a patch version that I think draws together all the threads of discussion so far. It's in fact quite a lot simpler than the existing code, with no special UTF8 case - this should improve LIKE/ILIKE processing for all charsets. More eyeballs please for nasty corner cases. cheers andrew ? src/backend/utils/adt/.deps 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 21 May 2007 16:50:48 -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,48 ---- #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 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) \ --- 72,84 ---- * 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)--) + #define BYTEEQ(p1, p2) (*(p1) == *(p2)) + /* 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) \ *************** *** 89,122 **** while (__l-- > 0) \ *(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 --- 87,137 ---- while (__l-- > 0) \ *(dst)++ = *(src)++; \ } while (0) + #define BYTEEQIC(b1, b2) BYTEEQ(b1, b2) ! #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) BYTEEQ(p1, p2) ! #define NextChar(p, plen) NextByte(p, plen) #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--) + #define BYTEEQIC(b1, b2) BYTEEQ(b1, b2) + + #define MatchText SB_MatchText + #define do_like_escape SB_do_like_escape #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 ! 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); } --- 153,159 ---- 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); } --- 174,180 ---- 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); } --- 195,201 ---- 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); } --- 216,222 ---- 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); } --- 237,243 ---- 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); } --- 258,264 ---- 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); } --- 273,283 ---- 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); } --- 288,298 ---- 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); } --- 303,310 ---- 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); } --- 315,322 ---- 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); --- 333,339 ---- 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() */ --- 349,356 ---- { 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 21 May 2007 16:50:48 -0000 *************** *** 9,19 **** * 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 --- 9,18 ---- * Before the inclusion, we need to define following macros: * * CHAREQ ! * BYTEEQIC * NextChar * CopyAdvChar * MatchText (MBMatchText) * do_like_escape (MB_do_like_escape) * * Copyright (c) 1996-2007, PostgreSQL Global Development Group *************** *** 81,89 **** { 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 == '%') --- 80,88 ---- { if (*p == '\\') { ! /* Next byte must match literally, whatever it is */ ! NextByte(p, plen); ! if ((plen <= 0) || !BYTEEQ(t, p)) return LIKE_FALSE; } else if (*p == '%') *************** *** 91,97 **** /* %% 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; --- 90,96 ---- /* %% 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); } /* --- 99,147 ---- * Otherwise, scan for a text position at which we can match the * rest of the pattern. */ ! if (*p == '_') { ! while (tlen > 0) { int matched = MatchText(t, tlen, p, plen); ! if (matched != LIKE_FALSE) ! return matched; /* TRUE or ABORT */ ! NextChar(t, tlen); ! } } ! else if (*p == '\\') { ! while (tlen > 0) { ! int matched = MatchText(t, tlen, p, plen); ! if (matched != LIKE_FALSE) ! return matched; /* TRUE or ABORT */ ! ! NextByte(t, tlen); } + } + else + { + + while (tlen > 0) + { + /* + * Optimization to prevent most recursion: don't recurse + * unless first pattern char matches this text char. + */ + if (BYTEEQ(t, p)) + { + int matched = MatchText(t, tlen, p, plen); + + if (matched != LIKE_FALSE) + return matched; /* TRUE or ABORT */ + } ! NextByte(t, tlen); ! } } /* *************** *** 209,215 **** */ return LIKE_ABORT; } ! else if ((*p != '_') && !ICHAREQ(t, p)) { /* * Not the single-character wildcard and no explicit match? Then --- 150,162 ---- */ return LIKE_ABORT; } ! else if (*p == '_') ! { ! NextChar(t, tlen); ! NextByte(p, plen); ! continue; ! } ! else if (!BYTEEQ(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) --- 164,176 ---- */ 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; --- 179,186 ---- /* 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) { --- 189,202 ---- * 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 **** --- 290,302 ---- return result; } + #endif /* do_like_escape */ + + #undef CHAREQ + #undef NextChar + #undef CopyAdvChar + #undef MatchText + + #ifdef do_like_escape + #undef do_like_escape + #endif
pgsql-patches by date: