Commit 30ab107d authored by Thomas G. Lockhart's avatar Thomas G. Lockhart

Implement LIKE/ESCAPE. Change parser to use like()/notlike()

 rather than the "~~" operator; this made it easy to add ESCAPE features.
Implement ILIKE, NOT ILIKE, and the ESCAPE clause for them.
 afaict this is not MultiByte clean, but lots of other stuff isn't either.
Fix up underlying support code for LIKE/NOT LIKE.
 Things should be faster and does not require internal string copying.
Update regression test to add explicit checks for
 LIKE/NOT LIKE/ILIKE/NOT ILIKE.
Remove colon and semi-colon operators as threatened in 7.0.
Implement SQL99 COMMIT/AND NO CHAIN.
 Throw elog(ERROR) on COMMIT/AND CHAIN per spec
 since we don't yet support it.
Implement SQL99 CREATE/DROP SCHEMA as equivalent to CREATE DATABASE.
 This is only a stopgap or demo since schemas will have another
 implementation soon.
Remove a few unused production rules to get rid of warnings
 which crept in on the last commit.
Fix up tabbing in some places by removing embedded spaces.
parent df402346
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.181 2000/07/30 22:13:50 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.182 2000/08/06 18:05:21 thomas Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -123,7 +123,7 @@ static void doNegateFloat(Value *v);
AlterSchemaStmt, AlterTableStmt, ClosePortalStmt,
CopyStmt, CreateStmt, CreateAsStmt, CreateSchemaStmt, CreateSeqStmt, DefineStmt, DropStmt,
TruncateStmt, CommentStmt,
ExtendStmt, FetchStmt, GrantStmt, CreateTrigStmt, DropTrigStmt,
ExtendStmt, FetchStmt, GrantStmt, CreateTrigStmt, DropSchemaStmt, DropTrigStmt,
CreatePLangStmt, DropPLangStmt,
IndexStmt, ListenStmt, UnlistenStmt, LockStmt, OptimizableStmt,
ProcedureStmt, ReindexStmt, RemoveAggrStmt, RemoveOperStmt,
......@@ -191,7 +191,7 @@ static void doNegateFloat(Value *v);
%type <list> for_update_clause, update_list
%type <boolean> opt_all
%type <boolean> opt_table
%type <boolean> opt_trans
%type <boolean> opt_chain, opt_trans
%type <jexpr> from_expr, join_clause, join_expr
%type <jexpr> join_clause_with_union, join_expr_with_union
......@@ -252,7 +252,7 @@ static void doNegateFloat(Value *v);
%type <typnam> Typename, SimpleTypename, ConstTypename
Generic, Numeric, Geometric, Character, ConstDatetime, ConstInterval, Bit
%type <str> typename, generic, numeric, geometric, character, datetime, bit
%type <str> generic, character, datetime, bit
%type <str> extract_arg
%type <str> opt_charset, opt_collate
%type <str> opt_float
......@@ -302,7 +302,7 @@ static void doNegateFloat(Value *v);
CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
DAY_P, DEC, DECIMAL, DECLARE, DEFAULT, DELETE, DESC,
DISTINCT, DOUBLE, DROP,
ELSE, END_TRANS, EXCEPT, EXECUTE, EXISTS, EXTRACT,
ELSE, END_TRANS, ESCAPE, EXCEPT, EXECUTE, EXISTS, EXTRACT,
FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
GLOBAL, GRANT, GROUP, HAVING, HOUR_P,
IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS,
......@@ -320,7 +320,7 @@ static void doNegateFloat(Value *v);
WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
/* Keywords (in SQL3 reserved words) */
%token CHARACTERISTICS,
%token CHAIN, CHARACTERISTICS,
DEFERRABLE, DEFERRED,
IMMEDIATE, INITIALLY, INOUT,
OFF, OUT,
......@@ -345,7 +345,7 @@ static void doNegateFloat(Value *v);
DATABASE, DELIMITERS, DO,
EACH, ENCODING, EXCLUSIVE, EXPLAIN, EXTEND,
FORCE, FORWARD, FUNCTION, HANDLER,
INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
ILIKE, INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
LANCOMPILER, LIMIT, LISTEN, LOAD, LOCATION, LOCK_P,
MAXVALUE, MINVALUE, MODE, MOVE,
NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
......@@ -368,7 +368,7 @@ static void doNegateFloat(Value *v);
%right NOT
%right '='
%nonassoc '<' '>'
%nonassoc LIKE
%nonassoc LIKE ILIKE
%nonassoc OVERLAPS
%nonassoc BETWEEN
%nonassoc IN
......@@ -382,13 +382,14 @@ static void doNegateFloat(Value *v);
%left '^'
%left '|' /* this is the relation union op, not logical or */
/* Unary Operators */
%right ':'
%left ';' /* end of statement or natural log */
%right ':' /* delimiter for array ranges */
%left ';' /* end of statement */
%right UMINUS
%left '.'
%left '[' ']'
%left TYPECAST
%left UNION INTERSECT EXCEPT
%left ESCAPE
%%
/*
......@@ -432,6 +433,7 @@ stmt : AlterSchemaStmt
| ClusterStmt
| DefineStmt
| DropStmt
| DropSchemaStmt
| TruncateStmt
| CommentStmt
| DropGroupStmt
......@@ -678,7 +680,16 @@ DropGroupStmt: DROP GROUP UserId
CreateSchemaStmt: CREATE SCHEMA UserId
{
elog(ERROR, "CREATE SCHEMA not yet supported");
/* for now, just make this the same as CREATE DATABASE */
CreatedbStmt *n = makeNode(CreatedbStmt);
n->dbname = $3;
n->dbpath = NULL;
#ifdef MULTIBYTE
n->encoding = GetTemplateEncoding();
#else
n->encoding = 0;
#endif
$$ = (Node *)n;
}
;
......@@ -688,6 +699,13 @@ AlterSchemaStmt: ALTER SCHEMA UserId
}
;
DropSchemaStmt: DROP SCHEMA UserId
{
DropdbStmt *n = makeNode(DropdbStmt);
n->dbname = $3;
$$ = (Node *)n;
}
/*****************************************************************************
*
......@@ -2823,6 +2841,12 @@ TransactionStmt: ABORT_TRANS opt_trans
n->command = COMMIT;
$$ = (Node *)n;
}
| COMMIT opt_trans opt_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->command = COMMIT;
$$ = (Node *)n;
}
| END_TRANS opt_trans
{
TransactionStmt *n = makeNode(TransactionStmt);
......@@ -2835,6 +2859,12 @@ TransactionStmt: ABORT_TRANS opt_trans
n->command = ROLLBACK;
$$ = (Node *)n;
}
| ROLLBACK opt_trans opt_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->command = ROLLBACK;
$$ = (Node *)n;
}
;
opt_trans: WORK { $$ = TRUE; }
......@@ -2842,6 +2872,19 @@ opt_trans: WORK { $$ = TRUE; }
| /*EMPTY*/ { $$ = TRUE; }
;
opt_chain: AND NO CHAIN
{ $$ = FALSE; }
| AND CHAIN
{
/* SQL99 asks that conforming dbs reject AND CHAIN
* if they don't support it. So we can't just ignore it.
* - thomas 2000-08-06
*/
elog(ERROR, "COMMIT/CHAIN not yet supported");
$$ = TRUE;
}
;
/*****************************************************************************
*
......@@ -2922,8 +2965,7 @@ createdb_opt_location: LOCATION '=' Sconst { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
createdb_opt_encoding:
ENCODING '=' Sconst
createdb_opt_encoding: ENCODING '=' Sconst
{
#ifdef MULTIBYTE
int i;
......@@ -3975,14 +4017,6 @@ ConstTypename: Generic
| ConstDatetime
;
typename: generic { $$ = $1; }
| numeric { $$ = $1; }
| geometric { $$ = $1; }
| bit { $$ = $1; }
| character { $$ = $1; }
| datetime { $$ = $1; }
;
Generic: generic
{
$$ = makeNode(TypeName);
......@@ -4032,13 +4066,6 @@ Numeric: FLOAT opt_float
}
;
numeric: FLOAT { $$ = xlateSqlType("float"); }
| DOUBLE PRECISION { $$ = xlateSqlType("float8"); }
| DECIMAL { $$ = xlateSqlType("decimal"); }
| DEC { $$ = xlateSqlType("decimal"); }
| NUMERIC { $$ = xlateSqlType("numeric"); }
;
Geometric: PATH_P
{
$$ = makeNode(TypeName);
......@@ -4047,9 +4074,6 @@ Geometric: PATH_P
}
;
geometric: PATH_P { $$ = xlateSqlType("path"); }
;
opt_float: '(' Iconst ')'
{
if ($2 < 1)
......@@ -4435,16 +4459,6 @@ a_expr: c_expr
{ $$ = makeA_Expr(OP, "^", NULL, $2); }
| '|' a_expr
{ $$ = makeA_Expr(OP, "|", NULL, $2); }
| ':' a_expr
{ $$ = makeA_Expr(OP, ":", NULL, $2);
elog(NOTICE, "The ':' operator is deprecated. Use exp(x) instead."
"\n\tThis operator will be removed in a future release.");
}
| ';' a_expr
{ $$ = makeA_Expr(OP, ";", NULL, $2);
elog(NOTICE, "The ';' operator is deprecated. Use ln(x) instead."
"\n\tThis operator will be removed in a future release.");
}
| a_expr '%'
{ $$ = makeA_Expr(OP, "%", $1, NULL); }
| a_expr '^'
......@@ -4499,9 +4513,77 @@ a_expr: c_expr
{ $$ = makeA_Expr(NOT, NULL, NULL, $2); }
| a_expr LIKE a_expr
{ $$ = makeA_Expr(OP, "~~", $1, $3); }
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "like";
n->args = makeList($1, $3, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr LIKE a_expr ESCAPE a_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "like";
n->args = makeList($1, $3, $5, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr NOT LIKE a_expr
{ $$ = makeA_Expr(OP, "!~~", $1, $4); }
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "notlike";
n->args = makeList($1, $4, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr NOT LIKE a_expr ESCAPE a_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "notlike";
n->args = makeList($1, $4, $6, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr ILIKE a_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "ilike";
n->args = makeList($1, $3, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr ILIKE a_expr ESCAPE a_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "ilike";
n->args = makeList($1, $3, $5, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr NOT ILIKE a_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "inotlike";
n->args = makeList($1, $4, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr NOT ILIKE a_expr ESCAPE a_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "inotlike";
n->args = makeList($1, $4, $6, -1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| a_expr ISNULL
{ $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
......@@ -4659,16 +4741,6 @@ b_expr: c_expr
{ $$ = makeA_Expr(OP, "^", NULL, $2); }
| '|' b_expr
{ $$ = makeA_Expr(OP, "|", NULL, $2); }
| ':' b_expr
{ $$ = makeA_Expr(OP, ":", NULL, $2);
elog(NOTICE, "The ':' operator is deprecated. Use exp(x) instead."
"\n\tThis operator will be removed in a future release.");
}
| ';' b_expr
{ $$ = makeA_Expr(OP, ";", NULL, $2);
elog(NOTICE, "The ';' operator is deprecated. Use ln(x) instead."
"\n\tThis operator will be removed in a future release.");
}
| b_expr '%'
{ $$ = makeA_Expr(OP, "%", $1, NULL); }
| b_expr '^'
......@@ -5496,6 +5568,7 @@ TokenId: ABSOLUTE { $$ = "absolute"; }
| BY { $$ = "by"; }
| CACHE { $$ = "cache"; }
| CASCADE { $$ = "cascade"; }
| CHAIN { $$ = "chain"; }
| CLOSE { $$ = "close"; }
| COMMENT { $$ = "comment"; }
| COMMIT { $$ = "commit"; }
......@@ -5515,6 +5588,7 @@ TokenId: ABSOLUTE { $$ = "absolute"; }
| DROP { $$ = "drop"; }
| EACH { $$ = "each"; }
| ENCODING { $$ = "encoding"; }
| ESCAPE { $$ = "escape"; }
| EXCLUSIVE { $$ = "exclusive"; }
| EXECUTE { $$ = "execute"; }
| FETCH { $$ = "fetch"; }
......@@ -5661,6 +5735,7 @@ ColLabel: ColId { $$ = $1; }
| GLOBAL { $$ = "global"; }
| GROUP { $$ = "group"; }
| HAVING { $$ = "having"; }
| ILIKE { $$ = "ilike"; }
| INITIALLY { $$ = "initially"; }
| IN { $$ = "in"; }
| INNER_P { $$ = "inner"; }
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.79 2000/07/14 15:43:32 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.80 2000/08/06 18:05:22 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -55,6 +55,7 @@ static ScanKeyword ScanKeywords[] = {
{"cascade", CASCADE},
{"case", CASE},
{"cast", CAST},
{"chain", CHAIN},
{"char", CHAR},
{"character", CHARACTER},
{"characteristics", CHARACTERISTICS},
......@@ -101,6 +102,7 @@ static ScanKeyword ScanKeywords[] = {
{"else", ELSE},
{"encoding", ENCODING},
{"end", END_TRANS},
{"escape", ESCAPE},
{"except", EXCEPT},
{"exclusive", EXCLUSIVE},
{"execute", EXECUTE},
......@@ -124,6 +126,7 @@ static ScanKeyword ScanKeywords[] = {
{"handler", HANDLER},
{"having", HAVING},
{"hour", HOUR_P},
{"ilike", ILIKE},
{"immediate", IMMEDIATE},
{"in", IN},
{"increment", INCREMENT},
......
......@@ -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.37 2000/07/07 21:12:50 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.38 2000/08/06 18:05:41 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -20,97 +20,222 @@
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
static bool like(pg_wchar * text, pg_wchar * p);
#define LIKE_TRUE 1
#define LIKE_FALSE 0
#define LIKE_ABORT (-1)
static int MatchText(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e);
static int MatchTextLower(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e);
/*
* interface routines called by the function manager
*/
/*
fixedlen_like:
Datum
namelike(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
a generic fixed length like routine
s - the string to match against (not necessarily null-terminated)
p - the pattern (as text*)
charlen - the length of the string
*/
static bool
fixedlen_like(char *s, text *p, int charlen)
{
pg_wchar *sterm,
*pterm;
bool result;
int len;
/* be sure sterm is null-terminated */
#ifdef MULTIBYTE
sterm = (pg_wchar *) palloc((charlen + 1) * sizeof(pg_wchar));
(void) pg_mb2wchar_with_len((unsigned char *) s, sterm, charlen);
#else
sterm = (char *) palloc(charlen + 1);
memcpy(sterm, s, charlen);
sterm[charlen] = '\0';
#endif
PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
== LIKE_TRUE);
}
/*
* p is a text, not a string so we have to make a string
* from the vl_data field of the struct.
Datum
namenlike(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
!= LIKE_TRUE);
}
Datum
namelike_escape(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
== LIKE_TRUE);
}
Datum
namenlike_escape(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
!= LIKE_TRUE);
}
Datum
textlike(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
== LIKE_TRUE);
}
Datum
textnlike(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
!= LIKE_TRUE);
}
Datum
textlike_escape(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
== LIKE_TRUE);
}
Datum
textnlike_escape(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
!= LIKE_TRUE);
}
/*
* Case-insensitive versions
*/
/* palloc the length of the text + the null character */
len = VARSIZE(p) - VARHDRSZ;
#ifdef MULTIBYTE
pterm = (pg_wchar *) palloc((len + 1) * sizeof(pg_wchar));
(void) pg_mb2wchar_with_len((unsigned char *) VARDATA(p), pterm, len);
#else
pterm = (char *) palloc(len + 1);
memcpy(pterm, VARDATA(p), len);
*(pterm + len) = '\0';
#endif
Datum
inamelike(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
/* do the regexp matching */
result = like(sterm, pterm);
PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
== LIKE_TRUE);
}
pfree(sterm);
pfree(pterm);
Datum
inamenlike(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
return result;
PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
!= LIKE_TRUE);
}
Datum
namelike(PG_FUNCTION_ARGS)
inamelike_escape(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(fixedlen_like(NameStr(*n), p, strlen(NameStr(*n))));
PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
== LIKE_TRUE);
}
Datum
namenlike(PG_FUNCTION_ARGS)
inamenlike_escape(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(! fixedlen_like(NameStr(*n), p, strlen(NameStr(*n))));
PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
!= LIKE_TRUE);
}
Datum
textlike(PG_FUNCTION_ARGS)
itextlike(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(fixedlen_like(VARDATA(s), p, VARSIZE(s) - VARHDRSZ));
PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
== LIKE_TRUE);
}
Datum
textnlike(PG_FUNCTION_ARGS)
itextnlike(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
!= LIKE_TRUE);
}
Datum
itextlike_escape(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
== LIKE_TRUE);
}
Datum
itextnlike_escape(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_P(0);
text *p = PG_GETARG_TEXT_P(1);
text *e = PG_GETARG_TEXT_P(2);
PG_RETURN_BOOL(! fixedlen_like(VARDATA(s), p, VARSIZE(s) - VARHDRSZ));
PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
!= LIKE_TRUE);
}
......@@ -136,12 +261,16 @@ textnlike(PG_FUNCTION_ARGS)
** LIKE <pattern> ESCAPE <escape character>. We are a small operation
** so we force you to use '\'. - ay 7/95]
**
** OK, we now support the SQL9x LIKE <pattern> ESCAPE <char> syntax.
** We should kill the backslash escaping mechanism since it is non-standard
** and undocumented afaik.
** The code is rewritten to avoid requiring null-terminated strings,
** which in turn allows us to leave out some memcpy() operations.
** This code should be faster and take less memory, but no promises...
** - thomas 2000-08-06
**
*/
#define LIKE_TRUE 1
#define LIKE_FALSE 0
#define LIKE_ABORT (-1)
/*--------------------
* Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
*
......@@ -153,19 +282,41 @@ textnlike(PG_FUNCTION_ARGS)
* pattern either, so an upper-level % scan can stop scanning now.
*--------------------
*/
#define NextChar(p, plen) (p)++, (plen)--
static int
DoMatch(pg_wchar * text, pg_wchar * p)
MatchText(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e)
{
for (; *p && *text; text ++, p++)
/* Fast path for match-everything pattern
* Include weird case of escape character as a percent sign or underscore,
* when presumably that wildcard character becomes a literal.
*/
if ((plen == 1) && (*p == '%')
&& ! ((e != NULL) && (*e == '%')))
return LIKE_TRUE;
while ((tlen > 0) && (plen > 0))
{
/* If an escape character was specified and we find it here in the pattern,
* then we'd better have an exact match for the next character.
*/
if ((e != NULL) && (*p == *e))
{
NextChar(p, plen);
if ((plen <= 0) || (*t != *p))
return LIKE_FALSE;
}
else
{
switch (*p)
{
case '\\':
/* Literal match with following character. */
p++;
NextChar(p, plen);
/* FALLTHROUGH */
default:
if (*text !=*p)
if (*t != *p)
return LIKE_FALSE;
break;
case '_':
......@@ -174,31 +325,33 @@ DoMatch(pg_wchar * text, pg_wchar * p)
case '%':
/* %% is the same as % according to the SQL standard */
/* Advance past all %'s */
while (*p == '%')
p++;
while ((plen > 0) && (*p == '%'))
NextChar(p, plen);
/* Trailing percent matches everything. */
if (*p == '\0')
if (plen <= 0)
return LIKE_TRUE;
/*
* Otherwise, scan for a text position at which we can
* match the rest of the pattern.
*/
for (; *text; text ++)
while (tlen > 0)
{
/*
* Optimization to prevent most recursion: don't
* recurse unless first pattern char might match this
* text char.
*/
if (*text == *p || *p == '\\' || *p == '_')
if ((*t == *p) || (*p == '\\') || (*p == '_')
|| ((e != NULL) && (*p == *e)))
{
int matched = DoMatch(text, p);
int matched = MatchText(t, tlen, p, plen, e);
if (matched != LIKE_FALSE)
return matched; /* TRUE or ABORT */
}
NextChar(t, tlen);
}
/*
......@@ -209,13 +362,17 @@ DoMatch(pg_wchar * text, pg_wchar * p)
}
}
if (*text !='\0')
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 (*p == '%') /* allow multiple %'s at end of pattern */
p++;
if (*p == '\0')
while ((plen > 0) && (*p == '%')) /* allow multiple %'s at end of pattern */
NextChar(p, plen);
if (plen <= 0)
return LIKE_TRUE;
/*
......@@ -223,16 +380,101 @@ DoMatch(pg_wchar * text, pg_wchar * p)
* start matching this pattern.
*/
return LIKE_ABORT;
}
} /* MatchText() */
/*
** User-level routine. Returns TRUE or FALSE.
*/
static bool
like(pg_wchar * text, pg_wchar * p)
static int
MatchTextLower(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e)
{
/* Fast path for match-everything pattern */
if (p[0] == '%' && p[1] == '\0')
return true;
return DoMatch(text, p) == LIKE_TRUE;
}
/* Fast path for match-everything pattern
* Include weird case of escape character as a percent sign or underscore,
* when presumably that wildcard character becomes a literal.
*/
if ((plen == 1) && (*p == '%')
&& ! ((e != NULL) && (*e == '%')))
return LIKE_TRUE;
while ((tlen > 0) && (plen > 0))
{
/* If an escape character was specified and we find it here in the pattern,
* then we'd better have an exact match for the next character.
*/
if ((e != NULL) && (tolower(*p) == tolower(*e)))
{
NextChar(p, plen);
if ((plen <= 0) || (tolower(*t) != tolower(*p)))
return LIKE_FALSE;
}
else
{
switch (*p)
{
case '\\':
/* Literal match with following character. */
NextChar(p, plen);
/* FALLTHROUGH */
default:
if (tolower(*t) != tolower(*p))
return LIKE_FALSE;
break;
case '_':
/* Match any single character. */
break;
case '%':
/* %% 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 ((tolower(*t) == tolower(*p)) || (*p == '\\') || (*p == '_')
|| ((e != NULL) && (tolower(*p) == tolower(*e))))
{
int matched = MatchText(t, tlen, p, plen, e);
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;
}
}
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;
} /* MatchTextLower() */
......@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.39 2000/08/04 04:16:17 tgl Exp $
* $Id: catversion.h,v 1.40 2000/08/06 18:06:13 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200008031
#define CATALOG_VERSION_NO 200008061
#endif
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_proc.h,v 1.160 2000/08/05 14:59:19 momjian Exp $
* $Id: pg_proc.h,v 1.161 2000/08/06 18:06:13 thomas Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
......@@ -2039,6 +2039,31 @@ DATA(insert OID = 1623 ( varchar PGUID 12 f t t t 1 f 1043 "20" 100 0 0 100
DESCR("convert int8 to varchar");
DATA(insert OID = 1624 ( mul_d_interval PGUID 12 f t t t 2 f 1186 "701 1186" 100 0 0 100 mul_d_interval - ));
DATA(insert OID = 1625 ( like PGUID 12 f t t t 3 f 16 "19 25 25" 100 0 1 0 namelike_escape - ));
DESCR("matches LIKE expression");
DATA(insert OID = 1626 ( notlike PGUID 12 f t t t 3 f 16 "19 25 25" 100 0 1 0 namenlike_escape - ));
DESCR("does not match LIKE expression");
DATA(insert OID = 1627 ( ilike PGUID 12 f t t t 3 f 16 "19 25 25" 100 0 1 0 inamelike_escape - ));
DESCR("matches case-insensitive LIKE expression");
DATA(insert OID = 1628 ( inotlike PGUID 12 f t t t 3 f 16 "19 25 25" 100 0 1 0 inamenlike_escape - ));
DESCR("does not match case-insensitive LIKE expression");
DATA(insert OID = 1629 ( like PGUID 12 f t t t 3 f 16 "25 25 25" 100 0 1 0 textlike_escape - ));
DESCR("matches LIKE expression");
DATA(insert OID = 1630 ( notlike PGUID 12 f t t t 3 f 16 "25 25 25" 100 0 1 0 textnlike_escape - ));
DESCR("does not match LIKE expression");
DATA(insert OID = 1631 ( ilike PGUID 12 f t t t 3 f 16 "25 25 25" 100 0 1 0 itextlike_escape - ));
DESCR("matches case-insensitive LIKE expression");
DATA(insert OID = 1632 ( inotlike PGUID 12 f t t t 3 f 16 "25 25 25" 100 0 1 0 itextnlike_escape - ));
DESCR("does not match case-insensitive LIKE expression");
DATA(insert OID = 1633 ( ilike PGUID 12 f t t t 2 f 16 "25 25" 100 0 1 0 itextlike - ));
DESCR("matches case-insensitive LIKE expression");
DATA(insert OID = 1634 ( inotlike PGUID 12 f t t t 2 f 16 "25 25" 100 0 1 0 itextnlike - ));
DESCR("does not match case-insensitive LIKE expression");
DATA(insert OID = 1635 ( ilike PGUID 12 f t t t 2 f 16 "19 25" 100 0 0 100 inamelike - ));
DESCR("matches case-insensitive LIKE expression");
DATA(insert OID = 1636 ( inotlike PGUID 12 f t t t 2 f 16 "19 25" 100 0 0 100 inamenlike - ));
DESCR("does not match case-insensitive LIKE expression");
DATA(insert OID = 1689 ( update_pg_pwd PGUID 12 f t f t 0 f 0 "" 100 0 0 100 update_pg_pwd - ));
DESCR("update pg_pwd file");
......@@ -2291,9 +2316,9 @@ DESCR("greater-than");
DATA(insert OID = 1721 ( numeric_ge PGUID 12 f t t t 2 f 16 "1700 1700" 100 0 0 100 numeric_ge - ));
DESCR("greater-than-or-equal");
DATA(insert OID = 1722 ( numeric_lt PGUID 12 f t t t 2 f 16 "1700 1700" 100 0 0 100 numeric_lt - ));
DESCR("lower-than");
DESCR("less-than");
DATA(insert OID = 1723 ( numeric_le PGUID 12 f t t t 2 f 16 "1700 1700" 100 0 0 100 numeric_le - ));
DESCR("lower-than-or-equal");
DESCR("less-than-or-equal");
DATA(insert OID = 1724 ( numeric_add PGUID 12 f t t t 2 f 1700 "1700 1700" 100 0 0 100 numeric_add - ));
DESCR("add");
DATA(insert OID = 1725 ( numeric_sub PGUID 12 f t t t 2 f 1700 "1700 1700" 100 0 0 100 numeric_sub - ));
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: builtins.h,v 1.130 2000/08/03 23:07:51 tgl Exp $
* $Id: builtins.h,v 1.131 2000/08/06 18:06:44 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -431,8 +431,12 @@ extern Datum pgsql_version(PG_FUNCTION_ARGS);
/* like.c */
extern Datum namelike(PG_FUNCTION_ARGS);
extern Datum namenlike(PG_FUNCTION_ARGS);
extern Datum namelike_escape(PG_FUNCTION_ARGS);
extern Datum namenlike_escape(PG_FUNCTION_ARGS);
extern Datum textlike(PG_FUNCTION_ARGS);
extern Datum textnlike(PG_FUNCTION_ARGS);
extern Datum textlike_escape(PG_FUNCTION_ARGS);
extern Datum textnlike_escape(PG_FUNCTION_ARGS);
/* oracle_compat.c */
extern Datum lower(PG_FUNCTION_ARGS);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment