diff options
Diffstat (limited to 'src/backend/utils/adt/like.c')
-rw-r--r-- | src/backend/utils/adt/like.c | 234 |
1 files changed, 233 insertions, 1 deletions
diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c index f27bc067d6a..4f6d6d0212b 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.45 2001/03/22 03:59:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.46 2001/09/14 17:46:40 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,8 @@ static int MatchText(unsigned char *t, int tlen, unsigned char *p, int plen); 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); #ifdef MULTIBYTE @@ -120,6 +122,9 @@ iwchareq(unsigned char *p1, unsigned char *p2) #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)--) /* * interface routines called by the function manager @@ -209,6 +214,48 @@ textnlike(PG_FUNCTION_ARGS) PG_RETURN_BOOL(result); } +Datum +bytealike(PG_FUNCTION_ARGS) +{ + bytea *str = PG_GETARG_BYTEA_P(0); + bytea *pat = PG_GETARG_BYTEA_P(1); + bool result; + unsigned char *s, + *p; + int slen, + plen; + + s = VARDATA(str); + slen = (VARSIZE(str) - VARHDRSZ); + p = VARDATA(pat); + plen = (VARSIZE(pat) - VARHDRSZ); + + result = (MatchBytea(s, slen, p, plen) == LIKE_TRUE); + + PG_RETURN_BOOL(result); +} + +Datum +byteanlike(PG_FUNCTION_ARGS) +{ + bytea *str = PG_GETARG_BYTEA_P(0); + bytea *pat = PG_GETARG_BYTEA_P(1); + bool result; + unsigned char *s, + *p; + int slen, + plen; + + s = VARDATA(str); + slen = (VARSIZE(str) - VARHDRSZ); + p = VARDATA(pat); + plen = (VARSIZE(pat) - VARHDRSZ); + + result = (MatchBytea(s, slen, p, plen) != LIKE_TRUE); + + PG_RETURN_BOOL(result); +} + /* * Case-insensitive versions */ @@ -395,6 +442,103 @@ like_escape(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(result); } +/* + * like_escape_bytea() --- given a pattern and an ESCAPE string, + * convert the pattern to use Postgres' standard backslash escape convention. + */ +Datum +like_escape_bytea(PG_FUNCTION_ARGS) +{ + bytea *pat = PG_GETARG_BYTEA_P(0); + bytea *esc = PG_GETARG_BYTEA_P(1); + bytea *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++ = '\\'; + BYTEA_CopyAdvChar(r, p, plen); + } + } + else + { + + /* + * The specified escape must be only a single character. + */ + BYTEA_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_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; + } + } + } + + VARATT_SIZEP(result) = r - ((unsigned char *) result); + + PG_RETURN_BYTEA_P(result); +} /* ** Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. @@ -614,3 +758,91 @@ MatchTextIC(unsigned char *t, int tlen, unsigned char *p, int plen) */ return LIKE_ABORT; } /* MatchTextIC() */ + +/* + * Same as above, but specifically for bytea (binary) datatype + */ +static int +MatchBytea(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 */ + 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() */ |