Add get_bit/set_bit functions for bit strings, paralleling those for bytea,
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Jan 2010 20:55:32 +0000 (20:55 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Jan 2010 20:55:32 +0000 (20:55 +0000)
and implement OVERLAY() for bit strings and bytea.

In passing also convert text OVERLAY() to a true built-in, instead of
relying on a SQL function.

Leonardo F, reviewed by Kevin Grittner

13 files changed:
doc/src/sgml/func.sgml
src/backend/parser/gram.y
src/backend/utils/adt/varbit.c
src/backend/utils/adt/varlena.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/include/utils/bytea.h
src/include/utils/varbit.h
src/test/regress/expected/bit.out
src/test/regress/expected/strings.out
src/test/regress/sql/bit.sql
src/test/regress/sql/strings.sql

index 3143767dc5ec1cdcedc998b5d1fd420dcaee66cb..f777f9f16503831831880fe340c27867ea3bf440 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.497 2010/01/19 05:50:18 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.498 2010/01/25 20:55:32 tgl Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
        <entry>Return Type</entry>
        <entry>Description</entry>
        <entry>Example</entry>
-       <entry>Result</entry>  
+       <entry>Result</entry>
       </row>
      </thead>
 
        <entry><literal>\\Post'gres\000</literal></entry>
       </row>
 
-      <row>
-       <entry><function>get_bit</function>(<parameter>string</parameter>, <parameter>offset</parameter>)</entry>
-       <entry><type>int</type></entry>
-       <entry>
-        Extract bit from string
-        <indexterm>
-         <primary>get_bit</primary>
-        </indexterm>
-       </entry>
-       <entry><literal>get_bit(E'Th\\000omas'::bytea, 45)</literal></entry>
-       <entry><literal>1</literal></entry>
-      </row>
-
-      <row>
-       <entry><function>get_byte</function>(<parameter>string</parameter>, <parameter>offset</parameter>)</entry>
-       <entry><type>int</type></entry>
-       <entry>
-        Extract byte from string
-        <indexterm>
-         <primary>get_byte</primary>
-        </indexterm>
-       </entry>
-       <entry><literal>get_byte(E'Th\\000omas'::bytea, 4)</literal></entry>
-       <entry><literal>109</literal></entry>
-      </row>
-
       <row>
        <entry><literal><function>octet_length</function>(<parameter>string</parameter>)</literal></entry>
        <entry><type>int</type></entry>
       </row>
 
       <row>
-       <entry><literal><function>position</function>(<parameter>substring</parameter> in <parameter>string</parameter>)</literal></entry>
-       <entry><type>int</type></entry>
-       <entry>Location of specified substring</entry>
-      <entry><literal>position(E'\\000om'::bytea in E'Th\\000omas'::bytea)</literal></entry>
-       <entry><literal>3</literal></entry>
-      </row>
-
-      <row>
-       <entry><function>set_bit</function>(<parameter>string</parameter>,
-       <parameter>offset</parameter>, <parameter>newvalue</>)</entry>
+       <entry><literal><function>overlay</function>(<parameter>string</parameter> placing <parameter>string</parameter> from <type>int</type> <optional>for <type>int</type></optional>)</literal></entry>
        <entry><type>bytea</type></entry>
        <entry>
-        Set bit in string
-        <indexterm>
-         <primary>set_bit</primary>
-        </indexterm>
+        Replace substring
        </entry>
-       <entry><literal>set_bit(E'Th\\000omas'::bytea, 45, 0)</literal></entry>
-       <entry><literal>Th\000omAs</literal></entry>
+       <entry><literal>overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 2 for 3)</literal></entry>
+       <entry><literal>T\\002\\003mas</literal></entry>
       </row>
 
       <row>
-       <entry><function>set_byte</function>(<parameter>string</parameter>,
-       <parameter>offset</parameter>, <parameter>newvalue</>)</entry>
-       <entry><type>bytea</type></entry>
-       <entry>
-        Set byte in string
-        <indexterm>
-         <primary>set_byte</primary>
-        </indexterm>
-       </entry>
-       <entry><literal>set_byte(E'Th\\000omas'::bytea, 4, 64)</literal></entry>
-       <entry><literal>Th\000o@as</literal></entry>
+       <entry><literal><function>position</function>(<parameter>substring</parameter> in <parameter>string</parameter>)</literal></entry>
+       <entry><type>int</type></entry>
+       <entry>Location of specified substring</entry>
+      <entry><literal>position(E'\\000om'::bytea in E'Th\\000omas'::bytea)</literal></entry>
+       <entry><literal>3</literal></entry>
       </row>
 
       <row>
       </entry>
       <entry><type>bytea</type></entry>
       <entry>
-       Decode binary string from <parameter>string</parameter> previously 
+       Decode binary string from <parameter>string</parameter> previously
        encoded with <function>encode</>.  Parameter type is same as in <function>encode</>.
       </entry>
       <entry><literal>decode(E'123\\000456', 'escape')</literal></entry>
       <entry><literal>123\000456</literal></entry>
      </row>
 
+      <row>
+       <entry>
+        <literal><function>get_bit</function>(<parameter>string</parameter>, <parameter>offset</parameter>)</literal>
+       </entry>
+       <entry><type>int</type></entry>
+       <entry>
+        Extract bit from string
+        <indexterm>
+         <primary>get_bit</primary>
+        </indexterm>
+       </entry>
+       <entry><literal>get_bit(E'Th\\000omas'::bytea, 45)</literal></entry>
+       <entry><literal>1</literal></entry>
+      </row>
+
+      <row>
+       <entry>
+        <literal><function>get_byte</function>(<parameter>string</parameter>, <parameter>offset</parameter>)</literal>
+       </entry>
+       <entry><type>int</type></entry>
+       <entry>
+        Extract byte from string
+        <indexterm>
+         <primary>get_byte</primary>
+        </indexterm>
+       </entry>
+       <entry><literal>get_byte(E'Th\\000omas'::bytea, 4)</literal></entry>
+       <entry><literal>109</literal></entry>
+      </row>
+
      <row>
       <entry><literal><function>length</function>(<parameter>string</parameter>)</literal></entry>
       <entry><type>int</type></entry>
       <entry><literal>md5(E'Th\\000omas'::bytea)</literal></entry>
       <entry><literal>8ab2d3c9689aaf18 b4958c334c82d8b1</literal></entry>
      </row>
+
+      <row>
+       <entry>
+        <literal><function>set_bit</function>(<parameter>string</parameter>,
+        <parameter>offset</parameter>, <parameter>newvalue</>)</literal>
+       </entry>
+       <entry><type>bytea</type></entry>
+       <entry>
+        Set bit in string
+        <indexterm>
+         <primary>set_bit</primary>
+        </indexterm>
+       </entry>
+       <entry><literal>set_bit(E'Th\\000omas'::bytea, 45, 0)</literal></entry>
+       <entry><literal>Th\000omAs</literal></entry>
+      </row>
+
+      <row>
+       <entry>
+        <literal><function>set_byte</function>(<parameter>string</parameter>,
+        <parameter>offset</parameter>, <parameter>newvalue</>)</literal>
+       </entry>
+       <entry><type>bytea</type></entry>
+       <entry>
+        Set byte in string
+        <indexterm>
+         <primary>set_byte</primary>
+        </indexterm>
+       </entry>
+       <entry><literal>set_byte(E'Th\\000omas'::bytea, 4, 64)</literal></entry>
+       <entry><literal>Th\000o@as</literal></entry>
+      </row>
     </tbody>
    </tgroup>
   </table>
     <literal><function>bit_length</function></literal>,
     <literal><function>octet_length</function></literal>,
     <literal><function>position</function></literal>,
-    <literal><function>substring</function></literal>.
+    <literal><function>substring</function></literal>,
+    <literal><function>overlay</function></literal>.
+   </para>
+
+   <para>
+    The following functions work on bit strings as well as binary
+    strings:
+    <literal><function>get_bit</function></literal>,
+    <literal><function>set_bit</function></literal>.
    </para>
 
    <para>
index c81e8a38ec9b468161d7acfeeccd69231f2119b0..981d2614042c7cfaf1e63861ff736d7aff535167 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.704 2010/01/22 16:40:18 rhaas Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.705 2010/01/25 20:55:32 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -9586,9 +9586,9 @@ func_expr:    func_name '(' ')' over_clause
            | 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, B, 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))
+                    * overlay(A, B, C)
                     */
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = SystemFuncName("overlay");
@@ -10150,6 +10150,7 @@ extract_arg:
  * SQL99 defines the OVERLAY() function:
  * o overlay(text placing text from int for int)
  * o overlay(text placing text from int)
+ * and similarly for binary strings
  */
 overlay_list:
            a_expr overlay_placing substr_from substr_for
index 7f39866f00550d447f906dd61d0b4e5a18c83278..4a550cdae2980a3ede53b08eb322b743ee6f1d93 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.63 2010/01/07 20:17:43 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.64 2010/01/25 20:55:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #define HEXDIG(z)   ((z)<10 ? ((z)+'0') : ((z)-10+'A'))
 
+static VarBit *bit_catenate(VarBit *arg1, VarBit *arg2);
 static VarBit *bitsubstring(VarBit *arg, int32 s, int32 l,
                            bool length_not_specified);
+static VarBit *bit_overlay(VarBit *t1, VarBit *t2, int sp, int sl);
 
 
 /* common code for bittypmodin and varbittypmodin */
@@ -877,6 +879,13 @@ bitcat(PG_FUNCTION_ARGS)
 {
    VarBit     *arg1 = PG_GETARG_VARBIT_P(0);
    VarBit     *arg2 = PG_GETARG_VARBIT_P(1);
+
+   PG_RETURN_VARBIT_P(bit_catenate(arg1, arg2));
+}
+
+static VarBit *
+bit_catenate(VarBit *arg1, VarBit *arg2)
+{
    VarBit     *result;
    int         bitlen1,
                bitlen2,
@@ -919,7 +928,7 @@ bitcat(PG_FUNCTION_ARGS)
        }
    }
 
-   PG_RETURN_VARBIT_P(result);
+   return result;
 }
 
 /* bitsubstr
@@ -1034,6 +1043,67 @@ bitsubstring(VarBit *arg, int32 s, int32 l, bool length_not_specified)
    return result;
 }
 
+/*
+ * bitoverlay
+ * Replace specified substring of first string with second
+ *
+ * The SQL standard defines OVERLAY() in terms of substring and concatenation.
+ * This code is a direct implementation of what the standard says.
+ */
+Datum
+bitoverlay(PG_FUNCTION_ARGS)
+{
+   VarBit     *t1 = PG_GETARG_VARBIT_P(0);
+   VarBit     *t2 = PG_GETARG_VARBIT_P(1);
+   int         sp = PG_GETARG_INT32(2); /* substring start position */
+   int         sl = PG_GETARG_INT32(3); /* substring length */
+
+   PG_RETURN_VARBIT_P(bit_overlay(t1, t2, sp, sl));
+}
+
+Datum
+bitoverlay_no_len(PG_FUNCTION_ARGS)
+{
+   VarBit     *t1 = PG_GETARG_VARBIT_P(0);
+   VarBit     *t2 = PG_GETARG_VARBIT_P(1);
+   int         sp = PG_GETARG_INT32(2); /* substring start position */
+   int         sl;
+
+   sl = VARBITLEN(t2);             /* defaults to length(t2) */
+   PG_RETURN_VARBIT_P(bit_overlay(t1, t2, sp, sl));
+}
+
+static VarBit *
+bit_overlay(VarBit *t1, VarBit *t2, int sp, int sl)
+{
+   VarBit     *result;
+   VarBit     *s1;
+   VarBit     *s2;
+   int         sp_pl_sl;
+
+   /*
+    * Check for possible integer-overflow cases.  For negative sp,
+    * throw a "substring length" error because that's what should be
+    * expected according to the spec's definition of OVERLAY().
+    */
+   if (sp <= 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_SUBSTRING_ERROR),
+                errmsg("negative substring length not allowed")));
+   sp_pl_sl = sp + sl;
+   if (sp_pl_sl <= sl)
+       ereport(ERROR,
+               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                errmsg("integer out of range")));
+
+   s1 = bitsubstring(t1, 1, sp-1, false);
+   s2 = bitsubstring(t1, sp_pl_sl, -1, true);
+   result = bit_catenate(s1, t2);
+   result = bit_catenate(result, s2);
+
+   return result;
+}
+
 /* bitlength, bitoctetlength
  * Return the length of a bit string
  */
@@ -1606,3 +1676,103 @@ bitposition(PG_FUNCTION_ARGS)
    }
    PG_RETURN_INT32(0);
 }
+
+
+/*
+ * bitsetbit
+ *
+ * Given an instance of type 'bit' creates a new one with
+ * the Nth bit set to the given value.
+ *
+ * The bit location is specified left-to-right in a zero-based fashion
+ * consistent with the other get_bit and set_bit functions, but
+ * inconsistent with the standard substring, position, overlay functions
+ */
+Datum
+bitsetbit(PG_FUNCTION_ARGS)
+{
+   VarBit     *arg1 = PG_GETARG_VARBIT_P(0);
+   int32       n = PG_GETARG_INT32(1);
+   int32       newBit = PG_GETARG_INT32(2);
+   VarBit     *result;
+   int         len,
+               bitlen;
+   bits8      *r,
+              *p;
+   int         byteNo,
+               bitNo;
+
+   bitlen = VARBITLEN(arg1);
+   if (n < 0 || n >= bitlen)
+       ereport(ERROR,
+               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                errmsg("bit index %d out of valid range (0..%d)",
+                       n, bitlen - 1)));
+   /*
+    * sanity check!
+    */
+   if (newBit != 0 && newBit != 1)
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                errmsg("new bit must be 0 or 1")));
+
+   len = VARSIZE(arg1);
+   result = (VarBit *) palloc(len);
+   SET_VARSIZE(result, len);
+   VARBITLEN(result) = bitlen;
+
+   p = VARBITS(arg1);
+   r = VARBITS(result);
+
+   memcpy(r, p, VARBITBYTES(arg1));
+
+   byteNo = n / BITS_PER_BYTE;
+   bitNo = BITS_PER_BYTE - 1 - (n % BITS_PER_BYTE);
+
+   /*
+    * Update the byte.
+    */
+   if (newBit == 0)
+       r[byteNo] &= (~(1 << bitNo));
+   else
+       r[byteNo] |= (1 << bitNo);
+
+   PG_RETURN_VARBIT_P(result);
+}
+
+/*
+ * bitgetbit
+ *
+ * returns the value of the Nth bit of a bit array (0 or 1).
+ *
+ * The bit location is specified left-to-right in a zero-based fashion
+ * consistent with the other get_bit and set_bit functions, but
+ * inconsistent with the standard substring, position, overlay functions
+ */
+Datum
+bitgetbit(PG_FUNCTION_ARGS)
+{
+   VarBit     *arg1 = PG_GETARG_VARBIT_P(0);
+   int32       n = PG_GETARG_INT32(1);
+   int         bitlen;
+   bits8      *p;
+   int         byteNo,
+               bitNo;
+
+   bitlen = VARBITLEN(arg1);
+   if (n < 0 || n >= bitlen)
+       ereport(ERROR,
+               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                errmsg("bit index %d out of valid range (0..%d)",
+                       n, bitlen - 1)));
+
+   p = VARBITS(arg1);
+
+   byteNo = n / BITS_PER_BYTE;
+   bitNo = BITS_PER_BYTE - 1 - (n % BITS_PER_BYTE);
+
+   if (p[byteNo] & (1 << bitNo))
+       PG_RETURN_INT32(1);
+   else
+       PG_RETURN_INT32(0);
+}
index 53650c88786fdb4a655d3f335ad4067b224236e3..7a8abf14a83d11ae57fe46bcaa419271c6a2ae23 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.173 2010/01/02 16:57:55 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.174 2010/01/25 20:55:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,11 +60,19 @@ static int  text_position(text *t1, text *t2);
 static void text_position_setup(text *t1, text *t2, TextPositionState *state);
 static int text_position_next(int start_pos, TextPositionState *state);
 static void text_position_cleanup(TextPositionState *state);
+static text *text_catenate(text *t1, text *t2);
 static text *text_substring(Datum str,
               int32 start,
               int32 length,
               bool length_not_specified);
+static text *text_overlay(text *t1, text *t2, int sp, int sl);
 static void appendStringInfoText(StringInfo str, const text *t);
+static bytea *bytea_catenate(bytea *t1, bytea *t2);
+static bytea *bytea_substring(Datum str,
+               int S,
+               int L,
+               bool length_not_specified);
+static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl);
 
 
 /*****************************************************************************
@@ -559,17 +567,31 @@ textcat(PG_FUNCTION_ARGS)
 {
    text       *t1 = PG_GETARG_TEXT_PP(0);
    text       *t2 = PG_GETARG_TEXT_PP(1);
+
+   PG_RETURN_TEXT_P(text_catenate(t1, t2));
+}
+
+/*
+ * text_catenate
+ * Guts of textcat(), broken out so it can be used by other functions
+ *
+ * Arguments can be in short-header form, but not compressed or out-of-line
+ */
+static text *
+text_catenate(text *t1, text *t2)
+{
+   text       *result;
    int         len1,
                len2,
                len;
-   text       *result;
    char       *ptr;
 
    len1 = VARSIZE_ANY_EXHDR(t1);
+   len2 = VARSIZE_ANY_EXHDR(t2);
+
+   /* paranoia ... probably should throw error instead? */
    if (len1 < 0)
        len1 = 0;
-
-   len2 = VARSIZE_ANY_EXHDR(t2);
    if (len2 < 0)
        len2 = 0;
 
@@ -586,7 +608,7 @@ textcat(PG_FUNCTION_ARGS)
    if (len2 > 0)
        memcpy(ptr + len1, VARDATA_ANY(t2), len2);
 
-   PG_RETURN_TEXT_P(result);
+   return result;
 }
 
 /*
@@ -865,6 +887,67 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
    return NULL;
 }
 
+/*
+ * textoverlay
+ * Replace specified substring of first string with second
+ *
+ * The SQL standard defines OVERLAY() in terms of substring and concatenation.
+ * This code is a direct implementation of what the standard says.
+ */
+Datum
+textoverlay(PG_FUNCTION_ARGS)
+{
+   text       *t1 = PG_GETARG_TEXT_PP(0);
+   text       *t2 = PG_GETARG_TEXT_PP(1);
+   int         sp = PG_GETARG_INT32(2); /* substring start position */
+   int         sl = PG_GETARG_INT32(3); /* substring length */
+
+   PG_RETURN_TEXT_P(text_overlay(t1, t2, sp, sl));
+}
+
+Datum
+textoverlay_no_len(PG_FUNCTION_ARGS)
+{
+   text       *t1 = PG_GETARG_TEXT_PP(0);
+   text       *t2 = PG_GETARG_TEXT_PP(1);
+   int         sp = PG_GETARG_INT32(2); /* substring start position */
+   int         sl;
+
+   sl = text_length(PointerGetDatum(t2)); /* defaults to length(t2) */
+   PG_RETURN_TEXT_P(text_overlay(t1, t2, sp, sl));
+}
+
+static text *
+text_overlay(text *t1, text *t2, int sp, int sl)
+{
+   text       *result;
+   text       *s1;
+   text       *s2;
+   int         sp_pl_sl;
+
+   /*
+    * Check for possible integer-overflow cases.  For negative sp,
+    * throw a "substring length" error because that's what should be
+    * expected according to the spec's definition of OVERLAY().
+    */
+   if (sp <= 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_SUBSTRING_ERROR),
+                errmsg("negative substring length not allowed")));
+   sp_pl_sl = sp + sl;
+   if (sp_pl_sl <= sl)
+       ereport(ERROR,
+               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                errmsg("integer out of range")));
+
+   s1 = text_substring(PointerGetDatum(t1), 1, sp-1, false);
+   s2 = text_substring(PointerGetDatum(t1), sp_pl_sl, -1, true);
+   result = text_catenate(s1, t2);
+   result = text_catenate(result, s2);
+
+   return result;
+}
+
 /*
  * textpos -
  *   Return the position of the specified substring.
@@ -1640,17 +1723,31 @@ byteacat(PG_FUNCTION_ARGS)
 {
    bytea      *t1 = PG_GETARG_BYTEA_PP(0);
    bytea      *t2 = PG_GETARG_BYTEA_PP(1);
+
+   PG_RETURN_BYTEA_P(bytea_catenate(t1, t2));
+}
+
+/*
+ * bytea_catenate
+ * Guts of byteacat(), broken out so it can be used by other functions
+ *
+ * Arguments can be in short-header form, but not compressed or out-of-line
+ */
+static bytea *
+bytea_catenate(bytea *t1, bytea *t2)
+{
+   bytea      *result;
    int         len1,
                len2,
                len;
-   bytea      *result;
    char       *ptr;
 
    len1 = VARSIZE_ANY_EXHDR(t1);
+   len2 = VARSIZE_ANY_EXHDR(t2);
+
+   /* paranoia ... probably should throw error instead? */
    if (len1 < 0)
        len1 = 0;
-
-   len2 = VARSIZE_ANY_EXHDR(t2);
    if (len2 < 0)
        len2 = 0;
 
@@ -1667,7 +1764,7 @@ byteacat(PG_FUNCTION_ARGS)
    if (len2 > 0)
        memcpy(ptr + len1, VARDATA_ANY(t2), len2);
 
-   PG_RETURN_BYTEA_P(result);
+   return result;
 }
 
 #define PG_STR_GET_BYTEA(str_) \
@@ -1691,16 +1788,41 @@ byteacat(PG_FUNCTION_ARGS)
 Datum
 bytea_substr(PG_FUNCTION_ARGS)
 {
-   int         S = PG_GETARG_INT32(1); /* start position */
+   PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0),
+                                     PG_GETARG_INT32(1),
+                                     PG_GETARG_INT32(2),
+                                     false));
+}
+
+/*
+ * bytea_substr_no_len -
+ *   Wrapper to avoid opr_sanity failure due to
+ *   one function accepting a different number of args.
+ */
+Datum
+bytea_substr_no_len(PG_FUNCTION_ARGS)
+{
+   PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0),
+                                     PG_GETARG_INT32(1),
+                                     -1,
+                                     true));
+}
+
+static bytea *
+bytea_substring(Datum str,
+               int S,
+               int L,
+               bool length_not_specified)
+{
    int         S1;             /* adjusted start position */
    int         L1;             /* adjusted substring length */
 
    S1 = Max(S, 1);
 
-   if (fcinfo->nargs == 2)
+   if (length_not_specified)
    {
        /*
-        * Not passed a length - PG_GETARG_BYTEA_P_SLICE() grabs everything to
+        * Not passed a length - DatumGetByteaPSlice() grabs everything to
         * the end of the string if we pass it a negative value for length.
         */
        L1 = -1;
@@ -1708,7 +1830,7 @@ bytea_substr(PG_FUNCTION_ARGS)
    else
    {
        /* end position */
-       int         E = S + PG_GETARG_INT32(2);
+       int         E = S + L;
 
        /*
         * A negative value for L is the only way for the end position to be
@@ -1725,28 +1847,78 @@ bytea_substr(PG_FUNCTION_ARGS)
         * string.
         */
        if (E < 1)
-           PG_RETURN_BYTEA_P(PG_STR_GET_BYTEA(""));
+           return PG_STR_GET_BYTEA("");
 
        L1 = E - S1;
    }
 
    /*
     * If the start position is past the end of the string, SQL99 says to
-    * return a zero-length string -- PG_GETARG_TEXT_P_SLICE() will do that
+    * return a zero-length string -- DatumGetByteaPSlice() will do that
     * for us. Convert to zero-based starting position
     */
-   PG_RETURN_BYTEA_P(PG_GETARG_BYTEA_P_SLICE(0, S1 - 1, L1));
+   return DatumGetByteaPSlice(str, S1 - 1, L1);
 }
 
 /*
- * bytea_substr_no_len -
- *   Wrapper to avoid opr_sanity failure due to
- *   one function accepting a different number of args.
+ * byteaoverlay
+ * Replace specified substring of first string with second
+ *
+ * The SQL standard defines OVERLAY() in terms of substring and concatenation.
+ * This code is a direct implementation of what the standard says.
  */
 Datum
-bytea_substr_no_len(PG_FUNCTION_ARGS)
+byteaoverlay(PG_FUNCTION_ARGS)
 {
-   return bytea_substr(fcinfo);
+   bytea      *t1 = PG_GETARG_BYTEA_PP(0);
+   bytea      *t2 = PG_GETARG_BYTEA_PP(1);
+   int         sp = PG_GETARG_INT32(2); /* substring start position */
+   int         sl = PG_GETARG_INT32(3); /* substring length */
+
+   PG_RETURN_BYTEA_P(bytea_overlay(t1, t2, sp, sl));
+}
+
+Datum
+byteaoverlay_no_len(PG_FUNCTION_ARGS)
+{
+   bytea      *t1 = PG_GETARG_BYTEA_PP(0);
+   bytea      *t2 = PG_GETARG_BYTEA_PP(1);
+   int         sp = PG_GETARG_INT32(2); /* substring start position */
+   int         sl;
+
+   sl = VARSIZE_ANY_EXHDR(t2);         /* defaults to length(t2) */
+   PG_RETURN_BYTEA_P(bytea_overlay(t1, t2, sp, sl));
+}
+
+static bytea *
+bytea_overlay(bytea *t1, bytea *t2, int sp, int sl)
+{
+   bytea      *result;
+   bytea      *s1;
+   bytea      *s2;
+   int         sp_pl_sl;
+
+   /*
+    * Check for possible integer-overflow cases.  For negative sp,
+    * throw a "substring length" error because that's what should be
+    * expected according to the spec's definition of OVERLAY().
+    */
+   if (sp <= 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_SUBSTRING_ERROR),
+                errmsg("negative substring length not allowed")));
+   sp_pl_sl = sp + sl;
+   if (sp_pl_sl <= sl)
+       ereport(ERROR,
+               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                errmsg("integer out of range")));
+
+   s1 = bytea_substring(PointerGetDatum(t1), 1, sp-1, false);
+   s2 = bytea_substring(PointerGetDatum(t1), sp_pl_sl, -1, true);
+   result = bytea_catenate(s1, t2);
+   result = bytea_catenate(result, s2);
+
+   return result;
 }
 
 /*
index 33ce64090620a71e99a89342a765dc3929a0b273..4c0fde84e39d839d91330bd7b0c548425dcbaf1d 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.578 2010/01/22 16:42:31 rhaas Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.579 2010/01/25 20:55:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201001222
+#define CATALOG_VERSION_NO 201001251
 
 #endif
index 9b910317a20864f32dc318220acae527dac1b270..f4b816f128b52032c9bc1774c8305a2654865e54 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.564 2010/01/19 14:11:32 mha Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.565 2010/01/25 20:55:32 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.pl reads this file and generates .bki
@@ -957,6 +957,10 @@ DATA(insert OID = 723 (  get_bit          PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "17
 DESCR("get bit");
 DATA(insert OID = 724 (  set_bit          PGNSP PGUID 12 1 0 0 f f f t f i 3 0 17 "17 23 23" _null_ _null_ _null_ _null_   byteaSetBit _null_ _null_ _null_ ));
 DESCR("set bit");
+DATA(insert OID = 749 (  overlay          PGNSP PGUID 12 1 0 0 f f f t f i 4 0 17 "17 17 23 23" _null_ _null_ _null_ _null_    byteaoverlay _null_ _null_ _null_ ));
+DESCR("substitute portion of string");
+DATA(insert OID = 752 (  overlay          PGNSP PGUID 12 1 0 0 f f f t f i 3 0 17 "17 17 23" _null_ _null_ _null_ _null_   byteaoverlay_no_len _null_ _null_ _null_ ));
+DESCR("substitute portion of string");
 
 DATA(insert OID = 725 (  dist_pl          PGNSP PGUID 12 1 0 0 f f f t f i 2 0 701 "600 628" _null_ _null_ _null_ _null_   dist_pl _null_ _null_ _null_ ));
 DESCR("distance between point and line");
@@ -1832,9 +1836,9 @@ DESCR("current schema name");
 DATA(insert OID = 1403 (  current_schemas  PGNSP PGUID 12 1 0 0 f f f t f s 1 0 1003 "16" _null_ _null_ _null_ _null_  current_schemas _null_ _null_ _null_ ));
 DESCR("current schema search list");
 
-DATA(insert OID = 1404 (  overlay          PGNSP PGUID 14 1 0 0 f f f t f i 4 0 25 "25 25 23 23" _null_ _null_ _null_ _null_   "select pg_catalog.substring($1, 1, ($3 - 1)) || $2 || pg_catalog.substring($1, ($3 + $4))" _null_ _null_ _null_ ));
+DATA(insert OID = 1404 (  overlay          PGNSP PGUID 12 1 0 0 f f f t f i 4 0 25 "25 25 23 23" _null_ _null_ _null_ _null_   textoverlay _null_ _null_ _null_ ));
 DESCR("substitute portion of string");
-DATA(insert OID = 1405 (  overlay          PGNSP PGUID 14 1 0 0 f f f t f i 3 0 25 "25 25 23" _null_ _null_ _null_ _null_  "select pg_catalog.substring($1, 1, ($3 - 1)) || $2 || pg_catalog.substring($1, ($3 + pg_catalog.char_length($2)))" _null_ _null_ _null_ ));
+DATA(insert OID = 1405 (  overlay          PGNSP PGUID 12 1 0 0 f f f t f i 3 0 25 "25 25 23" _null_ _null_ _null_ _null_  textoverlay_no_len _null_ _null_ _null_ ));
 DESCR("substitute portion of string");
 
 DATA(insert OID = 1406 (  isvertical       PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "600 600" _null_ _null_ _null_ _null_  point_vert _null_ _null_ _null_ ));
@@ -2402,9 +2406,17 @@ DESCR("adjust varbit() to typmod length");
 
 DATA(insert OID = 1698 (  position        PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "1560 1560" _null_ _null_ _null_ _null_ bitposition _null_ _null_ _null_ ));
 DESCR("return position of sub-bitstring");
-DATA(insert OID = 1699 (  substring            PGNSP PGUID 12 1 0 0 f f f t f i 2 0 1560 "1560 23" _null_ _null_ _null_ _null_ bitsubstr_no_len _null_ _null_ _null_ ));
+DATA(insert OID = 1699 (  substring           PGNSP PGUID 12 1 0 0 f f f t f i 2 0 1560 "1560 23" _null_ _null_ _null_ _null_ bitsubstr_no_len _null_ _null_ _null_ ));
 DESCR("return portion of bitstring");
 
+DATA(insert OID = 3030 (  overlay         PGNSP PGUID 12 1 0 0 f f f t f i 4 0 1560 "1560 1560 23 23" _null_ _null_ _null_ _null_  bitoverlay _null_ _null_ _null_ ));
+DESCR("substitute portion of bitstring");
+DATA(insert OID = 3031 (  overlay         PGNSP PGUID 12 1 0 0 f f f t f i 3 0 1560 "1560 1560 23" _null_ _null_ _null_ _null_ bitoverlay_no_len _null_ _null_ _null_ ));
+DESCR("substitute portion of bitstring");
+DATA(insert OID = 3032 (  get_bit         PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "1560 23" _null_ _null_ _null_ _null_ bitgetbit _null_ _null_ _null_ ));
+DESCR("get bit");
+DATA(insert OID = 3033 (  set_bit         PGNSP PGUID 12 1 0 0 f f f t f i 3 0 1560 "1560 23 23" _null_ _null_ _null_ _null_ bitsetbit _null_ _null_ _null_ ));
+DESCR("set bit");
 
 /* for mac type support */
 DATA(insert OID = 436 (  macaddr_in            PGNSP PGUID 12 1 0 0 f f f t f i 1 0 829 "2275" _null_ _null_ _null_ _null_ macaddr_in _null_ _null_ _null_ ));
index 758aeee79fdac962746be64193605dbc281636d1..11eb8acacc770e23cecf5be591843a8dab2fca9d 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.344 2010/01/19 05:50:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.345 2010/01/25 20:55:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -698,6 +698,8 @@ extern Datum textoctetlen(PG_FUNCTION_ARGS);
 extern Datum textpos(PG_FUNCTION_ARGS);
 extern Datum text_substr(PG_FUNCTION_ARGS);
 extern Datum text_substr_no_len(PG_FUNCTION_ARGS);
+extern Datum textoverlay(PG_FUNCTION_ARGS);
+extern Datum textoverlay_no_len(PG_FUNCTION_ARGS);
 extern Datum name_text(PG_FUNCTION_ARGS);
 extern Datum text_name(PG_FUNCTION_ARGS);
 extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2);
index e88cd0e2679757a14c19b669ee44ec5873f59e05..e36292a7ba2ec47f7592613c1abfde25d0e888a2 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/bytea.h,v 1.2 2010/01/02 16:58:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/bytea.h,v 1.3 2010/01/25 20:55:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,5 +46,7 @@ extern Datum byteacat(PG_FUNCTION_ARGS);
 extern Datum byteapos(PG_FUNCTION_ARGS);
 extern Datum bytea_substr(PG_FUNCTION_ARGS);
 extern Datum bytea_substr_no_len(PG_FUNCTION_ARGS);
+extern Datum byteaoverlay(PG_FUNCTION_ARGS);
+extern Datum byteaoverlay_no_len(PG_FUNCTION_ARGS);
 
 #endif   /* BYTEA_H */
index df51c9b41594ef7d890acbcff1b65b87c01bb7ec..02ed5f795e485b5dc61b2f8e37966fa0825f833c 100644 (file)
@@ -8,7 +8,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/varbit.h,v 1.30 2010/01/07 20:17:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/varbit.h,v 1.31 2010/01/25 20:55:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -89,6 +89,8 @@ extern Datum bitshiftright(PG_FUNCTION_ARGS);
 extern Datum bitcat(PG_FUNCTION_ARGS);
 extern Datum bitsubstr(PG_FUNCTION_ARGS);
 extern Datum bitsubstr_no_len(PG_FUNCTION_ARGS);
+extern Datum bitoverlay(PG_FUNCTION_ARGS);
+extern Datum bitoverlay_no_len(PG_FUNCTION_ARGS);
 extern Datum bitlength(PG_FUNCTION_ARGS);
 extern Datum bitoctetlength(PG_FUNCTION_ARGS);
 extern Datum bitfromint4(PG_FUNCTION_ARGS);
@@ -96,5 +98,7 @@ extern Datum bittoint4(PG_FUNCTION_ARGS);
 extern Datum bitfromint8(PG_FUNCTION_ARGS);
 extern Datum bittoint8(PG_FUNCTION_ARGS);
 extern Datum bitposition(PG_FUNCTION_ARGS);
+extern Datum bitsetbit(PG_FUNCTION_ARGS);
+extern Datum bitgetbit(PG_FUNCTION_ARGS);
 
 #endif
index 3563d2366d14c988dae073a6f2ba0d725011b56b..40082ca14a2f91f1f7753717cab04f108c6b4827 100644 (file)
@@ -509,3 +509,43 @@ SELECT POSITION(B'1101' IN v),
 
 DROP TABLE BIT_SHIFT_TABLE;
 DROP TABLE VARBIT_SHIFT_TABLE;
+-- Get/Set bit
+SELECT get_bit(B'0101011000100', 10);
+ get_bit 
+---------
+       1
+(1 row)
+
+SELECT set_bit(B'0101011000100100', 15, 1);
+     set_bit      
+------------------
+ 0101011000100101
+(1 row)
+
+SELECT set_bit(B'0101011000100100', 16, 1);    -- fail
+ERROR:  bit index 16 out of valid range (0..15)
+-- Overlay
+SELECT overlay(B'0101011100' placing '001' from 2 for 3);
+  overlay   
+------------
+ 0001011100
+(1 row)
+
+SELECT overlay(B'0101011100' placing '101' from 6);
+  overlay   
+------------
+ 0101010100
+(1 row)
+
+SELECT overlay(B'0101011100' placing '001' from 11);
+    overlay    
+---------------
+ 0101011100001
+(1 row)
+
+SELECT overlay(B'0101011100' placing '001' from 20);
+    overlay    
+---------------
+ 0101011100001
+(1 row)
+
index 392f48ef8c661b5a8a8134eb573798af0bea343c..42c34a552282027efaf4453ab9ff740d9d8c29b6 100644 (file)
@@ -1579,3 +1579,21 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea);
  \000trim\000
 (1 row)
 
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape');
+   encode    
+-------------
+ TTh\x01omas
+(1 row)
+
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape');
+       encode       
+--------------------
+ Th\000omas\x02\x03
+(1 row)
+
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape');
+     encode      
+-----------------
+ Th\000o\x02\x03
+(1 row)
+
index f178991d8fc80b864c1c1c4619ccd47424335202..73ddd379c03e3f0362ecbf992e2f3d4b5e099e32 100644 (file)
@@ -184,3 +184,14 @@ SELECT POSITION(B'1101' IN v),
 
 DROP TABLE BIT_SHIFT_TABLE;
 DROP TABLE VARBIT_SHIFT_TABLE;
+
+-- Get/Set bit
+SELECT get_bit(B'0101011000100', 10);
+SELECT set_bit(B'0101011000100100', 15, 1);
+SELECT set_bit(B'0101011000100100', 16, 1);    -- fail
+
+-- Overlay
+SELECT overlay(B'0101011100' placing '001' from 2 for 3);
+SELECT overlay(B'0101011100' placing '101' from 6);
+SELECT overlay(B'0101011100' placing '001' from 11);
+SELECT overlay(B'0101011100' placing '001' from 20);
index 63df9402ed70aae6eecc299727db181d001ba1d0..8c35be81360431689f4a6953d916cf3520bc13b4 100644 (file)
@@ -547,3 +547,6 @@ SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea);
 SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea);
 SELECT btrim(''::bytea, E'\\000'::bytea);
 SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea);
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape');
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape');
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape');