Jak wstawić (plik) dane do kolumny bajtów PostgreSQL?

37

To pytanie nie dotyczy bytea v. Oid v. Blob v. Dużych obiektów itp.

Mam tabelę zawierającą integerpole klucza podstawowego i byteapole. Chciałbym wprowadzić dane w byteapole. Można to przypuszczalnie zrobić w jednym z PL/języków i mogę się tym zająć PL/Pythonw przyszłości.

Ponieważ wciąż testuję i eksperymentuję, chciałbym po prostu wstawić dane z pliku (na serwerze) przy użyciu „standardowych” instrukcji SQL. Wiem, że tylko administratorzy z uprawnieniami do zapisu na serwerze mogliby wstawiać dane w sposób, w jaki chciałbym. Nie martwię się tym na tym etapie, ponieważ użytkownicy nie będą obecnie wstawiać byteadanych. Przeszukałem różne strony StackExchange, archiwa PostgreSQL i ogólnie Internet, ale nie byłem w stanie znaleźć odpowiedzi.

Edycja: Ta dyskusja z 2008 roku sugeruje, że to, co chcę zrobić, nie jest możliwe. Jak zatem byteawykorzystywane są pola?

Edycja: To podobne pytanie z 2005 roku pozostaje bez odpowiedzi.

Rozwiązane: szczegóły podane tutaj na psycopgstronie internetowej stanowiły podstawę rozwiązania, które napisałem w języku Python. Może być również możliwe wstawienie danych binarnych do byteakolumny za pomocą PL/Python. Nie wiem, czy jest to możliwe przy użyciu „czystego” SQL.

SabreWolfy
źródło
1
Link do dokumentów psycopg jest zepsuty i moja edycja wydaje się być odrzucona (!?). Oto aktualna lokalizacja .
Aryeh Leib Taurog
@AryehLeibTaurog: Dzięki. Odrzuciłem zmianę, ponieważ nie było dla mnie jasne, że zmieniony tekst był hiperłączem. Jeśli chcesz ponownie dokonać edycji, zatwierdzę ją.
SabreWolfy
@Andriy_M Dlaczego uważasz, że „Ta zmiana odbiega od pierwotnych założeń posta”. (Edycja wykonana przez informatik01?)
miracle173
@ miracle173: Ponieważ mam wrażenie, że niektóre z sugerowanych tagów były nieistotne (właściwie tylko jeden blob). Jeśli to był błąd, szczerze przepraszam.
Andriy M,

Odpowiedzi:

26

jako administrator:

create or replace function bytea_import(p_path text, p_result out bytea) 
                   language plpgsql as $$
declare
  l_oid oid;
begin
  select lo_import(p_path) into l_oid;
  select lo_get(l_oid) INTO p_result;
  perform lo_unlink(l_oid);
end;$$;

lo_get został wprowadzony w wersji 9.4, więc dla starszych wersji potrzebujesz:

create or replace function bytea_import(p_path text, p_result out bytea) 
                   language plpgsql as $$
declare
  l_oid oid;
  r record;
begin
  p_result := '';
  select lo_import(p_path) into l_oid;
  for r in ( select data 
             from pg_largeobject 
             where loid = l_oid 
             order by pageno ) loop
    p_result = p_result || r.data;
  end loop;
  perform lo_unlink(l_oid);
end;$$;

następnie:

insert into my_table(bytea_data) select bytea_import('/my/file.name');
Jack Douglas
źródło
W procesie odwrotnym, nie próbowałem tego , ale jeśli działa, lo_export będzie wszystko, czego potrzeba
Jack Douglas
17

Zastosowanie pg_read_file('location_of file')::bytea.

Na przykład,

create table test(id int, image bytea);
insert into test values (1, pg_read_file('/home/xyz')::bytea);

podręcznik

sudalai
źródło
2
Lub nieco prostsze pg_read_binary_file('/path/to/file'). Zobacz postgresql.org/docs/current/static/functions-admin.html
Arto Bendiken
15

To rozwiązanie nie jest dokładnie wydajne pod względem czasu działania, ale jest banalnie łatwe w porównaniu do tworzenia własnych nagłówków COPY BINARY. Co więcej, nie wymaga żadnych bibliotek ani skryptów poza bash.

Najpierw przekonwertuj plik na zrzut heksowy, podwajając rozmiar pliku. xxd -pprzybliża nas do siebie, ale wprowadza denerwujące nowości, którymi musimy się zająć:

xxd -p /path/file.bin | tr -d '\n' > /path/file.hex

Następnie zaimportuj dane do PostgreSQL jako bardzo duże textpole. Ten typ mieści do jednego GB na wartość pola, więc w większości przypadków powinniśmy być w porządku:

CREATE TABLE hexdump (hex text); COPY hexdump FROM '/path/file.hex';

Teraz, gdy nasze dane są nieskończenie dużym ciągiem szesnastkowym, używamy PostgresQL-a, decodeaby uzyskać byteatyp:

CREATE TABLE bindump AS SELECT decode(hex, 'hex') FROM hexdump;
dobra strona
źródło
To rozwiązanie powoduje jednak usunięcie \ n znaków z pliku.
SabreWolfy
2
SabreWolfy: Nie, nie ma. tr -d '\n'Działa na wyjściu xxd, który koduje binarne treść wejścia jako znaki ASCII w kodzie szesnastkowym (0-9 i AF). xxd zdarza się również w liniach wyjściowych w regularnych odstępach czasu, aby dane wyjściowe były czytelne dla człowieka, ale w tym przypadku chcemy, aby zostały usunięte. Informacje o liniach w oryginalnych danych będą w postaci szesnastkowej i pozostaną nienaruszone.
goodside
5

Odpowiedź z xxd jest miła, a dla małych plików, bardzo szybko. Poniżej znajduje się przykładowy skrypt, którego używam.

xxd  -p /home/user/myimage.png | tr -d '\n' > /tmp/image.hex
echo "
    -- CREATE TABLE hexdump (hex text);
    DELETE FROM hexdump;
    COPY hexdump FROM '/tmp/image.hex';

    -- CREATE TABLE bindump (binarydump bytea);
    DELETE FROM bindump;

    INSERT INTO bindump (binarydump)  
    (SELECT decode(hex, 'hex') FROM hexdump limit 1);

    UPDATE users 
    SET image= 
    (
        SELECT decode(hex, 'hex') 
        FROM hexdump LIMIT 1
    )  
    WHERE id=15489 ;
    " | psql mydatabase
Społeczność
źródło
1

Użyj funkcji Postgres COPY BINARY . Jest to zasadniczo odpowiednik zewnętrznych tabel Oracle .

Gajusz
źródło
Dzięki. Podany link wskazuje, że dane muszą być w formacie tabeli binarnej ASCII lub PostgreSQL. W dalszej części strony wspomina się, że format tabeli binarnej jest najpierw tworzony za pomocą polecenia KOPIUJ DO. Czy którekolwiek z tych podejść pozwoliłoby mi wstawić plik binarny (PDF, dokument, arkusz kalkulacyjny) do byteakolumny?
SabreWolfy
Dokumentacja PostgreSQL na temat COPY BINARY ( postgresql.org/docs/8.4/interactive/sql-copy.html ) wskazuje, że podczas wstawiania danych binarnych wymagany jest specjalny nagłówek pliku. Czy muszę zbudować ten nagłówek i dołączyć go do danych binarnych? Wydaje się to dość skomplikowane w przypadku zwykłego przechowywania ciągu danych binarnych.
SabreWolfy
Hmm, skoro już o tym wspomniałeś, nie jestem pewien, po prostu przypomniałem sobie polecenie i założyłem, że tak się stanie. Być może PL / cokolwiek to jedyny sposób, aby to zrobić.
Gajusz