/*-------------------------------------------------------------------------
*
- * like.c--
- * like expression handling code.
+ * like.c
+ * like expression handling code.
*
- * Copyright (c) 1994, Regents of the University of California
+ * NOTES
+ * A big hack of the regexp.c code!! Contributed by
+ * Keith Parks <emkxp01@mtcc.demon.co.uk> (7/95).
*
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * /usr/local/devel/pglite/cvs/src/backend/utils/adt/like.c,v 1.1 1995/07/30 23:55:36 emkxp01 Exp
- *
- *
- * NOTES
- * A big hack of the regexp.c code!! Contributed by
- * Keith Parks <emkxp01@mtcc.demon.co.uk> (7/95).
- *
+ * $PostgreSQL: pgsql/src/backend/utils/adt/like.c,v 1.66 2006/10/04 00:29:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
-#include <string.h>
-#include "postgres.h" /* postgres system include file */
-#include "utils/elog.h" /* for logging postgres errors */
-#include "utils/palloc.h"
-#include "utils/builtins.h" /* where the function declarations go */
+#include "postgres.h"
+
+#include <ctype.h>
+
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+
+
+#define LIKE_TRUE 1
+#define LIKE_FALSE 0
+#define LIKE_ABORT (-1)
-int like(char *text, char *p);
+
+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;
+
+ /* Optimization: quickly compare the first byte. */
+ if (*p1 != *p2)
+ return 0;
+
+ p1_len = pg_mblen(p1);
+ if (pg_mblen(p2) != p1_len)
+ return 0;
+
+ /* They are the same length */
+ while (p1_len--)
+ {
+ if (*p1++ != *p2++)
+ return 0;
+ }
+ return 1;
+}
/*
- * interface routines called by the function manager
+ * Formerly we had a routine iwchareq() here that tried to do case-insensitive
+ * comparison of multibyte characters. It did not work at all, however,
+ * because it relied on tolower() which has a single-byte API ... and
+ * towlower() wouldn't be much better since we have no suitably cheap way
+ * 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) \
+ do { int __l = pg_mblen(src); \
+ (srclen) -= __l; \
+ 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)--)
+
+
/*
- fixedlen_like:
-
- a generic fixed length like routine
- s - the string to match against (not necessarily null-terminated)
- p - the pattern
- charlen - the length of the string
-*/
-static bool
-fixedlen_like(char *s, struct varlena* p, int charlen)
+ * interface routines called by the function manager
+ */
+
+Datum
+namelike(PG_FUNCTION_ARGS)
{
- char *sterm, *pterm;
- int result;
-
- if (!s || !p)
- return FALSE;
-
- /* be sure sterm is null-terminated */
- sterm = (char *) palloc(charlen + 1);
- memset(sterm, 0, charlen + 1);
- strncpy(sterm, s, charlen);
-
- /* p is a text = varlena, not a string so we have to make
- * a string from the vl_data field of the struct. */
-
- /* palloc the length of the text + the null character */
- pterm = (char *) palloc(VARSIZE(p) - VARHDRSZ + 1);
- memmove(pterm, VARDATA(p), VARSIZE(p) - VARHDRSZ);
- *(pterm + VARSIZE(p) - VARHDRSZ) = (char)NULL;
-
- /* do the regexp matching */
- result = like(sterm, pterm);
-
- pfree(sterm);
- pfree(pterm);
-
- return ((bool) result);
+ Name str = PG_GETARG_NAME(0);
+ text *pat = PG_GETARG_TEXT_P(1);
+ bool result;
+ char *s,
+ *p;
+ int slen,
+ plen;
+
+ s = NameStr(*str);
+ slen = strlen(s);
+ 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);
}
-bool
-char2like(uint16 arg1, struct varlena *p)
+Datum
+namenlike(PG_FUNCTION_ARGS)
{
- char *s = (char *) &arg1;
- return (fixedlen_like(s, p, 2));
-}
+ Name str = PG_GETARG_NAME(0);
+ text *pat = PG_GETARG_TEXT_P(1);
+ bool result;
+ char *s,
+ *p;
+ int slen,
+ plen;
-bool
-char2nlike(uint16 arg1, struct varlena *p)
-{
- return (!char2like(arg1, p));
+ s = NameStr(*str);
+ slen = strlen(s);
+ 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);
}
-bool
-char4like(uint32 arg1, struct varlena *p)
+Datum
+textlike(PG_FUNCTION_ARGS)
{
- char *s = (char *) &arg1;
- return (fixedlen_like(s, p, 4));
+ text *str = PG_GETARG_TEXT_P(0);
+ text *pat = PG_GETARG_TEXT_P(1);
+ bool result;
+ char *s,
+ *p;
+ int slen,
+ plen;
+
+ s = VARDATA(str);
+ slen = (VARSIZE(str) - VARHDRSZ);
+ 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);
}
-bool
-char4nlike(uint32 arg1, struct varlena *p)
+Datum
+textnlike(PG_FUNCTION_ARGS)
{
- return (!char4like(arg1, p));
+ text *str = PG_GETARG_TEXT_P(0);
+ text *pat = PG_GETARG_TEXT_P(1);
+ bool result;
+ char *s,
+ *p;
+ int slen,
+ plen;
+
+ s = VARDATA(str);
+ slen = (VARSIZE(str) - VARHDRSZ);
+ 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);
}
-bool
-char8like(char *s, struct varlena *p)
+Datum
+bytealike(PG_FUNCTION_ARGS)
{
- return (fixedlen_like(s, p, 8));
+ bytea *str = PG_GETARG_BYTEA_P(0);
+ bytea *pat = PG_GETARG_BYTEA_P(1);
+ bool result;
+ 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);
}
-bool
-char8nlike(char *s, struct varlena *p)
+Datum
+byteanlike(PG_FUNCTION_ARGS)
{
- return (!char8like(s, p));
+ bytea *str = PG_GETARG_BYTEA_P(0);
+ bytea *pat = PG_GETARG_BYTEA_P(1);
+ bool result;
+ 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);
}
-bool
-char16like(char *s, struct varlena *p)
+/*
+ * Case-insensitive versions
+ */
+
+Datum
+nameiclike(PG_FUNCTION_ARGS)
{
- return (fixedlen_like(s, p, 16));
+ 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);
}
-bool
-char16nlike(char *s, struct varlena *p)
+
+Datum
+nameicnlike(PG_FUNCTION_ARGS)
{
- return (!char16like(s, p));
+ 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);
}
-bool
-namelike(NameData *n, struct varlena *p)
+Datum
+texticlike(PG_FUNCTION_ARGS)
{
- return (fixedlen_like(n->data, p, NAMEDATALEN));
+ 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);
}
-bool
-namenlike(NameData *s, struct varlena *p)
+Datum
+texticnlike(PG_FUNCTION_ARGS)
{
- return (!namelike(s, p));
+ 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);
}
-bool
-textlike(struct varlena *s, struct varlena *p)
+/*
+ * like_escape() --- given a pattern and an ESCAPE string,
+ * convert the pattern to use Postgres' standard backslash escape convention.
+ */
+Datum
+like_escape(PG_FUNCTION_ARGS)
{
- return (fixedlen_like(VARDATA(s), p, VARSIZE(s) - VARHDRSZ));
+ text *pat = PG_GETARG_TEXT_P(0);
+ text *esc = PG_GETARG_TEXT_P(1);
+ text *result;
+
+ if (pg_database_encoding_max_length() == 1)
+ result = do_like_escape(pat, esc);
+ else
+ result = MB_do_like_escape(pat, esc);
+
+ PG_RETURN_TEXT_P(result);
}
-bool textnlike(struct varlena *s, struct varlena *p)
+/*
+ * 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)
{
- return (!textlike(s, p));
-}
+ 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;
+ }
+ }
+ }
+
+ VARATT_SIZEP(result) = r - ((char *) result);
-/* $Revision: 1.1.1.1 $
-** "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.
-** 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. (I suspect the standard
-** handles "%%" as a single literal percent)
-**
-** 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]
-**
-*/
-
-#define LIKE_TRUE 1
-#define LIKE_FALSE 0
-#define LIKE_ABORT -1
+ PG_RETURN_BYTEA_P(result);
+}
/*
-** Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
-*/
+ * Same as above, but specifically for bytea (binary) datatype
+ */
static int
-DoMatch(register char *text, register char *p)
+MatchBytea(char *t, int tlen, char *p, int plen)
{
- register int matched;
-
- for ( ; *p; text++, p++) {
- if (*text == '\0' && *p != '%')
- return LIKE_ABORT;
- switch (*p) {
- case '\\':
- /* Literal match with following character. */
- p++;
- /* FALLTHROUGH */
- default:
- if (*text != *p)
- return LIKE_FALSE;
- continue;
- case '_':
- /* Match anything. */
- continue;
- case '%':
- while (*++p == '%')
- /* Consecutive percents act just like one. */
- continue;
- if (*p == '\0')
- /* Trailing percent matches everything. */
+ /* Fast path for match-everything pattern */
+ if ((plen == 1) && (*p == '%'))
return LIKE_TRUE;
- while (*text)
- if ((matched = DoMatch(text++, p)) != LIKE_FALSE)
- return matched;
- return LIKE_ABORT;
+
+ 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);
}
- }
- return *text == '\0';
-}
+ 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;
-/*
-** User-level routine. Returns TRUE or FALSE.
-*/
-int
-like(char *text, char *p)
-{
- if (p[0] == '%' && p[1] == '\0')
- return TRUE;
- return (DoMatch(text, p) == LIKE_TRUE);
-}
+ /*
+ * End of text with no match, so no point in trying later places to start
+ * matching this pattern.
+ */
+ return LIKE_ABORT;
+} /* MatchBytea() */