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

erhoui1w  于 2023-01-05  发布在  PostgreSQL
关注(0)|答案(4)|浏览(275)

我有一个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
    上面的序列可以工作,唯一的问题是我希望bytea中的字节序列是 reversed。例如,如果old_column中的值是0x1234567890abcdef,上面的序列将生成\0224Vx\220\253\315\357,但我希望它是\357\315\253\220xV4\022。看起来结果bytea使用了来自原始bigint的big-endian顺序。
    有没有不写程序就能做到这一点的简单方法?我在PostgreSQL中寻找swap64()排序的函数,但没有找到。
mnowg1ta

mnowg1ta1#

使用十六进制表示的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');
gcuhipw9

gcuhipw92#

下面是我编写的一个纯SQL函数,用于反转bytea类型值的字节顺序:

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的字节字符串上是这样。

jtw3ybtb

jtw3ybtb3#

我现在正在使用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;
7uhlpewt

7uhlpewt4#

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;

相关问题