我有一个数据库,其中的一个表存储bytea从另一个系统收集的各种通用数据的Blob()。该bytea字段可以包含任何内容。为了知道如何解释数据,该表还具有一个格式字段。我写了一个Java应用程序读取bytea从数据库的字段byte[],然后我可以很容易地将其转换为double[]或int[]或任何格式字段表示,通过ByteBuffer与各种视图(DoubleBuffer,IntBuffer,等)。
bytea
byte[]
double[]
int[]
ByteBuffer
DoubleBuffer
IntBuffer
现在,我需要在触发器函数中对数据库本身上的数据进行一些操作,以保持与另一个表的完整性。我可以找到几乎可以想象到的任何数据类型的转换,但是我找不到从bytea(甚至bit到)往返的任何数据double precision。bytea可以将A分解,转换为位,然后转换为intor bigint,但不能转换为a double precision。例如,x'deadbeefdeadbeef'::bit(64)::bigint将毫无问题地转换为-2401053088876216593,而是x'deadbeefdeadbeef'::bit(64)::double precision失败,并显示“错误:无法将类型位转换为双精度”,而不给出IEEE 754的答案-1.1885959257070704E148。
bit
double precision
int
bigint
x'deadbeefdeadbeef'::bit(64)::bigint
-2401053088876216593
x'deadbeefdeadbeef'::bit(64)::double precision
-1.1885959257070704E148
我找到了这个答案它基本上实现了IEEE标准,可以将位转换为两倍,但是PostgreSQL中确实没有基本的转换功能吗?另外,当我处理完数据并需要更新表时,我也需要倒退double precision,bytea直到这个答案没有提供。
最好的解决方案是使用SQL命令使用IEEE754-1985标准将其转换为字节。
首先,需要检查IEEE754-1985标准定义的特殊情况。然后,如果不是特殊情况,只需遵循标准算法进行转换。示例代码如下。
bytea_value bytea, is_little_endian boolean然后将输入分为4个字节,如下所示:
bytea_value bytea, is_little_endian boolean
byte_array[0]:= get_byte(bytea_value, 0); byte_array[1]:= get_byte(bytea_value, 1); byte_array[2]:= get_byte(bytea_value, 2); byte_array[3]:= get_byte(bytea_value, 3);
然后通过考虑小端或大端获得二进制值
IF is_little_endian THEN binary_value:= byte_array[0]::bit(8) || byte_array[1]::bit(8) || byte_array[2]::bit(8) || byte_array[3]::bit(8); ELSE binary_value:= byte_array[3]::bit(8) || byte_array[2]::bit(8) || byte_array[1]::bit(8) || byte_array[0]::bit(8); END IF; 现在检查特殊情况:
IF binary_value = '00000000000000000000000000000000' OR binary_value = '10000000000000000000000000000000' THEN -- IEEE754-1985 Zero return 0.0; END IF; sign := substring(binary_value from 1 for 1); exponent := substring(binary_value from 2 for 8); mantissa := substring(binary_value from 10 for 23); IF exponent = '11111111' THEN IF mantissa = '00000000000000000000000' THEN -- IEEE754-1985 negative and positive infinity IF sign = '1' THEN return '-Infinity'; ELSE return 'Infinity'; END IF; ELSE return 'NaN'; -- IEEE754-1985 Not a number END IF; END IF;
如果它不属于任何特殊情况,则将其转换如下:
exp := exponent::int; IF exp > 126 THEN exp := exp - 127; ELSE exp:= -exp; END IF; WHILE mantissa_index < 24 LOOP IF substring(mantissa from mantissa_index for 1) = '1' THEN result := result + power(2, -(mantissa_index)); END IF; mantissa_index = mantissa_index + 1; END LOOP; result := result * power(2, exp); IF(sign = '1') THEN result = -result; END IF; return result;
有任何想法吗?
好的,我找到了答案。在PostgreSQL中,您可以使用Python编写函数。为了启用Python,您必须安装PostgreSQL安装所需的特定版本的Python,并将其在PATH环境变量中可用。您可以通过查看安装说明找到PostgreSQL安装所需的Python版本。我目前在Windows上使用PostgreSQL 9.6.5,它要求使用Python 3.3。我最初尝试了最新的Python 3.6,但无法正常工作。我选择了适用于Windows的最新Python 3.3,即3.3.5。
安装Python之后,您可以CREATE EXTENSION plpython3u;按照https://www.postgresql.org/docs/current/static/plpython.html所述在数据库上执行,从而在PostgreSQL中启用它。从那里,您可以使用Python主体编写任何函数。
CREATE EXTENSION plpython3u;
对于我的具体情况,从转换bytea到double precision[]背,我写了下面的功能:
double precision[]
CREATE FUNCTION bytea_to_double_array(b bytea) RETURNS double precision[] LANGUAGE 'plpython3u' AS $BODY$ if 'struct' in GD: struct = GD['struct'] else: import struct GD['struct'] = struct return struct.unpack('<' + str(int(len(b) / 8)) + 'd', b) $BODY$; CREATE FUNCTION double_array_to_bytea(dblarray double precision[]) RETURNS bytea LANGUAGE 'plpython3u' AS $BODY$ if 'struct' in GD: struct = GD['struct'] else: import struct GD['struct'] = struct # dblarray here is really a list. # PostgreSQL passes SQL arrays as Python lists return struct.pack('<' + str(int(len(dblarray))) + 'd', *dblarray) $BODY$;
在我的情况下,所有双打都存储在little endian中,因此我使用<。我还struct按照的描述,将模块的导入缓存在全局字典中。我使用GD而不是SD,因为我希望导入可以在我可能编写的其他函数中使用。有关GD和SD的信息,请参见https://www.postgresql.org/docs/current/static/plpython- sharing.html。
<
struct
要了解它的作用,请知道我数据库中的blob以小字节序存储,
SELECT bytea_to_double_array(decode('efbeaddeefbeadde', 'hex')), encode(double_array_to_bytea(array[-1.1885959257070704E148]), 'hex');
我得到的答案是
bytea_to_double_array | encode double precision[] | text -------------------------+------------------ {-1.18859592570707e+148} | efbeaddeefbeadde
这里'efbeaddeefbeadde'是'deadbeefdeadbeef'在小尾数。
'efbeaddeefbeadde'
'deadbeefdeadbeef'