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.
This commit is contained in:
Thomas G. Lockhart 2000-08-06 18:06:44 +00:00
parent df40234639
commit 30ab107dbf
6 changed files with 579 additions and 230 deletions

View File

@ -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;
}
/*****************************************************************************
*
@ -2648,7 +2666,7 @@ opt_force: FORCE { $$ = TRUE; }
*****************************************************************************/
RenameStmt: ALTER TABLE relation_name opt_inh_star
/* "*" deprecated */
/* "*" deprecated */
RENAME opt_column opt_name TO name
{
RenameStmt *n = makeNode(RenameStmt);
@ -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;
}
;
/*****************************************************************************
*
@ -2891,11 +2934,11 @@ LoadStmt: LOAD file_name
*****************************************************************************/
CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_location createdb_opt_encoding
{
CreatedbStmt *n;
{
CreatedbStmt *n;
if ($5 == NULL && $6 == -1)
elog(ERROR, "CREATE DATABASE WITH requires at least one option.");
if ($5 == NULL && $6 == -1)
elog(ERROR, "CREATE DATABASE WITH requires at least one option.");
n = makeNode(CreatedbStmt);
n->dbname = $3;
@ -2918,50 +2961,49 @@ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_location createdb
;
createdb_opt_location: LOCATION '=' Sconst { $$ = $3; }
| LOCATION '=' DEFAULT { $$ = NULL; }
| LOCATION '=' DEFAULT { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; }
;
createdb_opt_encoding:
ENCODING '=' Sconst
{
createdb_opt_encoding: ENCODING '=' Sconst
{
#ifdef MULTIBYTE
int i;
i = pg_char_to_encoding($3);
if (i == -1)
elog(ERROR, "%s is not a valid encoding name", $3);
$$ = i;
int i;
i = pg_char_to_encoding($3);
if (i == -1)
elog(ERROR, "%s is not a valid encoding name", $3);
$$ = i;
#else
elog(ERROR, "Multi-byte support is not enabled");
elog(ERROR, "Multi-byte support is not enabled");
#endif
}
| ENCODING '=' Iconst
{
}
| ENCODING '=' Iconst
{
#ifdef MULTIBYTE
if (!pg_get_encent_by_encoding($3))
elog(ERROR, "%d is not a valid encoding code", $3);
$$ = $3;
if (!pg_get_encent_by_encoding($3))
elog(ERROR, "%d is not a valid encoding code", $3);
$$ = $3;
#else
elog(ERROR, "Multi-byte support is not enabled");
elog(ERROR, "Multi-byte support is not enabled");
#endif
}
| ENCODING '=' DEFAULT
{
}
| ENCODING '=' DEFAULT
{
#ifdef MULTIBYTE
$$ = GetTemplateEncoding();
$$ = GetTemplateEncoding();
#else
$$ = 0;
$$ = 0;
#endif
}
| /*EMPTY*/
{
}
| /*EMPTY*/
{
#ifdef MULTIBYTE
$$ = GetTemplateEncoding();
$$ = GetTemplateEncoding();
#else
$$= 0;
$$= 0;
#endif
}
;
}
;
/*****************************************************************************
@ -3255,7 +3297,7 @@ UpdateStmt: UPDATE opt_only relation_name
where_clause
{
UpdateStmt *n = makeNode(UpdateStmt);
n->inh = $2;
n->inh = $2;
n->relname = $3;
n->targetList = $5;
n->fromClause = $6;
@ -3353,7 +3395,7 @@ SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
List *select_list = NIL;
SelectStmt *first_select;
bool intersect_present = FALSE,
unionall_present = FALSE;
unionall_present = FALSE;
/* Take the operator tree as an argument and create a
* list of all SelectStmt Nodes found in the tree.
@ -3429,21 +3471,21 @@ select_clause: '(' select_clause ')'
| select_clause EXCEPT select_clause
{
$$ = (Node *)makeA_Expr(AND,NULL,$1,
makeA_Expr(NOT,NULL,NULL,$3));
makeA_Expr(NOT,NULL,NULL,$3));
}
| select_clause UNION opt_all select_clause
{
if (IsA($4, SelectStmt))
{
SelectStmt *n = (SelectStmt *)$4;
n->unionall = $3;
/* NOTE: if UNION ALL appears with a parenthesized set
* operation to its right, the ALL is silently discarded.
* Should we generate an error instead? I think it may
* be OK since ALL with UNION to its right is ignored
* anyway...
*/
}
{
SelectStmt *n = (SelectStmt *)$4;
n->unionall = $3;
/* NOTE: if UNION ALL appears with a parenthesized set
* operation to its right, the ALL is silently discarded.
* Should we generate an error instead? I think it may
* be OK since ALL with UNION to its right is ignored
* anyway...
*/
}
$$ = (Node *)makeA_Expr(OR,NULL,$1,$4);
}
| select_clause INTERSECT select_clause
@ -3899,21 +3941,21 @@ relation_expr: relation_name
$$->relname = $1;
$$->inh = SQL_inheritance;
}
| relation_name '*' %prec '='
| relation_name '*' %prec '='
{
/* inheritance query */
$$ = makeNode(RelExpr);
$$->relname = $1;
$$->inh = TRUE;
}
| ONLY relation_name %prec '='
{
| ONLY relation_name %prec '='
{
/* no inheritance */
$$ = makeNode(RelExpr);
$$->relname = $2;
$$->inh = FALSE;
}
;
;
opt_array_bounds: '[' ']' opt_array_bounds
{ $$ = lcons(makeInteger(-1), $3); }
@ -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"; }

View File

@ -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},

View File

@ -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,70 +20,30 @@
#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:
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
/*
* p is a text, 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 */
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
/* do the regexp matching */
result = like(sterm, pterm);
pfree(sterm);
pfree(pterm);
return result;
}
Datum
namelike(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(fixedlen_like(NameStr(*n), p, strlen(NameStr(*n))));
PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
== LIKE_TRUE);
}
Datum
@ -92,7 +52,36 @@ namenlike(PG_FUNCTION_ARGS)
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(! fixedlen_like(NameStr(*n), p, strlen(NameStr(*n))));
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
@ -101,7 +90,10 @@ textlike(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(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
== LIKE_TRUE);
}
Datum
@ -110,7 +102,140 @@ textnlike(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(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
*/
Datum
inamelike(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
== LIKE_TRUE);
}
Datum
inamenlike(PG_FUNCTION_ARGS)
{
Name n = PG_GETARG_NAME(0);
text *p = PG_GETARG_TEXT_P(1);
PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
NULL)
!= LIKE_TRUE);
}
Datum
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(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
== LIKE_TRUE);
}
Datum
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(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
VARDATA(p), (VARSIZE(p)-VARHDRSZ),
((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
!= LIKE_TRUE);
}
Datum
itextlike(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
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(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,69 +282,97 @@ textnlike(PG_FUNCTION_ARGS)
* pattern either, so an upper-level % scan can stop scanning now.
*--------------------
*/
static int
DoMatch(pg_wchar * text, pg_wchar * p)
{
for (; *p && *text; text ++, p++)
{
switch (*p)
{
case '\\':
/* Literal match with following character. */
p++;
/* FALLTHROUGH */
default:
if (*text !=*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 (*p == '%')
p++;
/* Trailing percent matches everything. */
if (*p == '\0')
return LIKE_TRUE;
/*
* Otherwise, scan for a text position at which we can
* match the rest of the pattern.
*/
for (; *text; text ++)
{
#define NextChar(p, plen) (p)++, (plen)--
static int
MatchText(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e)
{
/* 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. */
NextChar(p, plen);
/* FALLTHROUGH */
default:
if (*t != *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;
/*
* Optimization to prevent most recursion: don't
* recurse unless first pattern char might match this
* text char.
* Otherwise, scan for a text position at which we can
* match the rest of the pattern.
*/
if (*text == *p || *p == '\\' || *p == '_')
while (tlen > 0)
{
int matched = DoMatch(text, p);
/*
* Optimization to prevent most recursion: don't
* recurse unless first pattern char might match this
* text char.
*/
if ((*t == *p) || (*p == '\\') || (*p == '_')
|| ((e != NULL) && (*p == *e)))
{
int matched = MatchText(t, tlen, p, plen, e);
if (matched != LIKE_FALSE)
return matched; /* TRUE or ABORT */
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;
/*
* 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 (*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 (*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() */

View File

@ -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

View File

@ -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 - ));

View File

@ -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);