1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* like.c--
|
|
|
|
* like expression handling code.
|
|
|
|
*
|
|
|
|
* 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).
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "postgres.h" /* postgres system include file */
|
|
|
|
#include "utils/palloc.h"
|
|
|
|
#include "utils/builtins.h" /* where the function declarations go */
|
|
|
|
|
1997-08-19 23:40:56 +02:00
|
|
|
static int like(char *text, char *p);
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* interface routines called by the function manager
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
char *sterm, *pterm;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
if (!s || !p)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* be sure sterm is null-terminated */
|
|
|
|
sterm = (char *) palloc(charlen + 1);
|
1997-08-12 22:16:25 +02:00
|
|
|
strNcpy(sterm, s, charlen);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
char2like(uint16 arg1, struct varlena *p)
|
|
|
|
{
|
|
|
|
char *s = (char *) &arg1;
|
|
|
|
return (fixedlen_like(s, p, 2));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
char2nlike(uint16 arg1, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (!char2like(arg1, p));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
char4like(uint32 arg1, struct varlena *p)
|
|
|
|
{
|
|
|
|
char *s = (char *) &arg1;
|
|
|
|
return (fixedlen_like(s, p, 4));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
char4nlike(uint32 arg1, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (!char4like(arg1, p));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
char8like(char *s, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (fixedlen_like(s, p, 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
char8nlike(char *s, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (!char8like(s, p));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
char16like(char *s, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (fixedlen_like(s, p, 16));
|
|
|
|
}
|
|
|
|
bool
|
|
|
|
char16nlike(char *s, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (!char16like(s, p));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
namelike(NameData *n, struct varlena *p)
|
|
|
|
{
|
1996-07-09 08:39:19 +02:00
|
|
|
if (!n) return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
return (fixedlen_like(n->data, p, NAMEDATALEN));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
namenlike(NameData *s, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (!namelike(s, p));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
textlike(struct varlena *s, struct varlena *p)
|
|
|
|
{
|
1996-07-09 08:39:19 +02:00
|
|
|
if (!s) return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
return (fixedlen_like(VARDATA(s), p, VARSIZE(s) - VARHDRSZ));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool textnlike(struct varlena *s, struct varlena *p)
|
|
|
|
{
|
|
|
|
return (!textlike(s, p));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-08-19 23:40:56 +02:00
|
|
|
/* $Revision: 1.6 $
|
1996-07-09 08:22:35 +02:00
|
|
|
** "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
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
DoMatch(register char *text, register char *p)
|
|
|
|
{
|
|
|
|
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. */
|
|
|
|
return LIKE_TRUE;
|
|
|
|
while (*text)
|
|
|
|
if ((matched = DoMatch(text++, p)) != LIKE_FALSE)
|
|
|
|
return matched;
|
|
|
|
return LIKE_ABORT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return *text == '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** User-level routine. Returns TRUE or FALSE.
|
|
|
|
*/
|
1997-08-19 23:40:56 +02:00
|
|
|
static int
|
1996-07-09 08:22:35 +02:00
|
|
|
like(char *text, char *p)
|
|
|
|
{
|
|
|
|
if (p[0] == '%' && p[1] == '\0')
|
|
|
|
return TRUE;
|
|
|
|
return (DoMatch(text, p) == LIKE_TRUE);
|
|
|
|
}
|