将bigint转换为bytea,但交换字节顺序

时间:2012-06-21 16:06:16

标签: postgresql endianness bytea

我有一个PostgreSQL表,我希望将列从bigint更改为bytea字节以保存更多数据。我在考虑使用以下序列:

  1. alter table mytable add new_column
  2. update mytable set new_column = int8send(old_column)
  3. alter table drop old_column
  4. alter table rename new_column to old_column
  5. 以上序列有效,唯一的问题是我希望bytea中的字节序列反转。例如,如果old_column中的值为0x1234567890abcdef,则上述序列会生成\0224Vx\220\253\315\357,但我希望它是 \357\315\253\220xV4\022。似乎结果bytea使用来自原始bigint的big-endian订单。

    没有编写程序,有没有简单的方法可以做到这一点?我在PostgreSQL中寻找swap64()类函数但未能找到它。

4 个答案:

答案 0 :(得分:3)

在十六进制表示中使用regexp提取可以在没有plpgsql代码的情况下进行字节交换。 下面是交换bigint常量的示例,假设为SET standard_conforming_strings to ON(默认为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');

返回cdab896745230100。然后应用decode(value, 'hex')将其转换为bytea。

整个类型转换实际上可以在一个SQL语句中完成:

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');

答案 1 :(得分:2)

这是我编写的用于反转bytea类型值的字节顺序的纯SQL函数:

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;

我昨天刚刚写了它,所以它没有经过特别好的测试和优化,但它似乎有效,至少在长度达1k的字节串上。

答案 2 :(得分:0)

This function,虽然不完全符合您的要求,但应该可以帮助您顺利前进。

该功能的源代码将在下面逐字复制。

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; 

答案 3 :(得分:0)

我现在正在玩pageinspect module,我也很好奇如何更改现有bytea值的字节顺序,这非常符合你的情况。

我提出了以下功能:

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;

它很简单,与文字reverse()功能类似:

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