Convert bigint to bytea, but reverse byte order

I have a PostgreSQL table in which I want to change the column from bigintto byteabyte to store more data. I think using the following sequence:

  • alter table mytable add new_column
  • update mytable set new_column = int8send(old_column)
  • alter table drop old_column
  • alter table rename new_column to old_column

The above sequence works, the only problem is that I want the byte sequence in to byteabe canceled. For example, if the value is old_columnequal 0x1234567890abcdef, the above sequence generates \0224Vx\220\253\315\357, but I want it to be \357\315\253\220xV4\022. It seems that the resulting one byteauses a large sequence number from the source bigint.

Is there an easy way to do this without writing a program? I searched for a function swap64()in PostgreSQL, but could not find it.

+5
source share
4 answers

You can swap bytes without the plpgsql code using regexp selections in hexadecimal representation. Here is an example to change the bigint constant, assuming SET standard_conforming_strings to ON(by default with PG 9.1)

select regexp_replace( lpad(to_hex(x'123456789abcd'::bigint),16,'0'),
 '(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
 '\8\7\6\5\4\3\2\1');

He returns cdab896745230100. Then apply decode(value, 'hex')to convert it to bytea.

All type conversion can be performed in a single SQL statement:

ALTER TABLE mytable ALTER COLUMN old_column TYPE bytea
  USING decode(
    regexp_replace( lpad(to_hex(old_column), 16,'0'),
 '(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
 '\8\7\6\5\4\3\2\1')
  , 'hex');
+3
source

Here's the pure-SQL function that I wrote to reverse the byte order of the bytea-type value :

CREATE OR REPLACE FUNCTION reverse_bytes_iter(bytes bytea, length int, midpoint int, index int)
RETURNS bytea AS
$$
  SELECT CASE WHEN index >= midpoint THEN bytes ELSE
    reverse_bytes_iter(
      set_byte(
        set_byte(bytes, index, get_byte(bytes, length-index)),
        length-index, get_byte(bytes, index)
      ),
      length, midpoint, index + 1
    )
  END;
$$ LANGUAGE SQL IMMUTABLE;

CREATE OR REPLACE FUNCTION reverse_bytes(bytes bytea) RETURNS bytea AS
'SELECT reverse_bytes_iter(bytes, octet_length(bytes)-1, octet_length(bytes)/2, 0)'
LANGUAGE SQL IMMUTABLE;

I just wrote it yesterday, so it is not particularly well tested and not optimized, but it seems to work at least on byte lines up to 1 k long.

+1
source

, , , .

.

CREATE OR REPLACE FUNCTION utils.int_littleendian(v_number integer) 
  RETURNS bytea AS 
$BODY$ 
DECLARE 
        v_textresult bytea; 
        v_temp int; 
        v_int int; 
        v_i int = 0; 
BEGIN 
        v_int = v_number; 
        v_textresult = '1234'; 
        WHILE(v_i < 4) LOOP 
                raise notice 'loop %',v_int; 
                v_temp := v_int%256; 
                v_int := v_int - v_temp; 
                v_int := v_int / 256; 
                SELECT set_byte(v_textresult,v_i,v_temp) INTO v_textresult; 
                v_i := v_i + 1; 
        END LOOP; 
        return v_textresult; 
END; 

$BODY$ 
  LANGUAGE 'plpgsql' VOLATILE 
  COST 100; 
0

I am playing with pageinspectmodule now , and I was also curious how to change the byte order of an existing byte value, which pretty much matches your case.

I came up with the following function:

CREATE OR REPLACE FUNCTION reverse(bytea) RETURNS bytea AS $reverse$
    SELECT string_agg(byte,''::bytea)
      FROM (
        SELECT substr($1,i,1) byte
          FROM generate_series(length($1),1,-1) i) s
$reverse$ LANGUAGE sql;

It is quite simple and works similarly to text reverse():

WITH v(val) AS (
    VALUES ('\xaabbccdd'::bytea),('\x0123456789abcd'::bytea)
)
SELECT val, reverse(val)
  FROM v;
0
source

All Articles