diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 3143767dc5ec1cdcedc998b5d1fd420dcaee66cb..f777f9f16503831831880fe340c27867ea3bf440 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -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> @@ -2620,7 +2620,7 @@ <entry>Return Type</entry> <entry>Description</entry> <entry>Example</entry> - <entry>Result</entry> + <entry>Result</entry> </row> </thead> @@ -2640,32 +2640,6 @@ <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> @@ -2675,39 +2649,21 @@ </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> @@ -2784,7 +2740,7 @@ </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> @@ -2805,6 +2761,36 @@ <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> @@ -2834,6 +2820,38 @@ <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> @@ -2934,7 +2952,15 @@ <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> diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c81e8a38ec9b468161d7acfeeccd69231f2119b0..981d2614042c7cfaf1e63861ff736d7aff535167 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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 diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index 7f39866f00550d447f906dd61d0b4e5a18c83278..4a550cdae2980a3ede53b08eb322b743ee6f1d93 100644 --- a/src/backend/utils/adt/varbit.c +++ b/src/backend/utils/adt/varbit.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -23,8 +23,10 @@ #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); +} diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 53650c88786fdb4a655d3f335ad4067b224236e3..7a8abf14a83d11ae57fe46bcaa419271c6a2ae23 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -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; } /* diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 33ce64090620a71e99a89342a765dc3929a0b273..4c0fde84e39d839d91330bd7b0c548425dcbaf1d 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 9b910317a20864f32dc318220acae527dac1b270..f4b816f128b52032c9bc1774c8305a2654865e54 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 758aeee79fdac962746be64193605dbc281636d1..11eb8acacc770e23cecf5be591843a8dab2fca9d 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -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); diff --git a/src/include/utils/bytea.h b/src/include/utils/bytea.h index e88cd0e2679757a14c19b669ee44ec5873f59e05..e36292a7ba2ec47f7592613c1abfde25d0e888a2 100644 --- a/src/include/utils/bytea.h +++ b/src/include/utils/bytea.h @@ -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 */ diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h index df51c9b41594ef7d890acbcff1b65b87c01bb7ec..02ed5f795e485b5dc61b2f8e37966fa0825f833c 100644 --- a/src/include/utils/varbit.h +++ b/src/include/utils/varbit.h @@ -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 diff --git a/src/test/regress/expected/bit.out b/src/test/regress/expected/bit.out index 3563d2366d14c988dae073a6f2ba0d725011b56b..40082ca14a2f91f1f7753717cab04f108c6b4827 100644 --- a/src/test/regress/expected/bit.out +++ b/src/test/regress/expected/bit.out @@ -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) + diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out index 392f48ef8c661b5a8a8134eb573798af0bea343c..42c34a552282027efaf4453ab9ff740d9d8c29b6 100644 --- a/src/test/regress/expected/strings.out +++ b/src/test/regress/expected/strings.out @@ -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) + diff --git a/src/test/regress/sql/bit.sql b/src/test/regress/sql/bit.sql index f178991d8fc80b864c1c1c4619ccd47424335202..73ddd379c03e3f0362ecbf992e2f3d4b5e099e32 100644 --- a/src/test/regress/sql/bit.sql +++ b/src/test/regress/sql/bit.sql @@ -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); diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql index 63df9402ed70aae6eecc299727db181d001ba1d0..8c35be81360431689f4a6953d916cf3520bc13b4 100644 --- a/src/test/regress/sql/strings.sql +++ b/src/test/regress/sql/strings.sql @@ -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');