Implement SQL99 OVERLAY(). Allows substitution of a substring in a string.
authorThomas G. Lockhart <[email protected]>
Tue, 11 Jun 2002 15:44:38 +0000 (15:44 +0000)
committerThomas G. Lockhart <[email protected]>
Tue, 11 Jun 2002 15:44:38 +0000 (15:44 +0000)
Implement SQL99 SIMILAR TO as a synonym for our existing operator "~".
Implement SQL99 regular expression SUBSTRING(string FROM pat FOR escape).
 Extend the definition to make the FOR clause optional.
 Define textregexsubstr() to actually implement this feature.
Update the regression test to include these new string features.
 All tests pass.
Rename the regular expression support routines from "pg95_xxx" to "pg_xxx".
Define CREATE CHARACTER SET in the parser per SQL99. No implementation yet.

16 files changed:
doc/src/sgml/ref/set.sgml
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/parser/scan.l
src/backend/regex/regcomp.c
src/backend/regex/regerror.c
src/backend/regex/regexec.c
src/backend/regex/regfree.c
src/backend/regex/retest.c
src/backend/utils/adt/regexp.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/regex/regex.h
src/include/utils/builtins.h
src/test/regress/expected/strings.out
src/test/regress/sql/strings.sql

index 65ff6eca29fa8ef952ebdf5220a7f3ba52160a14..5ccb3a7a0df96b159ad44d8082a248c934a36c0b 100644 (file)
@@ -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
index fbf63db85e2f78a87199b2f731be4679f85c7299..b8fd3d034893ecb6faaaab10267bc8489d495f27 100644 (file)
@@ -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
        ;
 
index c60b2df2a6d5a153c1006f5b6be257c17094753e..dca44c448f988c37ae4c38ec6781a93500a02e25 100644 (file)
@@ -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},
index a6bc3549e17d60d51499d4c52ec046bc96a7464d..e3d88b4521b04472fb51761a5801206fe98b3616 100644 (file)
@@ -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;
index bea464bd2ae88aa2b8a8bd0d8f27e27d9e1ff68d..d6f7b26fa1a4cb77579963312a8b768664dee2c8 100644 (file)
@@ -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;
 }
 
index 3fa00946aa414e2307991f96680e5ec3cf553d43..fb12cba3048ee78dda4d4f425438702133abf230 100644 (file)
@@ -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;
index 476773044138393e42f671896496dd5e574cbdd6..06459ef1dbcaefec6d33d44df2ba88ebb6b85031 100644 (file)
@@ -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;
 
index 87e0c99ef96690afcb1bb3b6b9da293216fa4add..5672fcf240f4f9f7358f26f3d53d4ddefe36d041 100644 (file)
@@ -49,7 +49,7 @@
  * regfree - free everything
  */
 void
-pg95_regfree(regex_t *preg)
+pg_regfree(regex_t *preg)
 {
    struct re_guts *g;
 
index eee8ef09ae3a24c0e4c041deefb0221c44d347a9..ca5d6c5394aa6fd43c7af2941982f2eb2c84129b 100644 (file)
@@ -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);
    }
 }
index a61717a86021f791fc69f81b34ea667faaac0773..9afc58feee7d6111721a4923408687c7ef57110b 100644 (file)
@@ -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();
+}
index bd861396210ee2a03750535db067d45d5498fb98..ae10bfb6879328e574204b041996137a3e4ca1ab 100644 (file)
@@ -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
index a7d56dc90dcb4b094e9c675cd49e0637d3a6aa4c..56831dcfca105ec5c2aa3ce201ffde0fdf2de1fc 100644 (file)
@@ -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_ ));
index 09f966e1bf26ae1c712dfcb2f9713d53f6486130..dd8e0da171bd61491535dbf9ab4921e07d1c70d7 100644 (file)
@@ -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_ */
index 6340aa225403f402a3f65cdecca525f5f20e46b4..7676ce5663fdbaf7184ed0675554cbe8306ce001 100644 (file)
@@ -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);
index ebfe8eeb66328d0014fc1981638dba5698a84a34..680a76c0bd5696f9c170389fee7a15a80d7fe2fc 100644 (file)
@@ -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 
 ----------------------
index b7f214f4d89c30c573cdb7c13a9149f302f5d5d9..3127d0ebffcc5e932c72d0da9e1c26cb77f15b93 100644 (file)
@@ -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";
-