Преобразование bytea в двойную точность в PostgreSQL

у меня есть база данных, где в одной из таблиц хранится blob (bytea) всех видов общих данных, собранных из другой системы. The bytea в поле может быть что угодно. Чтобы знать, как интерпретировать данные, таблица также имеет поле формата. Я написал Java-приложение для чтения bytea поле из базы данных byte[] и тогда я могу легко преобразовать его в double[] или int[] или все, что говорит поле формата, используя ByteBuffer и различные виды (DoubleBuffer, IntBuffer, etc.).

теперь у меня есть ситуация, когда мне нужно сделать некоторые манипуляции с данными в самой базе данных в рамках триггерной функции, чтобы поддерживать целостность с другой таблицей. Я могу найти преобразования практически для любого типа данных, но я не могу найти ничего для перехода от bytea (или даже bit) к double precision и обратно. А bytea можно разбить, преобразовать в биты, а затем преобразовать в int или bigint, а не double precision. Например, x'deadbeefdeadbeef'::bit(64)::bigint преобразует в -2401053088876216593 без проблем, но x'deadbeefdeadbeef'::bit(64)::double precision терпит неудачу с "ошибкой: не может привести бит типа к двойной точности" вместо того, чтобы дать ответ IEEE 754 -1.1885959257070704E148.

я нашел этот ответ https://stackoverflow.com/a/11661849/5274457, который в основном реализует стандарт IEEE для преобразования битов в double, но действительно ли нет базовой функции преобразования в PostgreSQL для этого? Кроме того, мне нужно вернуться назад от double precision to bytea когда я закончу манипулировать данными и нужно обновить таблицы, которые этот ответ не предоставляет.

какие идеи?

1 ответов


Ок, я нашел ответ. В PostgreSQL вы можете писать функции с помощью Python. Чтобы включить использование Python, вы должны установить конкретную версию Python, необходимую для установки PostgreSQL, и иметь ее доступной в переменной среды PATH. Вы можете найти, какая версия Python требуется для установки PostgreSQL, просмотрев примечания по установке. В настоящее время я использую PostgreSQL 9.6.5 в Windows, и он вызывает Python 3.3. Сначала я попробовал последнюю версию Python 3.6, но это не сработает. Я остановился на последнем Python 3.3 для Windows, который является 3.3.5.

после установки Python вы включаете его в PostgreSQL, выполнив CREATE EXTENSION plpython3u; в вашей базе данных, как описано здесь https://www.postgresql.org/docs/current/static/plpython.html. Оттуда вы можете написать любую функцию с телами Python.

для моего конкретного случая для преобразования из bytea до 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 модуль в глобальном словаре, как описано в https://stackoverflow.com/a/15025425/5274457. Я использовал GD вместо SD, потому что я хочу, чтобы импорт был доступен в других функциях, которые я могу написать. Сведения о GD и SD см. В разделе https://www.postgresql.org/docs/current/static/plpython-sharing.html.

чтобы увидеть его в действии, зная, что капли в моей базе данных хранятся как мало endian,

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' в прямом порядке байтов.