7

我有一个 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反转。例如,如果一个值old_column0x1234567890abcdef,上面的序列会生成\0224Vx\220\253\315\357,但我希望它是 \357\315\253\220xV4\022。似乎结果bytea使用来自 originating 的大端顺序bigint

有没有一种简单的方法可以在不编写程序的情况下做到这一点?我swap64()在 PostgreSQL 中寻找一种函数,但没有找到。

4

4 回答 4

3

可以在没有 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')将其转换为字节。

整个类型转换实际上可以在一条 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');
于 2012-06-22T00:24:31.923 回答
3

bytea这是我编写的一个纯 SQL 函数,用于反转-type 值的字节顺序:

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

于 2014-08-05T11:08:57.447 回答
1

我现在正在玩pageinspect模块,我也很想知道如何更改现有 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;
于 2014-10-21T19:24:24.630 回答
0

此功能虽然不完全是您想要的,但应该可以帮助您上路。

该函数的源代码在下面逐字复制。

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; 
于 2012-06-21T18:32:51.523 回答