aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/gram.y279
-rw-r--r--src/backend/parser/keywords.c5
-rw-r--r--src/backend/utils/adt/like.c470
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_proc.h31
-rw-r--r--src/include/utils/builtins.h6
6 files changed, 572 insertions, 223 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ba2b8d3b898..89bda3d82df 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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"; }
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index e173805a79c..848f1dc8d6b 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -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},
diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c
index 5a7b8473392..058fb1d9656 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.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);
- /* 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
+ PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
+ VARDATA(p), (VARSIZE(p)-VARHDRSZ),
+ NULL)
+ != LIKE_TRUE);
+}
- /* do the regexp matching */
- result = like(sterm, pterm);
+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);
- pfree(sterm);
- pfree(pterm);
+ PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
+ VARDATA(p), (VARSIZE(p)-VARHDRSZ),
+ ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
+ == LIKE_TRUE);
+}
- return result;
+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
-namelike(PG_FUNCTION_ARGS)
+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
+ */
+
+Datum
+inamelike(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(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
+ VARDATA(p), (VARSIZE(p)-VARHDRSZ),
+ NULL)
+ == LIKE_TRUE);
}
Datum
-namenlike(PG_FUNCTION_ARGS)
+inamenlike(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(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
+ VARDATA(p), (VARSIZE(p)-VARHDRSZ),
+ NULL)
+ != LIKE_TRUE);
}
Datum
-textlike(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(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(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,69 +282,97 @@ 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))
{
- switch (*p)
+ /* 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))
{
- 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 ++)
- {
+ 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() */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index c9ac2c39aba..d0c0b8e2bf9 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4bc8609521f..9f616dbc101 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -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 - ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 4fa7481f042..f9d3679e27a 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -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);