Adjust bytea get_bit/set_bit to cope with bytea strings > 256MB.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 7 Apr 2020 20:30:55 +0000 (16:30 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 7 Apr 2020 20:30:55 +0000 (16:30 -0400)
Since the existing bit number argument can't exceed INT32_MAX, it's
not possible for these functions to manipulate bits beyond the first
256MB of a bytea value.  However, it'd be good if they could do at
least that much, and not fall over entirely for longer bytea values.
Adjust the comparisons to be done in int64 arithmetic so that works.
Also tweak the error reports to show sane values in case of overflow.

Also add some test cases to improve the miserable code coverage
of these functions.

Apply patch to back branches only; HEAD has a better solution
as of commit 26a944cf2.

Extracted from a much larger patch by Movead Li

Discussion: https://postgr.es/m/20200312115135445367128@highgo.ca

src/backend/utils/adt/varlena.c
src/test/regress/expected/strings.out
src/test/regress/sql/strings.sql

index eba7cfc7c1b957c107850c57d0f28cf2d8109dbd..df7e7b14c198d04818850c30f4e13239b1dda2ad 100644 (file)
@@ -3055,11 +3055,12 @@ byteaGetBit(PG_FUNCTION_ARGS)
 
    len = VARSIZE_ANY_EXHDR(v);
 
-   if (n < 0 || n >= len * 8)
+   /* Do comparison arithmetic in int64 in case len exceeds INT_MAX/8 */
+   if (n < 0 || n >= (int64) len * 8)
        ereport(ERROR,
                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                 errmsg("index %d out of valid range, 0..%d",
-                       n, len * 8 - 1)));
+                       n, (int) Min((int64) len * 8 - 1, INT_MAX))));
 
    byteNo = n / 8;
    bitNo = n % 8;
@@ -3126,11 +3127,12 @@ byteaSetBit(PG_FUNCTION_ARGS)
 
    len = VARSIZE(res) - VARHDRSZ;
 
-   if (n < 0 || n >= len * 8)
+   /* Do comparison arithmetic in int64 in case len exceeds INT_MAX/8 */
+   if (n < 0 || n >= (int64) len * 8)
        ereport(ERROR,
                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                 errmsg("index %d out of valid range, 0..%d",
-                       n, len * 8 - 1)));
+                       n, (int) Min((int64) len * 8 - 1, INT_MAX))));
 
    byteNo = n / 8;
    bitNo = n % 8;
index 3b1a7d80b8269ae0c6f13e39fcbe6dc3cda2db78..d7fdc29bceff1c6923b4610e073e56517788f321 100644 (file)
@@ -1432,6 +1432,83 @@ select md5('12345678901234567890123456789012345678901234567890123456789012345678
  t
 (1 row)
 
+--
+-- encode/decode
+--
+SET bytea_output TO hex;
+SELECT encode('\x1234567890abcdef00', 'hex');
+       encode       
+--------------------
+ 1234567890abcdef00
+(1 row)
+
+SELECT decode('1234567890abcdef00', 'hex');
+        decode        
+----------------------
+ \x1234567890abcdef00
+(1 row)
+
+SELECT encode(('\x' || repeat('1234567890abcdef0001', 7))::bytea, 'base64');
+                                    encode                                    
+------------------------------------------------------------------------------
+ EjRWeJCrze8AARI0VniQq83vAAESNFZ4kKvN7wABEjRWeJCrze8AARI0VniQq83vAAESNFZ4kKvN+
+ 7wABEjRWeJCrze8AAQ==
+(1 row)
+
+SELECT decode(encode(('\x' || repeat('1234567890abcdef0001', 7))::bytea,
+                     'base64'), 'base64');
+                                                                     decode                                                                     
+------------------------------------------------------------------------------------------------------------------------------------------------
+ \x1234567890abcdef00011234567890abcdef00011234567890abcdef00011234567890abcdef00011234567890abcdef00011234567890abcdef00011234567890abcdef0001
+(1 row)
+
+SELECT encode('\x1234567890abcdef00', 'escape');
+           encode            
+-----------------------------
+ \x124Vx\220\253\315\357\000
+(1 row)
+
+SELECT decode(encode('\x1234567890abcdef00', 'escape'), 'escape');
+        decode        
+----------------------
+ \x1234567890abcdef00
+(1 row)
+
+--
+-- get_bit/set_bit etc
+--
+SELECT get_bit('\x1234567890abcdef00'::bytea, 43);
+ get_bit 
+---------
+       1
+(1 row)
+
+SELECT get_bit('\x1234567890abcdef00'::bytea, 99);  -- error
+ERROR:  index 99 out of valid range, 0..71
+SELECT set_bit('\x1234567890abcdef00'::bytea, 43, 0);
+       set_bit        
+----------------------
+ \x1234567890a3cdef00
+(1 row)
+
+SELECT set_bit('\x1234567890abcdef00'::bytea, 99, 0);  -- error
+ERROR:  index 99 out of valid range, 0..71
+SELECT get_byte('\x1234567890abcdef00'::bytea, 3);
+ get_byte 
+----------
+      120
+(1 row)
+
+SELECT get_byte('\x1234567890abcdef00'::bytea, 99);  -- error
+ERROR:  index 99 out of valid range, 0..8
+SELECT set_byte('\x1234567890abcdef00'::bytea, 7, 11);
+       set_byte       
+----------------------
+ \x1234567890abcd0b00
+(1 row)
+
+SELECT set_byte('\x1234567890abcdef00'::bytea, 99, 11);  -- error
+ERROR:  index 99 out of valid range, 0..8
 --
 -- test behavior of escape_string_warning and standard_conforming_strings options
 --
@@ -1518,6 +1595,7 @@ select 'a\\bcd' as f1, 'a\\b\'cd' as f2, 'a\\b\'''cd' as f3, 'abcd\\'   as f4, '
 --
 -- Additional string functions
 --
+SET bytea_output TO escape;
 SELECT initcap('hi THOMAS');
   initcap  
 -----------
index 5e39458bd2234d5e4b13016115bca137f01831db..2d93d2a36e2826c025499b7bdc7d7e8dedce7f78 100644 (file)
@@ -492,6 +492,31 @@ select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'::byt
 
 select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890'::bytea) = '57edf4a22be3c955ac49da2e2107b67a' AS "TRUE";
 
+--
+-- encode/decode
+--
+SET bytea_output TO hex;
+
+SELECT encode('\x1234567890abcdef00', 'hex');
+SELECT decode('1234567890abcdef00', 'hex');
+SELECT encode(('\x' || repeat('1234567890abcdef0001', 7))::bytea, 'base64');
+SELECT decode(encode(('\x' || repeat('1234567890abcdef0001', 7))::bytea,
+                     'base64'), 'base64');
+SELECT encode('\x1234567890abcdef00', 'escape');
+SELECT decode(encode('\x1234567890abcdef00', 'escape'), 'escape');
+
+--
+-- get_bit/set_bit etc
+--
+SELECT get_bit('\x1234567890abcdef00'::bytea, 43);
+SELECT get_bit('\x1234567890abcdef00'::bytea, 99);  -- error
+SELECT set_bit('\x1234567890abcdef00'::bytea, 43, 0);
+SELECT set_bit('\x1234567890abcdef00'::bytea, 99, 0);  -- error
+SELECT get_byte('\x1234567890abcdef00'::bytea, 3);
+SELECT get_byte('\x1234567890abcdef00'::bytea, 99);  -- error
+SELECT set_byte('\x1234567890abcdef00'::bytea, 7, 11);
+SELECT set_byte('\x1234567890abcdef00'::bytea, 99, 11);  -- error
+
 --
 -- test behavior of escape_string_warning and standard_conforming_strings options
 --
@@ -526,6 +551,7 @@ select 'a\\bcd' as f1, 'a\\b\'cd' as f2, 'a\\b\'''cd' as f3, 'abcd\\'   as f4, '
 --
 -- Additional string functions
 --
+SET bytea_output TO escape;
 
 SELECT initcap('hi THOMAS');