]> granicus.if.org Git - postgresql/commitdiff
Add string functions: concat(), concat_ws(), left(), right(), and reverse().
authorItagaki Takahiro <itagaki.takahiro@gmail.com>
Tue, 24 Aug 2010 06:30:44 +0000 (06:30 +0000)
committerItagaki Takahiro <itagaki.takahiro@gmail.com>
Tue, 24 Aug 2010 06:30:44 +0000 (06:30 +0000)
Pavel Stehule, reviewed by me.

doc/src/sgml/func.sgml
src/backend/utils/adt/varlena.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/test/regress/expected/text.out
src/test/regress/sql/text.sql

index ae5139cb9ebc9aff6bfdbf6ce5efcfb181164652..66f72f321430677e74917cefd5218c3bfb6b9701 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.529 2010/08/17 04:37:20 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.530 2010/08/24 06:30:43 itagaki Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
    <indexterm>
     <primary>chr</primary>
    </indexterm>
+   <indexterm>
+    <primary>concat</primary>
+   </indexterm>
+   <indexterm>
+    <primary>concat_ws</primary>
+   </indexterm>
    <indexterm>
     <primary>convert</primary>
    </indexterm>
    <indexterm>
     <primary>initcap</primary>
    </indexterm>
+   <indexterm>
+    <primary>left</primary>
+   </indexterm>
    <indexterm>
     <primary>lpad</primary>
    </indexterm>
    <indexterm>
     <primary>replace</primary>
    </indexterm>
+   <indexterm>
+    <primary>reverse</primary>
+   </indexterm>
+   <indexterm>
+    <primary>right</primary>
+   </indexterm>
    <indexterm>
     <primary>rpad</primary>
    </indexterm>
        <entry><literal>A</literal></entry>
       </row>
 
+      <row>
+       <entry>
+        <literal><function>concat</function>(<parameter>str</parameter> <type>"any"</type>
+         [, <parameter>str</parameter> <type>"any"</type> [, ...] ])</literal>
+       </entry>
+       <entry><type>text</type></entry>
+       <entry>
+        Concatenate all arguments. NULL arguments are ignored.
+       </entry>
+       <entry><literal>concat('abcde', 2, NULL, 22)</literal></entry>
+       <entry><literal>abcde222</literal></entry>
+      </row>
+
+      <row>
+       <entry>
+        <literal><function>concat_ws</function>(<parameter>sep</parameter> <type>text</type>,
+        <parameter>str</parameter> <type>"any"</type>
+        [, <parameter>str</parameter> <type>"any"</type> [, ...] ])</literal>
+       </entry>
+       <entry><type>text</type></entry>
+       <entry>
+        Concatenate all but first arguments with separators. The first
+        parameter is used as a separator. NULL arguments are ignored.
+       </entry>
+       <entry><literal>concat_ws(',', 'abcde', 2, NULL, 22)</literal></entry>
+       <entry><literal>abcde,2,22</literal></entry>
+      </row>
+
       <row>
        <entry>
         <literal><function>convert</function>(<parameter>string</parameter> <type>bytea</type>,
        <entry><literal>Hi Thomas</literal></entry>
       </row>
 
+      <row>
+       <entry>
+        <literal><function>left</function>(<parameter>str</parameter> <type>text</type>,
+        <parameter>n</parameter> <type>int</type>)</literal>
+       </entry>
+       <entry><type>text</type></entry>
+       <entry>
+        Return first <replaceable>n</> characters in the string. When <replaceable>n</>
+        is negative, return all but last |<replaceable>n</>| characters.
+        </entry>
+       <entry><literal>left('abcde', 2)</literal></entry>
+       <entry><literal>ab</literal></entry>
+      </row>
+
       <row>
        <entry><literal><function>length</function>(<parameter>string</parameter>)</literal></entry>
        <entry><type>int</type></entry>
        <entry><literal>abXXefabXXef</literal></entry>
       </row>
 
+      <row>
+       <entry>
+        <literal><function>reverse</function>(<parameter>str</parameter>)</literal>
+       </entry>
+       <entry><type>text</type></entry>
+       <entry>
+        Return reversed string.
+       </entry>
+       <entry><literal>reverse('abcde')</literal></entry>
+       <entry><literal>edcba</literal></entry>
+      </row>
+
+      <row>
+       <entry>
+        <literal><function>right</function>(<parameter>str</parameter> <type>text</type>,
+         <parameter>n</parameter> <type>int</type>)</literal>
+       </entry>
+       <entry><type>text</type></entry>
+       <entry>
+        Return last <replaceable>n</> characters in the string. When <replaceable>n</>
+        is negative, return all but first |<replaceable>n</>| characters.
+       </entry>
+       <entry><literal>right('abcde', 2)</literal></entry>
+       <entry><literal>de</literal></entry>
+      </row>
+
       <row>
        <entry>
         <literal><function>rpad</function>(<parameter>string</parameter> <type>text</type>,
index 1ad4667d63356a77b1b49241c982bda5e67028cc..cf62dd1b57b6b6a73d1e76079873ce65540f5fb1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.179 2010/08/10 21:51:00 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.180 2010/08/24 06:30:43 itagaki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3556,3 +3556,149 @@ string_agg_finalfn(PG_FUNCTION_ARGS)
        else
                PG_RETURN_NULL();
 }
+
+static text *
+concat_internal(const char *sepstr, int seplen, int argidx, FunctionCallInfo fcinfo)
+{
+       StringInfoData  str;
+       text               *result;
+       int                             i;
+
+       initStringInfo(&str);
+
+       for (i = argidx; i < PG_NARGS(); i++)
+       {
+               if (!PG_ARGISNULL(i))
+               {
+                       Oid             valtype;
+                       Datum   value;
+                       Oid             typOutput;
+                       bool    typIsVarlena;
+
+                       if (i > argidx)
+                               appendBinaryStringInfo(&str, sepstr, seplen);
+
+                       /* append n-th value */
+                       value = PG_GETARG_DATUM(i);
+                       valtype = get_fn_expr_argtype(fcinfo->flinfo, i);
+                       getTypeOutputInfo(valtype, &typOutput, &typIsVarlena);
+                       appendStringInfoString(&str,
+                               OidOutputFunctionCall(typOutput, value));
+               }
+       }
+
+       result = cstring_to_text_with_len(str.data, str.len);
+       pfree(str.data);
+
+       return result;
+}
+
+/*
+ * Concatenate all arguments. NULL arguments are ignored.
+ */
+Datum
+text_concat(PG_FUNCTION_ARGS)
+{
+       PG_RETURN_TEXT_P(concat_internal(NULL, 0, 0, fcinfo));
+}
+
+/*
+ * Concatenate all but first argument values with separators. The first
+ * parameter is used as a separator. NULL arguments are ignored.
+ */
+Datum
+text_concat_ws(PG_FUNCTION_ARGS)
+{
+       text               *sep;
+
+       /* return NULL when separator is NULL */
+       if (PG_ARGISNULL(0))
+               PG_RETURN_NULL();
+
+       sep = PG_GETARG_TEXT_PP(0);
+
+       PG_RETURN_TEXT_P(concat_internal(
+               VARDATA_ANY(sep), VARSIZE_ANY_EXHDR(sep), 1, fcinfo));
+}
+
+/*
+ * Return first n characters in the string. When n is negative,
+ * return all but last |n| characters.
+ */
+Datum
+text_left(PG_FUNCTION_ARGS)
+{
+       text       *str = PG_GETARG_TEXT_PP(0);
+       const char *p = VARDATA_ANY(str);
+       int                     len = VARSIZE_ANY_EXHDR(str);
+       int                     n = PG_GETARG_INT32(1);
+       int                     rlen;
+
+       if (n < 0)
+               n = pg_mbstrlen_with_len(p, len) + n;
+       rlen = pg_mbcharcliplen(p, len, n);
+
+       PG_RETURN_TEXT_P(cstring_to_text_with_len(p, rlen));
+}
+
+/*
+ * Return last n characters in the string. When n is negative,
+ * return all but first |n| characters.
+ */
+Datum
+text_right(PG_FUNCTION_ARGS)
+{
+       text       *str = PG_GETARG_TEXT_PP(0);
+       const char *p = VARDATA_ANY(str);
+       int                     len = VARSIZE_ANY_EXHDR(str);
+       int                     n = PG_GETARG_INT32(1);
+       int                     off;
+
+       if (n < 0)
+               n = -n;
+       else
+               n = pg_mbstrlen_with_len(p, len) - n;
+       off = pg_mbcharcliplen(p, len, n);
+
+       PG_RETURN_TEXT_P(cstring_to_text_with_len(p + off, len - off));
+}
+
+/*
+ * Return reversed string
+ */
+Datum
+text_reverse(PG_FUNCTION_ARGS)
+{
+       text               *str = PG_GETARG_TEXT_PP(0);
+       const char         *p = VARDATA_ANY(str);
+       int                             len = VARSIZE_ANY_EXHDR(str);
+       const char         *endp = p + len;
+       text               *result;
+       char               *dst;
+
+       result = palloc(len + VARHDRSZ);
+       dst = (char*) VARDATA(result) + len;
+       SET_VARSIZE(result, len + VARHDRSZ);
+
+       if (pg_database_encoding_max_length() > 1)
+       {
+               /* multibyte version */
+               while (p < endp)
+               {
+                       int             sz;
+
+                       sz = pg_mblen(p);
+                       dst -= sz;
+                       memcpy(dst, p, sz);
+                       p += sz;
+               }
+       }
+       else
+       {
+               /* single byte version */
+               while (p < endp)
+                       *(--dst) = *p++;
+       }
+
+       PG_RETURN_TEXT_P(result);
+}
index 5c538e6f790633b3c16c69ca5c650728bea80acb..f5a96c81f58e2eeb29d023de7ed91d143874f969 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.596 2010/08/21 10:59:17 mha Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.597 2010/08/24 06:30:43 itagaki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201008211
+#define CATALOG_VERSION_NO     201008241
 
 #endif
index c4dd14a71e7162a7bd1df55dfdf4791a3432995a..b3f1e14fd32fb74b4f9bdddc273c6ff70881fd50 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.580 2010/08/21 10:59:17 mha Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.581 2010/08/24 06:30:43 itagaki Exp $
  *
  * NOTES
  *       The script catalog/genbki.pl reads this file and generates .bki
@@ -2722,6 +2722,16 @@ DESCR("I/O");
 DATA(insert OID = 1799 (  oidout                  PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 "26" _null_ _null_ _null_ _null_ oidout _null_ _null_ _null_ ));
 DESCR("I/O");
 
+DATA(insert OID = 3058 ( concat                PGNSP PGUID 12 1 0 2276 f f f f f s 1 0 25 "2276" "{2276}" "{v}" _null_ _null_  text_concat _null_ _null_ _null_ ));
+DESCR("concatenate values");
+DATA(insert OID = 3059 ( concat_ws     PGNSP PGUID 12 1 0 2276 f f f f f s 2 0 25 "25 2276" "{25,2276}" "{i,v}" _null_ _null_  text_concat_ws _null_ _null_ _null_ ));
+DESCR("concatenate values with separators");
+DATA(insert OID = 3060 ( left          PGNSP PGUID 12 1 0 0 f f f t f i 2 0 25 "25 23" _null_ _null_ _null_ _null_  text_left _null_ _null_ _null_ ));
+DESCR("return the first n characters");
+DATA(insert OID = 3061 ( right         PGNSP PGUID 12 1 0 0 f f f t f i 2 0 25 "25 23" _null_ _null_ _null_ _null_  text_right _null_ _null_ _null_ ));
+DESCR("return the last n characters");
+DATA(insert OID = 3062 ( reverse       PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_  text_reverse  _null_ _null_ _null_ ));
+DESCR("reverse text");
 
 DATA(insert OID = 1810 (  bit_length      PGNSP PGUID 14 1 0 0 f f f t f i 1 0 23 "17" _null_ _null_ _null_ _null_ "select pg_catalog.octet_length($1) * 8" _null_ _null_ _null_ ));
 DESCR("length in bits");
index 258a37494303214b7e5a984fa50ac6cbf0d92f77..4612cb354302eb0f81074ba120fb86e4065b966c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.354 2010/08/10 21:51:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.355 2010/08/24 06:30:44 itagaki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -733,6 +733,12 @@ extern Datum pg_column_size(PG_FUNCTION_ARGS);
 extern Datum string_agg_transfn(PG_FUNCTION_ARGS);
 extern Datum string_agg_finalfn(PG_FUNCTION_ARGS);
 
+extern Datum text_concat(PG_FUNCTION_ARGS);
+extern Datum text_concat_ws(PG_FUNCTION_ARGS);
+extern Datum text_left(PG_FUNCTION_ARGS);
+extern Datum text_right(PG_FUNCTION_ARGS);
+extern Datum text_reverse(PG_FUNCTION_ARGS);
+
 /* version.c */
 extern Datum pgsql_version(PG_FUNCTION_ARGS);
 
index 08d002fe71ecadf89a7e04c4c79898d075165146..84f4a5cda8b79bd033f76b6288228e220c4d74fd 100644 (file)
@@ -51,3 +51,70 @@ ERROR:  operator does not exist: integer || numeric
 LINE 1: select 3 || 4.0;
                  ^
 HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
+/*
+ * string functions
+ */
+select concat('one');
+ concat 
+--------
+ one
+(1 row)
+
+select concat(1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD'));
+        concat        
+----------------------
+ 123hellotf03-09-2010
+(1 row)
+
+select concat_ws('#','one');
+ concat_ws 
+-----------
+ one
+(1 row)
+
+select concat_ws('#',1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD'));
+         concat_ws          
+----------------------------
+ 1#2#3#hello#t#f#03-09-2010
+(1 row)
+
+select concat_ws(',',10,20,null,30);
+ concat_ws 
+-----------
+ 10,20,30
+(1 row)
+
+select concat_ws('',10,20,null,30);
+ concat_ws 
+-----------
+ 102030
+(1 row)
+
+select concat_ws(NULL,10,20,null,30) is null;
+ ?column? 
+----------
+ t
+(1 row)
+
+select reverse('abcde');
+ reverse 
+---------
+ edcba
+(1 row)
+
+select i, left('ahoj', i), right('ahoj', i) from generate_series(-5, 5) t(i) order by i;
+ i  | left | right 
+----+------+-------
+ -5 |      | 
+ -4 |      | 
+ -3 | a    | j
+ -2 | ah   | oj
+ -1 | aho  | hoj
+  0 |      | 
+  1 | a    | j
+  2 | ah   | oj
+  3 | aho  | hoj
+  4 | ahoj | ahoj
+  5 | ahoj | ahoj
+(11 rows)
+
index b739e56e2d69ff06c420bbf1239baabcb64aa75f..a8768ee81aa5526112a22e843063f6f954312363 100644 (file)
@@ -28,3 +28,16 @@ select 'four: ' || 2+2;
 -- but not this:
 
 select 3 || 4.0;
+
+/*
+ * string functions
+ */
+select concat('one');
+select concat(1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD'));
+select concat_ws('#','one');
+select concat_ws('#',1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD'));
+select concat_ws(',',10,20,null,30);
+select concat_ws('',10,20,null,30);
+select concat_ws(NULL,10,20,null,30) is null;
+select reverse('abcde');
+select i, left('ahoj', i), right('ahoj', i) from generate_series(-5, 5) t(i) order by i;