diff --git a/doc/src/sgml/ref/set.sgml b/doc/src/sgml/ref/set.sgml index 65ff6eca29fa8ef952ebdf5220a7f3ba52160a14..5ccb3a7a0df96b159ad44d8082a248c934a36c0b 100644 --- a/doc/src/sgml/ref/set.sgml +++ b/doc/src/sgml/ref/set.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/set.sgml,v 1.61 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/set.sgml,v 1.62 2002/06/11 15:41:30 thomas Exp $ PostgreSQL documentation --> @@ -28,8 +28,8 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep <listitem> <para> Specifies that the command takes effect for the current session. - (This is the default if neither <option>SESSION</> nor - <option>LOCAL</> appears.) + (This is the default if neither <option>SESSION</> nor + <option>LOCAL</> appears.) </para> </listitem> </varlistentry> @@ -39,11 +39,11 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep <listitem> <para> Specifies that the command takes effect for only the current - transaction. After <command>COMMIT</> or <command>ROLLBACK</>, - the session-level setting takes effect again. Note that - <command>SET LOCAL</> will appear to have no effect if it's - executed outside a <command>BEGIN</> block, since the transaction - will end immediately. + transaction. After <command>COMMIT</> or <command>ROLLBACK</>, + the session-level setting takes effect again. Note that + <command>SET LOCAL</> will appear to have no effect if it's + executed outside a <command>BEGIN</> block, since the transaction + will end immediately. </para> </listitem> </varlistentry> @@ -112,7 +112,6 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep Here are additional details about a few of the parameters that can be set: <variablelist> - <varlistentry> <term>DATESTYLE</term> <listitem> @@ -126,44 +125,44 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep The following are date/time output styles: <variablelist> - <varlistentry> - <term>ISO</term> - <listitem> - <para> - Use ISO 8601-style dates and times (<literal>YYYY-MM-DD - HH:MM:SS</literal>). This is the default. + <varlistentry> + <term>ISO</term> + <listitem> + <para> + Use ISO 8601-style dates and times (<literal>YYYY-MM-DD + HH:MM:SS</literal>). This is the default. </para> - </listitem> - </varlistentry> + </listitem> + </varlistentry> <varlistentry> - <term>SQL</term> - <listitem> - <para> - Use Oracle/Ingres-style dates and times. Note that this - style has nothing to do with SQL (which mandates ISO 8601 - style), the naming of this option is a historical accident. - </para> - </listitem> - </varlistentry> + <term>SQL</term> + <listitem> + <para> + Use Oracle/Ingres-style dates and times. Note that this + style has nothing to do with SQL (which mandates ISO 8601 + style), the naming of this option is a historical accident. + </para> + </listitem> + </varlistentry> <varlistentry> - <term>PostgreSQL</term> - <listitem> - <para> - Use traditional <productname>PostgreSQL</productname> format. - </para> - </listitem> - </varlistentry> + <term>PostgreSQL</term> + <listitem> + <para> + Use traditional <productname>PostgreSQL</productname> format. + </para> + </listitem> + </varlistentry> <varlistentry> - <term>German</term> - <listitem> - <para> - Use <literal>dd.mm.yyyy</literal> for numeric date representations. - </para> - </listitem> - </varlistentry> + <term>German</term> + <listitem> + <para> + Use <literal>dd.mm.yyyy</literal> for numeric date representations. + </para> + </listitem> + </varlistentry> </variablelist> </para> @@ -173,24 +172,24 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep and the preferred interpretation of ambiguous date input. <variablelist> - <varlistentry> - <term>European</term> - <listitem> - <para> - Use <literal>dd/mm/yyyy</literal> for numeric date representations. - </para> - </listitem> - </varlistentry> + <varlistentry> + <term>European</term> + <listitem> + <para> + Use <literal>dd/mm/yyyy</literal> for numeric date representations. + </para> + </listitem> + </varlistentry> <varlistentry> - <term>NonEuropean</term> - <term>US</term> - <listitem> - <para> - Use <literal>mm/dd/yyyy</literal> for numeric date representations. - </para> - </listitem> - </varlistentry> + <term>NonEuropean</term> + <term>US</term> + <listitem> + <para> + Use <literal>mm/dd/yyyy</literal> for numeric date representations. + </para> + </listitem> + </varlistentry> </variablelist> </para> @@ -214,23 +213,23 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep in addition to the normal methods of setting it via <command>SET</> or a configuration-file entry: <simplelist> - <member> - Setting the postmaster's <envar>PGDATESTYLE</envar> environment - variable. (This will be overridden by any of the other methods.) - </member> - <member> - Running postmaster using the option <option>-o -e</option> to - select the <literal>European</literal> conventions. - (This overrides environment variables and configuration-file - entries.) - </member> - <member> - Setting the client's <envar>PGDATESTYLE</envar> environment variable. - If PGDATESTYLE is set in the frontend environment of a client - based on libpq, libpq will automatically set DATESTYLE to the - value of PGDATESTYLE during connection start-up. This is - equivalent to a manually issued <command>SET</>. - </member> + <member> + Setting the postmaster's <envar>PGDATESTYLE</envar> environment + variable. (This will be overridden by any of the other methods.) + </member> + <member> + Running postmaster using the option <option>-o -e</option> to + set dates to the <literal>European</literal> convention. + (This overrides environment variables and configuration-file + entries.) + </member> + <member> + Setting the client's <envar>PGDATESTYLE</envar> environment variable. + If PGDATESTYLE is set in the frontend environment of a client + based on libpq, libpq will automatically set DATESTYLE to the + value of PGDATESTYLE during connection start-up. This is + equivalent to a manually issued <command>SET DATESTYLE</>. + </member> </simplelist> </para> @@ -250,50 +249,49 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep <term>SEED</term> <listitem> <para> - Sets the internal seed for the random number generator. + Sets the internal seed for the random number generator. - <variablelist> - <varlistentry> - <term><replaceable class="parameter">value</replaceable></term> - <listitem> - <para> - The value for the seed to be used by the - <function>random</function> function. Allowed - values are floating-point numbers between 0 and 1, which - are then multiplied by 2<superscript>31</>-1. - </para> - </listitem> - </varlistentry> - </variablelist> - </para> + <variablelist> + <varlistentry> + <term><replaceable class="parameter">value</replaceable></term> + <listitem> + <para> + The value for the seed to be used by the + <function>random</function> function. Allowed + values are floating-point numbers between 0 and 1, which + are then multiplied by 2<superscript>31</>-1. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> - <para> - The seed can also be set by invoking the - <function>setseed</function> SQL function: + <para> + The seed can also be set by invoking the + <function>setseed</function> SQL function: - <programlisting> + <programlisting> SELECT setseed(<replaceable>value</replaceable>); - </programlisting> - </para> - - </listitem> - </varlistentry> + </programlisting> + </para> + </listitem> + </varlistentry> - <varlistentry> - <term>SERVER_ENCODING</term> - <listitem> - <para> - Shows the server-side multibyte encoding. (At present, this - parameter can be shown but not set, because the encoding is - determined at initdb time.) - </para> - </listitem> - </varlistentry> + <varlistentry> + <term>SERVER_ENCODING</term> + <listitem> + <para> + Shows the server-side multibyte encoding. (At present, this + parameter can be shown but not set, because the encoding is + determined at initdb time.) + </para> + </listitem> + </varlistentry> - <varlistentry> - <term>TIME ZONE</term> - <term>TIMEZONE</term> - <listitem> + <varlistentry> + <term>TIME ZONE</term> + <term>TIMEZONE</term> + <listitem> <para> Sets the default time zone for your session. Arguments can be an SQL time interval constant, an integer or double precision @@ -301,92 +299,88 @@ SELECT setseed(<replaceable>value</replaceable>); by the host operating system. </para> - <para> - The available time zone names depend on your operating - system. For example, on Linux - <filename>/usr/share/zoneinfo</filename> contains the database - of time zones. - </para> - <para> - Here are some typical values for time zone names: + <para> + Here are some typical values for time zone settings: <variablelist> - <varlistentry> - <term>'PST8PDT'</term> - <listitem> - <para> - Set the time zone for California. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>'Portugal'</term> - <listitem> - <para> - Set the time zone for Portugal. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>'Europe/Rome'</term> - <listitem> - <para> - Set the time zone for Italy. - </para> - </listitem> - </varlistentry> - </variablelist> - </para> + <varlistentry> + <term>'PST8PDT'</term> + <listitem> + <para> + Set the time zone for Berkeley, California. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>'Portugal'</term> + <listitem> + <para> + Set the time zone for Portugal. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>'Europe/Rome'</term> + <listitem> + <para> + Set the time zone for Italy. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>7</term> + <listitem> + <para> + Set the time zone to 7 hours offset west from GMT (equivalent + to PDT). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>INTERVAL '08:00' HOUR TO MINUTE</term> + <listitem> + <para> + Set the time zone to 8 hours offset west from GMT (equivalent + to PST). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>LOCAL</term> + <term>DEFAULT</term> + <listitem> + <para> + Set the time zone to your local time zone (the one that + your operating system defaults to). + </para> + </listitem> + </varlistentry> + </variablelist> + </para> - <para> - In addition to time zone names, <productname>PostgreSQL</productname> - accepts these other methods of specifying a time zone: + <para> + The available time zone names depend on your operating + system. For example, on Linux + <filename>/usr/share/zoneinfo</filename> contains the database + of time zones; the names of the files in that directory can be + used as parameters to this command. + </para> - <variablelist> - <varlistentry> - <term>7</term> - <listitem> - <para> - Set the time zone to 7 hours offset west from GMT (equivalent - to PDT). - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>INTERVAL '08:00' HOUR TO MINUTE</term> - <listitem> - <para> - Set the time zone to 8 hours offset west from GMT (equivalent - to PST). - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>LOCAL</term> - <term>DEFAULT</term> - <listitem> - <para> - Set the time zone to your local time zone (the one that - your operating system defaults to). - </para> - </listitem> - </varlistentry> - </variablelist> - </para> - <para> - If an invalid time zone name is specified, the time zone - becomes GMT (on most systems anyway). - </para> - <para> - If the <envar>PGTZ</envar> environment variable is set in the frontend - environment of a client based on libpq, libpq will automatically - set TIMEZONE to the value of <envar>PGTZ</envar> during connection start-up. - </para> - </listitem> - </varlistentry> + <para> + If an invalid time zone is specified, the time zone + becomes GMT (on most systems anyway). + </para> - </variablelist> - </para> + <para> + If the <envar>PGTZ</envar> environment variable is set in the frontend + environment of a client based on libpq, libpq will automatically + <command>SET TIMEZONE</command> to the value of + <envar>PGTZ</envar> during connection start-up. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> <para> Use <xref linkend="SQL-SHOW" endterm="SQL-SHOW-title"> to show the diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index fbf63db85e2f78a87199b2f731be4679f85c7299..b8fd3d034893ecb6faaaab10267bc8489d495f27 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.320 2002/06/11 13:40:50 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.321 2002/06/11 15:41:37 thomas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -227,10 +227,10 @@ static void doNegateFloat(Value *v); %type <node> join_outer, join_qual %type <jtype> join_type -%type <list> extract_list, position_list +%type <list> extract_list, overlay_list, position_list %type <list> substr_list, trim_list %type <ival> opt_interval -%type <node> substr_from, substr_for +%type <node> overlay_placing, substr_from, substr_for %type <boolean> opt_binary, opt_using, opt_instead, opt_cursor %type <boolean> opt_with_copy, index_opt_unique, opt_verbose, opt_full @@ -336,7 +336,7 @@ static void doNegateFloat(Value *v); FALSE_P, FETCH, FLOAT_P, FOR, FORCE, FOREIGN, FORWARD, FREEZE, FROM, FULL, FUNCTION, - GLOBAL, GRANT, GROUP_P, + GET, GLOBAL, GRANT, GROUP_P, HANDLER, HAVING, HOUR_P, ILIKE, IMMEDIATE, IMMUTABLE, IMPLICIT, IN_P, INCREMENT, INDEX, INHERITS, @@ -356,16 +356,16 @@ static void doNegateFloat(Value *v); NUMERIC, OF, OFF, OFFSET, OIDS, OLD, ON, ONLY, OPERATOR, OPTION, OR, ORDER, - OUT_P, OUTER_P, OVERLAPS, OWNER, + OUT_P, OUTER_P, OVERLAPS, OVERLAY, OWNER, - PARTIAL, PASSWORD, PATH_P, PENDANT, POSITION, PRECISION, PRIMARY, + PARTIAL, PASSWORD, PATH_P, PENDANT, PLACING, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PROCEDURAL, READ, REAL, REFERENCES, REINDEX, RELATIVE, RENAME, REPLACE, RESET, RESTRICT, RETURNS, REVOKE, RIGHT, ROLLBACK, ROW, RULE, SCHEMA, SCROLL, SECOND_P, SECURITY, SELECT, SEQUENCE, SERIALIZABLE, - SESSION, SESSION_USER, SET, SETOF, SHARE, SHOW, SMALLINT, SOME, + SESSION, SESSION_USER, SET, SETOF, SHARE, SHOW, SIMILAR, SMALLINT, SOME, STABLE, START, STATEMENT, STATISTICS, STDIN, STDOUT, STORAGE, STRICT, SUBSTRING, SYSID, @@ -402,7 +402,7 @@ static void doNegateFloat(Value *v); %right NOT %right '=' %nonassoc '<' '>' -%nonassoc LIKE ILIKE +%nonassoc LIKE ILIKE SIMILAR %nonassoc ESCAPE %nonassoc OVERLAPS %nonassoc BETWEEN @@ -420,6 +420,7 @@ static void doNegateFloat(Value *v); %right UMINUS %left '[' ']' %left '(' ')' +%left COLLATE %left TYPECAST %left '.' %% @@ -2139,6 +2140,14 @@ DefineStmt: CREATE AGGREGATE func_name definition n->definition = $4; $$ = (Node *)n; } + | CREATE CHARACTER SET opt_as any_name GET definition opt_collate + { + DefineStmt *n = makeNode(DefineStmt); + n->defType = CHARACTER; + n->defnames = $5; + n->definition = $7; + $$ = (Node *)n; + } ; definition: '(' def_list ')' { $$ = $2; } @@ -4978,9 +4987,18 @@ qual_all_Op: all_Op * it's factored out just to eliminate redundant coding. */ a_expr: c_expr - { $$ = $1; } + { $$ = $1; } | a_expr TYPECAST Typename { $$ = makeTypeCast($1, $3); } + | a_expr COLLATE ColId + { + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName($3); + n->args = makeList1($1); + n->agg_star = FALSE; + n->agg_distinct = FALSE; + $$ = (Node *) n; + } | a_expr AT TIME ZONE c_expr { FuncCall *n = makeNode(FuncCall); @@ -5088,6 +5106,30 @@ a_expr: c_expr n->agg_distinct = FALSE; $$ = (Node *) makeSimpleA_Expr(OP, "!~~*", $1, (Node *) n); } + + | a_expr SIMILAR TO a_expr %prec SIMILAR + { $$ = (Node *) makeSimpleA_Expr(OP, "~", $1, $4); } + | a_expr SIMILAR TO a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName("like_escape"); + n->args = makeList2($4, $6); + n->agg_star = FALSE; + n->agg_distinct = FALSE; + $$ = (Node *) makeSimpleA_Expr(OP, "~", $1, (Node *) n); + } + | a_expr NOT SIMILAR TO a_expr %prec SIMILAR + { $$ = (Node *) makeSimpleA_Expr(OP, "!~", $1, $5); } + | a_expr NOT SIMILAR TO a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName("like_escape"); + n->args = makeList2($5, $7); + n->agg_star = FALSE; + n->agg_distinct = FALSE; + $$ = (Node *) makeSimpleA_Expr(OP, "!~", $1, (Node *) n); + } + /* NullTest clause * Define SQL92-style Null test clause. * Allow two forms described in the standard: @@ -5568,6 +5610,20 @@ c_expr: columnref n->agg_distinct = FALSE; $$ = (Node *)n; } + | OVERLAY '(' overlay_list ')' + { + /* overlay(A PLACING B FROM C FOR D) is converted to + * substring(A, 1, C-1) || B || substring(A, C+1, C+D) + * overlay(A PLACING B FROM C) is converted to + * substring(A, 1, C-1) || B || substring(A, C+1, C+char_length(B)) + */ + FuncCall *n = makeNode(FuncCall); + n->funcname = SystemFuncName("overlay"); + n->args = $3; + n->agg_star = FALSE; + n->agg_distinct = FALSE; + $$ = (Node *)n; + } | POSITION '(' position_list ')' { /* position(A in B) is converted to position(B, A) */ @@ -5706,6 +5762,25 @@ extract_arg: IDENT { $$ = $1; } | SCONST { $$ = $1; } ; +/* OVERLAY() arguments + * SQL99 defines the OVERLAY() function: + * o overlay(text placing text from int for int) + * o overlay(text placing text from int) + */ +overlay_list: a_expr overlay_placing substr_from substr_for + { + $$ = makeList4($1, $2, $3, $4); + } + | a_expr overlay_placing substr_from + { + $$ = makeList3($1, $2, $3); + } + ; + +overlay_placing: PLACING a_expr + { $$ = $2; } + ; + /* position_list uses b_expr not a_expr to avoid conflict with general IN */ position_list: b_expr IN_P b_expr @@ -6259,6 +6334,7 @@ unreserved_keyword: | FORCE | FORWARD | FUNCTION + | GET | GLOBAL | HANDLER | HOUR_P @@ -6404,6 +6480,7 @@ col_name_keyword: | NONE | NULLIF | NUMERIC + | OVERLAY | POSITION | REAL | SETOF @@ -6423,7 +6500,7 @@ col_name_keyword: * * Do not include POSITION, SUBSTRING, etc here since they have explicit * productions in a_expr to support the goofy SQL9x argument syntax. - * - thomas 2000-11-28 + * - thomas 2000-11-28 */ func_name_keyword: AUTHORIZATION @@ -6445,6 +6522,7 @@ func_name_keyword: | OUTER_P | OVERLAPS | RIGHT + | SIMILAR | VERBOSE ; diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index c60b2df2a6d5a153c1006f5b6be257c17094753e..dca44c448f988c37ae4c38ec6781a93500a02e25 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.112 2002/06/11 13:40:51 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.113 2002/06/11 15:41:37 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -129,6 +129,7 @@ static const ScanKeyword ScanKeywords[] = { {"from", FROM}, {"full", FULL}, {"function", FUNCTION}, + {"get", GET}, {"global", GLOBAL}, {"grant", GRANT}, {"group", GROUP_P}, @@ -211,11 +212,13 @@ static const ScanKeyword ScanKeywords[] = { {"out", OUT_P}, {"outer", OUTER_P}, {"overlaps", OVERLAPS}, + {"overlay", OVERLAY}, {"owner", OWNER}, {"partial", PARTIAL}, {"password", PASSWORD}, {"path", PATH_P}, {"pendant", PENDANT}, + {"placing", PLACING}, {"position", POSITION}, {"precision", PRECISION}, {"primary", PRIMARY}, @@ -251,6 +254,7 @@ static const ScanKeyword ScanKeywords[] = { {"setof", SETOF}, {"share", SHARE}, {"show", SHOW}, + {"similar", SIMILAR}, {"smallint", SMALLINT}, {"some", SOME}, {"stable", STABLE}, diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index a6bc3549e17d60d51499d4c52ec046bc96a7464d..e3d88b4521b04472fb51761a5801206fe98b3616 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.94 2002/05/02 18:44:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.95 2002/06/11 15:41:37 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -92,14 +92,14 @@ unsigned char unescape_single_char(unsigned char c); * We use exclusive states for quoted strings, extended comments, * and to eliminate parsing troubles for numeric strings. * Exclusive states: - * <xbit> bit string literal + * <xb> bit string literal * <xc> extended C-style comments - thomas 1997-07-12 * <xd> delimited identifiers (double-quoted identifiers) - thomas 1997-10-27 * <xh> hexadecimal numeric string - thomas 1997-11-16 * <xq> quoted strings - thomas 1997-07-30 */ -%x xbit +%x xb %x xc %x xd %x xh @@ -107,10 +107,10 @@ unsigned char unescape_single_char(unsigned char c); /* Bit string */ -xbitstart [bB]{quote} -xbitstop {quote} -xbitinside [^']* -xbitcat {quote}{whitespace_with_newline}{quote} +xbstart [bB]{quote} +xbstop {quote} +xbinside [^']* +xbcat {quote}{whitespace_with_newline}{quote} /* Hexadecimal number */ @@ -285,13 +285,13 @@ other . <xc><<EOF>> { yyerror("unterminated /* comment"); } -{xbitstart} { +{xbstart} { token_start = yytext; - BEGIN(xbit); + BEGIN(xb); startlit(); addlitchar('b'); } -<xbit>{xbitstop} { +<xb>{xbstop} { BEGIN(INITIAL); if (literalbuf[strspn(literalbuf + 1, "01") + 1] != '\0') yyerror("invalid bit string input"); @@ -299,14 +299,14 @@ other . return BITCONST; } <xh>{xhinside} | -<xbit>{xbitinside} { +<xb>{xbinside} { addlit(yytext, yyleng); } <xh>{xhcat} | -<xbit>{xbitcat} { +<xb>{xbcat} { /* ignore */ } -<xbit><<EOF>> { yyerror("unterminated bit string literal"); } +<xb><<EOF>> { yyerror("unterminated bit string literal"); } {xhstart} { token_start = yytext; diff --git a/src/backend/regex/regcomp.c b/src/backend/regex/regcomp.c index bea464bd2ae88aa2b8a8bd0d8f27e27d9e1ff68d..d6f7b26fa1a4cb77579963312a8b768664dee2c8 100644 --- a/src/backend/regex/regcomp.c +++ b/src/backend/regex/regcomp.c @@ -174,9 +174,10 @@ static int never = 0; /* for use in asserts; shuts lint up */ /* * regcomp - interface for parser and compilation + * returns 0 success, otherwise REG_something */ -int /* 0 success, otherwise REG_something */ -pg95_regcomp(regex_t *preg, const char *pattern, int cflags) +int +pg_regcomp(regex_t *preg, const char *pattern, int cflags) { struct parse pa; struct re_guts *g; @@ -224,7 +225,6 @@ pg95_regcomp(regex_t *preg, const char *pattern, int cflags) (void) pg_mb2wchar((unsigned char *) pattern, wcp); len = pg_wchar_strlen(wcp); #else - len = strlen((char *) pattern); #endif } @@ -305,7 +305,7 @@ pg95_regcomp(regex_t *preg, const char *pattern, int cflags) /* win or lose, we're done */ if (p->error != 0) /* lose */ - pg95_regfree(preg); + pg_regfree(preg); return p->error; } diff --git a/src/backend/regex/regerror.c b/src/backend/regex/regerror.c index 3fa00946aa414e2307991f96680e5ec3cf553d43..fb12cba3048ee78dda4d4f425438702133abf230 100644 --- a/src/backend/regex/regerror.c +++ b/src/backend/regex/regerror.c @@ -117,8 +117,8 @@ static struct rerr */ /* ARGSUSED */ size_t -pg95_regerror(int errcode, const regex_t *preg, - char *errbuf, size_t errbuf_size) +pg_regerror(int errcode, const regex_t *preg, + char *errbuf, size_t errbuf_size) { struct rerr *r; size_t len; diff --git a/src/backend/regex/regexec.c b/src/backend/regex/regexec.c index 476773044138393e42f671896496dd5e574cbdd6..06459ef1dbcaefec6d33d44df2ba88ebb6b85031 100644 --- a/src/backend/regex/regexec.c +++ b/src/backend/regex/regexec.c @@ -149,8 +149,8 @@ do { \ * when choosing which matcher to call. */ int /* 0 success, REG_NOMATCH failure */ -pg95_regexec(const regex_t *preg, const char *string, size_t nmatch, - regmatch_t *pmatch, int eflags) +pg_regexec(const regex_t *preg, const char *string, size_t nmatch, + regmatch_t *pmatch, int eflags) { struct re_guts *g = preg->re_g; diff --git a/src/backend/regex/regfree.c b/src/backend/regex/regfree.c index 87e0c99ef96690afcb1bb3b6b9da293216fa4add..5672fcf240f4f9f7358f26f3d53d4ddefe36d041 100644 --- a/src/backend/regex/regfree.c +++ b/src/backend/regex/regfree.c @@ -49,7 +49,7 @@ * regfree - free everything */ void -pg95_regfree(regex_t *preg) +pg_regfree(regex_t *preg) { struct re_guts *g; diff --git a/src/backend/regex/retest.c b/src/backend/regex/retest.c index eee8ef09ae3a24c0e4c041deefb0221c44d347a9..ca5d6c5394aa6fd43c7af2941982f2eb2c84129b 100644 --- a/src/backend/regex/retest.c +++ b/src/backend/regex/retest.c @@ -1,7 +1,7 @@ /* * a simple regexp debug program * - * $Header: /cvsroot/pgsql/src/backend/regex/Attic/retest.c,v 1.4 1999/07/17 20:17:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/regex/Attic/retest.c,v 1.5 2002/06/11 15:41:37 thomas Exp $ */ #include "postgres.h" @@ -22,7 +22,7 @@ main() if (p) *p = '\0'; - sts = pg95_regcomp(&re, buf, 1); + sts = pg_regcomp(&re, buf, 1); printf("regcomp: parses \"%s\" and returns %d\n", buf, sts); for (;;) { @@ -33,7 +33,7 @@ main() if (p) *p = '\0'; - sts = pg95_regexec(&re, buf, 0, 0, 0); + sts = pg_regexec(&re, buf, 0, 0, 0); printf("regexec: returns %d\n", sts); } } diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c index a61717a86021f791fc69f81b34ea667faaac0773..9afc58feee7d6111721a4923408687c7ef57110b 100644 --- a/src/backend/utils/adt/regexp.c +++ b/src/backend/utils/adt/regexp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/regexp.c,v 1.38 2001/11/05 17:46:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regexp.c,v 1.39 2002/06/11 15:41:37 thomas Exp $ * * Alistair Crooks added the code for the regex caching * agc - cached the regular expressions used - there's a good chance @@ -19,7 +19,7 @@ * * agc - incorporated Keith Bostic's Berkeley regex code into * the tree for all ports. To distinguish this regex code from any that - * is existent on a platform, I've prepended the string "pg95_" to + * is existent on a platform, I've prepended the string "pg_" to * the functions regcomp, regerror, regexec and regfree. * Fixed a bug that was originally a typo by me, where `i' was used * instead of `oldest' when compiling regular expressions - benign @@ -53,11 +53,13 @@ struct cached_re_str static int rec = 0; /* # of cached re's */ static struct cached_re_str rev[MAX_CACHED_RES]; /* cached re's */ static unsigned long lru; /* system lru tag */ +static int pg_lastre = 0; /* attempt to compile `re' as an re, then match it against text */ /* cflags - flag to regcomp indicates case sensitivity */ static bool -RE_compile_and_execute(text *text_re, char *text, int cflags) +RE_compile_and_execute(text *text_re, char *text, int cflags, + int nmatch, regmatch_t *pmatch) { char *re; int oldest; @@ -68,9 +70,27 @@ RE_compile_and_execute(text *text_re, char *text, int cflags) re = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(text_re))); + if ((i = pg_lastre) < rec) + { + if (rev[i].cre_s) + { + if (strcmp(rev[i].cre_s, re) == 0 && + rev[i].cre_type == cflags) + { + rev[i].cre_lru = ++lru; + pfree(re); + return (pg_regexec(&rev[i].cre_re, + text, nmatch, + pmatch, 0) == 0); + } + } + } + /* find a previously compiled regular expression */ for (i = 0; i < rec; i++) { + if (i == pg_lastre) continue; + if (rev[i].cre_s) { if (strcmp(rev[i].cre_s, re) == 0 && @@ -78,9 +98,9 @@ RE_compile_and_execute(text *text_re, char *text, int cflags) { rev[i].cre_lru = ++lru; pfree(re); - return (pg95_regexec(&rev[i].cre_re, - text, 0, - (regmatch_t *) NULL, 0) == 0); + return (pg_regexec(&rev[i].cre_re, + text, nmatch, + pmatch, 0) == 0); } } } @@ -107,7 +127,7 @@ RE_compile_and_execute(text *text_re, char *text, int cflags) if (rev[i].cre_lru > lru) lru = rev[i].cre_lru; } - pg95_regfree(&rev[oldest].cre_re); + pg_regfree(&rev[oldest].cre_re); /* * use malloc/free for the cre_s field because the storage has to @@ -118,7 +138,7 @@ RE_compile_and_execute(text *text_re, char *text, int cflags) } /* compile the re */ - regcomp_result = pg95_regcomp(&rev[oldest].cre_re, re, cflags); + regcomp_result = pg_regcomp(&rev[oldest].cre_re, re, cflags); if (regcomp_result == 0) { /* @@ -130,16 +150,16 @@ RE_compile_and_execute(text *text_re, char *text, int cflags) rev[oldest].cre_type = cflags; pfree(re); /* agc - fixed an old typo here */ - return (pg95_regexec(&rev[oldest].cre_re, text, 0, - (regmatch_t *) NULL, 0) == 0); + return (pg_regexec(&rev[oldest].cre_re, text, + nmatch, pmatch, 0) == 0); } else { char errMsg[1000]; /* re didn't compile */ - pg95_regerror(regcomp_result, &rev[oldest].cre_re, errMsg, - sizeof(errMsg)); + pg_regerror(regcomp_result, &rev[oldest].cre_re, errMsg, + sizeof(errMsg)); elog(ERROR, "Invalid regular expression: %s", errMsg); } @@ -167,7 +187,7 @@ fixedlen_regexeq(char *s, text *p, int charlen, int cflags) memcpy(sterm, s, charlen); sterm[charlen] = '\0'; - result = RE_compile_and_execute(p, sterm, cflags); + result = RE_compile_and_execute(p, sterm, cflags, 0, NULL); pfree(sterm); @@ -230,7 +250,7 @@ textregexne(PG_FUNCTION_ARGS) /* * routines that use the regexp stuff, but ignore the case. - * for this, we use the REG_ICASE flag to pg95_regcomp + * for this, we use the REG_ICASE flag to pg_regcomp */ @@ -281,3 +301,54 @@ nameicregexne(PG_FUNCTION_ARGS) strlen(NameStr(*n)), REG_ICASE | REG_EXTENDED)); } + + +/* textregexsubstr() + * Return a substring matched by a regular expression. + */ +Datum +textregexsubstr(PG_FUNCTION_ARGS) +{ + text *s = PG_GETARG_TEXT_P(0); + text *p = PG_GETARG_TEXT_P(1); + text *result; + char *sterm; + int len; + bool match; + int nmatch = 1; + regmatch_t pmatch; + + /* be sure sterm is null-terminated */ + len = VARSIZE(s) - VARHDRSZ; + sterm = (char *) palloc(len + 1); + memcpy(sterm, VARDATA(s), len); + sterm[len] = '\0'; + /* We need the match info back from the pattern match + * to be able to actually extract the substring. + * It seems to be adequate to pass in a structure to return + * only one result. + */ + match = RE_compile_and_execute(p, sterm, REG_EXTENDED, nmatch, &pmatch); + pfree(sterm); + + /* match? then return the substring matching the pattern */ + if (match) + { + return (DirectFunctionCall3(text_substr, + PointerGetDatum(s), + Int32GetDatum(pmatch.rm_so+1), + Int32GetDatum(pmatch.rm_eo-pmatch.rm_so))); + } +#if 0 + /* otherwise, return a zero-length string */ + else + { + result = palloc(VARHDRSZ); + VARATT_SIZEP(result) = VARHDRSZ; + PG_RETURN_TEXT_P(result); + } +#endif + + /* not reached */ + PG_RETURN_NULL(); +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index bd861396210ee2a03750535db067d45d5498fb98..ae10bfb6879328e574204b041996137a3e4ca1ab 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.133 2002/05/22 17:21:01 petere Exp $ + * $Id: catversion.h,v 1.134 2002/06/11 15:44:38 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200205221 +#define CATALOG_VERSION_NO 200206111 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index a7d56dc90dcb4b094e9c675cd49e0637d3a6aa4c..56831dcfca105ec5c2aa3ce201ffde0fdf2de1fc 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.240 2002/05/24 18:57:56 tgl Exp $ + * $Id: pg_proc.h,v 1.241 2002/06/11 15:41:37 thomas Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1679,7 +1679,7 @@ DESCR("less-equal-greater"); DATA(insert OID = 1359 ( timestamptz PGNSP PGUID 12 f f f t f i 2 1184 "1082 1266" 100 0 0 100 datetimetz_timestamptz - _null_ )); DESCR("convert date and time with time zone to timestamp with time zone"); -DATA(insert OID = 1364 ( time PGNSP PGUID 14 f f f t f i 1 1083 "702" 100 0 0 100 "select time(cast($1 as timestamp without time zone))" - _null_ )); +DATA(insert OID = 1364 ( time PGNSP PGUID 14 f f f t f i 1 1083 "702" 100 0 0 100 "select time(cast($1 as timestamp without time zone))" - _null_ )); DESCR("convert abstime to time"); DATA(insert OID = 1367 ( character_length PGNSP PGUID 12 f f f t f i 1 23 "1042" 100 0 0 100 bpcharlen - _null_ )); @@ -1764,6 +1764,11 @@ DESCR("current schema name"); DATA(insert OID = 1403 ( current_schemas PGNSP PGUID 12 f f f t f s 0 1003 "0" 100 0 0 100 current_schemas - _null_ )); DESCR("current schema search list"); +DATA(insert OID = 1404 ( overlay PGNSP PGUID 14 f f f t f i 4 25 "25 25 23 23" 100 0 0 100 "select substring($1, 1, ($3 - 1)) || $2 || substring($1, ($3 + $4))" - _null_ )); +DESCR("substitute portion of string"); +DATA(insert OID = 1405 ( overlay PGNSP PGUID 14 f f f t f i 3 25 "25 25 23" 100 0 0 100 "select substring($1, 1, ($3 - 1)) || $2 || substring($1, ($3 + char_length($2)))" - _null_ )); +DESCR("substitute portion of string"); + DATA(insert OID = 1406 ( isvertical PGNSP PGUID 12 f f f t f i 2 16 "600 600" 100 0 0 100 point_vert - _null_ )); DESCR("vertically aligned?"); DATA(insert OID = 1407 ( ishorizontal PGNSP PGUID 12 f f f t f i 2 16 "600 600" 100 0 0 100 point_horiz - _null_ )); @@ -2871,6 +2876,11 @@ DESCR("add"); DATA(insert OID = 2072 ( date_mi_interval PGNSP PGUID 14 f f f t f i 2 1114 "1082 1186" 100 0 0 100 "select cast($1 as timestamp without time zone) - $2;" - _null_ )); DESCR("subtract"); +DATA(insert OID = 2073 ( substring PGNSP PGUID 12 f f f t f i 2 25 "25 25" 100 0 0 100 textregexsubstr - _null_ )); +DESCR("substitutes regular expression"); +DATA(insert OID = 2074 ( substring PGNSP PGUID 14 f f f t f i 3 25 "25 25 25" 100 0 0 100 "select substring($1, like_escape($2, $3))" - _null_ )); +DESCR("substitutes regular expression with escape argument"); + /* Aggregates (moved here from pg_aggregate for 7.3) */ DATA(insert OID = 2100 ( avg PGNSP PGUID 12 t f f f f i 1 1700 "20" 100 0 0 100 aggregate_dummy - _null_ )); diff --git a/src/include/regex/regex.h b/src/include/regex/regex.h index 09f966e1bf26ae1c712dfcb2f9713d53f6486130..dd8e0da171bd61491535dbf9ab4921e07d1c70d7 100644 --- a/src/include/regex/regex.h +++ b/src/include/regex/regex.h @@ -53,7 +53,7 @@ typedef struct const pg_wchar *re_endp; /* end pointer for REG_PEND */ struct re_guts *re_g; /* none of your business :-) */ #ifdef MULTIBYTE - pg_wchar *patsave; /* mee too :-) */ + pg_wchar *patsave; /* me too :-) */ #endif } regex_t; @@ -102,12 +102,12 @@ typedef struct #define REG_LARGE 01000 /* force large representation */ #define REG_BACKR 02000 /* force use of backref code */ -extern int pg95_regcomp(regex_t *preg, const char *pattern, int cflags); -extern size_t pg95_regerror(int errcode, const regex_t *preg, - char *errbuf, size_t errbuf_size); -extern int pg95_regexec(const regex_t *preg, const char *string, - size_t nmatch, - regmatch_t *pmatch, int eflags); -extern void pg95_regfree(regex_t *preg); +extern int pg_regcomp(regex_t *preg, const char *pattern, int cflags); +extern size_t pg_regerror(int errcode, const regex_t *preg, + char *errbuf, size_t errbuf_size); +extern int pg_regexec(const regex_t *preg, const char *string, + size_t nmatch, + regmatch_t *pmatch, int eflags); +extern void pg_regfree(regex_t *preg); #endif /* !_REGEX_H_ */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 6340aa225403f402a3f65cdecca525f5f20e46b4..7676ce5663fdbaf7184ed0675554cbe8306ce001 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.182 2002/05/18 21:38:41 tgl Exp $ + * $Id: builtins.h,v 1.183 2002/06/11 15:41:38 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -327,6 +327,7 @@ extern Datum nameicregexeq(PG_FUNCTION_ARGS); extern Datum nameicregexne(PG_FUNCTION_ARGS); extern Datum texticregexeq(PG_FUNCTION_ARGS); extern Datum texticregexne(PG_FUNCTION_ARGS); +extern Datum textregexsubstr(PG_FUNCTION_ARGS); /* regproc.c */ extern Datum regprocin(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out index ebfe8eeb66328d0014fc1981638dba5698a84a34..680a76c0bd5696f9c170389fee7a15a80d7fe2fc 100644 --- a/src/test/regress/expected/strings.out +++ b/src/test/regress/expected/strings.out @@ -3,6 +3,7 @@ -- Test various data entry syntaxes. -- -- SQL92 string continuation syntax +-- E021-03 character string literals SELECT 'first line' ' - next line' ' - third line' @@ -20,6 +21,7 @@ SELECT 'first line' ERROR: parser: parse error at or near "' - third line'" -- -- test conversions between various string types +-- E021-10 implicit casting among the character data types -- SELECT CAST(f1 AS text) AS "text(char)" FROM CHAR_TBL; text(char) @@ -93,7 +95,9 @@ SELECT CAST(name 'namefield' AS varchar) AS "varchar(name)"; -- -- test SQL92 string functions +-- E### and T### are feature reference numbers from SQL99 -- +-- E021-09 trim function SELECT TRIM(BOTH FROM ' bunch o blanks ') = 'bunch o blanks' AS "bunch o blanks"; bunch o blanks ---------------- @@ -118,6 +122,7 @@ SELECT TRIM(BOTH 'x' FROM 'xxxxxsome Xsxxxxx') = 'some Xs' AS "some Xs"; t (1 row) +-- E021-06 substring expression SELECT SUBSTRING('1234567890' FROM 3) = '34567890' AS "34567890"; 34567890 ---------- @@ -130,6 +135,47 @@ SELECT SUBSTRING('1234567890' FROM 4 FOR 3) = '456' AS "456"; t (1 row) +-- T581 regular expression substring +SELECT SUBSTRING('abcdefg' FROM '(b|f).*(d)' FOR '#') AS "bcd"; + bcd +----- + bcd +(1 row) + +-- No match should return NULL +SELECT SUBSTRING('abcdefg' FROM '(1|2|3)' FOR '#') IS NULL AS "True"; + True +------ + t +(1 row) + +-- Null inputs should return NULL +SELECT SUBSTRING('abcdefg' FROM '(b|c)' FOR NULL) IS NULL AS "True"; + True +------ + t +(1 row) + +SELECT SUBSTRING(NULL FROM '(b|c)' FOR '#') IS NULL AS "True"; + True +------ + t +(1 row) + +SELECT SUBSTRING('abcdefg' FROM NULL FOR '#') IS NULL AS "True"; + True +------ + t +(1 row) + +-- PostgreSQL extention to allow omitting the escape character +SELECT SUBSTRING('abcdefg' FROM '(c|d).e') AS "cde"; + cde +----- + cde +(1 row) + +-- E021-11 position expression SELECT POSITION('4' IN '1234567890') = '4' AS "4"; 4 --- @@ -142,11 +188,37 @@ SELECT POSITION(5 IN '1234567890') = '5' AS "5"; t (1 row) +-- T312 character overlay function +SELECT OVERLAY('abcdef' PLACING '45' FROM 4) AS "abc45f"; + abc45f +-------- + abc45f +(1 row) + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5) AS "yabadaba"; + yabadaba +---------- + yabadaba +(1 row) + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5 FOR 0) AS "yabadabadoo"; + yabadabadoo +------------- + yabadabadoo +(1 row) + +SELECT OVERLAY('babosa' PLACING 'ubb' FROM 2 FOR 4) AS "bubba"; + bubba +------- + bubba +(1 row) + -- -- test LIKE -- Be sure to form every test as a LIKE/NOT LIKE pair. -- -- simplest examples +-- E061-04 like predicate SELECT 'hawkeye' LIKE 'h%' AS "true"; true ------ @@ -257,6 +329,7 @@ SELECT 'indio' NOT LIKE 'ind_o' ESCAPE '$' AS "false"; (1 row) -- escape character +-- E061-05 like predicate with escape clause SELECT 'h%' LIKE 'h#%' ESCAPE '#' AS "true"; true ------ @@ -469,6 +542,7 @@ SELECT 'Hawkeye' NOT ILIKE 'h%' AS "false"; -- -- test implicit type conversion -- +-- E021-07 character concatenation SELECT 'unknown' || ' and unknown' AS "Concat unknown types"; Concat unknown types ---------------------- diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql index b7f214f4d89c30c573cdb7c13a9149f302f5d5d9..3127d0ebffcc5e932c72d0da9e1c26cb77f15b93 100644 --- a/src/test/regress/sql/strings.sql +++ b/src/test/regress/sql/strings.sql @@ -4,6 +4,7 @@ -- -- SQL92 string continuation syntax +-- E021-03 character string literals SELECT 'first line' ' - next line' ' - third line' @@ -17,6 +18,7 @@ SELECT 'first line' -- -- test conversions between various string types +-- E021-10 implicit casting among the character data types -- SELECT CAST(f1 AS text) AS "text(char)" FROM CHAR_TBL; @@ -41,8 +43,10 @@ SELECT CAST(name 'namefield' AS varchar) AS "varchar(name)"; -- -- test SQL92 string functions +-- E### and T### are feature reference numbers from SQL99 -- +-- E021-09 trim function SELECT TRIM(BOTH FROM ' bunch o blanks ') = 'bunch o blanks' AS "bunch o blanks"; SELECT TRIM(LEADING FROM ' bunch o blanks ') = 'bunch o blanks ' AS "bunch o blanks "; @@ -51,20 +55,46 @@ SELECT TRIM(TRAILING FROM ' bunch o blanks ') = ' bunch o blanks' AS " bunch SELECT TRIM(BOTH 'x' FROM 'xxxxxsome Xsxxxxx') = 'some Xs' AS "some Xs"; +-- E021-06 substring expression SELECT SUBSTRING('1234567890' FROM 3) = '34567890' AS "34567890"; SELECT SUBSTRING('1234567890' FROM 4 FOR 3) = '456' AS "456"; +-- T581 regular expression substring +SELECT SUBSTRING('abcdefg' FROM '(b|f).*(d)' FOR '#') AS "bcd"; + +-- No match should return NULL +SELECT SUBSTRING('abcdefg' FROM '(1|2|3)' FOR '#') IS NULL AS "True"; + +-- Null inputs should return NULL +SELECT SUBSTRING('abcdefg' FROM '(b|c)' FOR NULL) IS NULL AS "True"; +SELECT SUBSTRING(NULL FROM '(b|c)' FOR '#') IS NULL AS "True"; +SELECT SUBSTRING('abcdefg' FROM NULL FOR '#') IS NULL AS "True"; + +-- PostgreSQL extention to allow omitting the escape character +SELECT SUBSTRING('abcdefg' FROM '(c|d).e') AS "cde"; + +-- E021-11 position expression SELECT POSITION('4' IN '1234567890') = '4' AS "4"; SELECT POSITION(5 IN '1234567890') = '5' AS "5"; +-- T312 character overlay function +SELECT OVERLAY('abcdef' PLACING '45' FROM 4) AS "abc45f"; + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5) AS "yabadaba"; + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5 FOR 0) AS "yabadabadoo"; + +SELECT OVERLAY('babosa' PLACING 'ubb' FROM 2 FOR 4) AS "bubba"; + -- -- test LIKE -- Be sure to form every test as a LIKE/NOT LIKE pair. -- -- simplest examples +-- E061-04 like predicate SELECT 'hawkeye' LIKE 'h%' AS "true"; SELECT 'hawkeye' NOT LIKE 'h%' AS "false"; @@ -94,6 +124,7 @@ SELECT 'indio' LIKE 'ind_o' ESCAPE '$' AS "true"; SELECT 'indio' NOT LIKE 'ind_o' ESCAPE '$' AS "false"; -- escape character +-- E061-05 like predicate with escape clause SELECT 'h%' LIKE 'h#%' ESCAPE '#' AS "true"; SELECT 'h%' NOT LIKE 'h#%' ESCAPE '#' AS "false"; @@ -156,6 +187,7 @@ SELECT 'Hawkeye' NOT ILIKE 'h%' AS "false"; -- test implicit type conversion -- +-- E021-07 character concatenation SELECT 'unknown' || ' and unknown' AS "Concat unknown types"; SELECT text 'text' || ' and unknown' AS "Concat text to unknown type"; @@ -165,4 +197,3 @@ SELECT char(20) 'characters' || 'and text' AS "Concat char to unknown type"; SELECT text 'text' || char(20) ' and characters' AS "Concat text to char"; SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar"; -