2017-09-21 95 views
6

Mam bazę danych, w której jedna z tabel przechowuje blob (bytea) wszystkich rodzajów danych ogólnych zebranych z innego systemu. Pole bytea może zawierać dowolne elementy. Aby wiedzieć, jak interpretować dane, tabela ma również pole formatu. Napisałem aplikację Java do zapoznania się z boiska bytea z bazy danych jako byte[] a następnie można łatwo przekształcić go double[] lub int[] lub cokolwiek pole formatu mówi używając ByteBuffer i różne widoki (DoubleBuffer, IntBuffer, etc.).Konwertuj bajt na podwójną precyzję w PostgreSQL

Teraz mam sytuację, w której muszę wykonać pewne manipulowanie danymi z samej bazy danych w ramach funkcji wyzwalacza, aby zachować integralność z inną tabelą. Mogę znaleźć konwersje dla dowolnego rodzaju danych, jakie można sobie wyobrazić, ale nie mogę znaleźć niczego, aby przejść od bytea (lub nawet bit) do double precision iz powrotem. Plik bytea można podzielić, przekonwertować na bity, a następnie przekonwertować na int lub bigint, ale nie na double precision. Na przykład x'deadbeefdeadbeef'::bit(64)::bigint zostanie przekonwertowany na -2401053088876216593 bez żadnych problemów, ale x'deadbeefdeadbeef'::bit(64)::double precision zakończy się niepowodzeniem z "BŁĄD: nie można przesłać bitów typu do podwójnej precyzji" zamiast podać odpowiedź IEEE 754 na -1.1885959257070704E148.

Znalazłem tę odpowiedź https://stackoverflow.com/a/11661849/5274457, która zasadniczo implementuje standard IEEE, aby konwertować bity na podwójne, ale czy naprawdę nie jest to podstawowa funkcja konwersji w PostgreSQL, aby to zrobić? Dodatkowo, muszę cofnąć się również od double precision do bytea po zakończeniu manipulowania danymi i konieczności aktualizacji tabel, których ta odpowiedź nie zapewnia.

Wszelkie pomysły?

+0

Prawdopodobnie będziesz potrzebować prostego rozszerzenia C, aby dodać wymaganą obsadę. Pg mógłby naprawdę użyć więcej funkcji konwersji do/z surowych postaci binarnych. –

+0

Zajrzę do pomysłu na rozszerzenie. Z tego co wiem, nie jest to tylko problem PostgreSQL. HSQL i SQLServer, z tego co wiem, mają ten sam problem. Każdy język programowania, który stosowałem, ma metody konwersji surowych bajtów na podwójne, ale bazy danych SQL nie mają tego. – Keith

+0

Generalnie działają na wyższym poziomie abstrakcji. W PostgreSQL, jedną z praktycznych opcji jest prawdopodobnie użycie prostej procedury plperyth lub plpythonu, aby to zrobić, w ten sposób nie musisz pisać rozszerzenia C. –

Odpowiedz

1

Ok, znalazłem odpowiedź. W PostgreSQL możesz pisać funkcje używając Pythona. Aby umożliwić korzystanie z Pythona, musisz zainstalować konkretną wersję Pythona potrzebną podczas instalacji PostgreSQL i udostępnić ją w zmiennej środowiskowej PATH. Możesz sprawdzić, która wersja Pythona jest twoją instalacją potrzeb PostgreSQL, patrząc na notatki instalacyjne. Obecnie używam PostgreSQL 9.6.5 na Windowsie i wołam o Python 3.3. Najpierw wypróbowałem najnowszy Python 3.6, ale to nie zadziałało. Rozwijałem się z najnowszym Pythonem 3.3 dla Windows, który jest 3.3.5.

Po zainstalowaniu Pythona, włączasz go w PostgreSQL, wykonując CREATE EXTENSION plpython3u; w bazie danych, jak to udokumentowano tutaj https://www.postgresql.org/docs/current/static/plpython.html. Stamtąd możesz napisać dowolną funkcję z ciałami Pythona.

Na mój konkretny przypadek przekonwertować z bytea do double precision[] iz powrotem, napisałem następujące funkcje:

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$; 

W moim przypadku, wszystkie pokoje dwuosobowe są przechowywane w little endian, więc używam <. Dodatkowo buforuję import modułu struct w globalnym słowniku, jak opisano w https://stackoverflow.com/a/15025425/5274457. Użyłem GD zamiast SD, ponieważ chcę, aby import był dostępny w innych funkcjach, które mogę napisać. Aby uzyskać informacje na temat GD i SD, zobacz https://www.postgresql.org/docs/current/static/plpython-sharing.html.

Aby zobaczyć go w akcji znając plamy w mojej bazy danych są przechowywane jako little endian,

SELECT bytea_to_double_array(decode('efbeaddeefbeadde', 'hex')), encode(double_array_to_bytea(array[-1.1885959257070704E148]), 'hex'); 

A odpowiedź mogę to

bytea_to_double_array | encode 
double precision[]  | text 
-------------------------+------------------ 
{-1.18859592570707e+148} | efbeaddeefbeadde 

gdzie 'efbeaddeefbeadde' jest 'deadbeefdeadbeef' w little endian.

+0

Dobra robota, dzięki :) –