Wie füge ich (Datei-) Daten in eine PostgreSQL-Bytea-Spalte ein?

37

Bei dieser Frage geht es nicht um Bytea vs. Oid vs. Blobs vs. große Objekte usw.

Ich habe eine Tabelle, die ein Primärschlüsselfeld integerund ein byteaFeld enthält. Ich möchte Daten in das byteaFeld eingeben . Dies kann vermutlich in einer der PL/Sprachen geschehen , und ich werde dies möglicherweise in PL/PythonZukunft prüfen.

Da ich noch teste und experimentiere, möchte ich einfach Daten aus einer Datei (auf dem Server) mithilfe von "Standard" -SQL-Anweisungen einfügen. Mir ist bekannt, dass nur Administratoren mit Schreibberechtigung auf dem Server Daten in der von mir gewünschten Weise einfügen können. Ich mache mir diesbezüglich keine Sorgen, da Benutzer byteaderzeit keine Daten eingeben würden . Ich habe die verschiedenen StackExchange-Sites, die PostgreSQL-Archive und das Internet allgemein durchsucht, konnte jedoch keine Antwort finden.

Bearbeiten: Diese Diskussion von 2008 impliziert, dass das, was ich tun möchte, nicht möglich ist. Wie werden byteaFelder dann verwendet?

Edit: Diese ähnliche Frage aus dem Jahr 2005 bleibt unbeantwortet.

Gelöst: Die hier auf der psycopgWebsite angegebenen Details bildeten die Grundlage für eine Lösung, die ich in Python geschrieben habe. Es kann auch möglich sein, Binärdaten mit in eine byteaSpalte einzufügen PL/Python. Ich weiß nicht, ob dies mit "reinem" SQL möglich ist.

SabreWolfy
quelle
1
Der Link zu den Psycopg-Dokumenten ist unterbrochen und meine Bearbeitung wurde anscheinend abgelehnt (!?). Hier ist der aktuelle Standort .
Aryeh Leib Taurog
@AryehLeibTaurog: Danke. Ich habe die Bearbeitung abgelehnt, weil mir nicht klar war, dass es sich bei Ihrem geänderten Text um einen Hyperlink handelt. Wenn Sie die Bearbeitung erneut vornehmen möchten, werde ich sie genehmigen.
SabreWolfy
@Andriy_M Warum denkst du, dass "diese Bearbeitung von der ursprünglichen Absicht des Beitrags abweicht." (Die Bearbeitung erfolgt durch informatik01?)
miracle173
@ miracle173: Weil ich den Eindruck hatte, dass einige der vorgeschlagenen Tags irrelevant waren (naja, eigentlich nur eines blob). Wenn das ein Fehler war, entschuldige ich mich aufrichtig.
Andriy M

Antworten:

26

als Superuser:

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 wurde in 9.4 eingeführt, daher benötigen Sie für ältere Versionen:

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

dann:

insert into my_table(bytea_data) select bytea_import('/my/file.name');
Jack Douglas
quelle
Für den umgekehrten Prozess, haben versucht , ich nicht dieses , aber wenn es funktioniert, lo_export werden alle , die Sie benötigen
Jack Douglas
17

Verwenden Sie pg_read_file('location_of file')::bytea.

Beispielsweise,

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

Handbuch

Sudalai
quelle
2
Oder ein bisschen einfacher pg_read_binary_file('/path/to/file'). Siehe postgresql.org/docs/current/static/functions-admin.html
Arto Bendiken,
15

Diese Lösung ist in Bezug auf die Laufzeit nicht gerade effizient, aber im Vergleich zur Erstellung eigener Header für trivial einfach COPY BINARY. Außerdem werden keine Bibliotheken oder Skriptsprachen außerhalb von Bash benötigt.

Konvertieren Sie zunächst die Datei in einen Hexdump, wobei Sie die Größe der Datei verdoppeln. xxd -pbringt uns ziemlich nahe, aber es wirft einige nervige Zeilenumbrüche auf, um die wir uns kümmern müssen:

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

Importieren Sie anschließend die Daten in PostgreSQL als sehr großes textFeld. Dieser Typ kann bis zu 1 GB pro Feldwert enthalten, daher sollten wir für die meisten Zwecke in Ordnung sein:

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

Da es sich bei unseren Daten um eine unnötig große hexadezimale Zeichenfolge handelt, verwenden wir PostgresQLs decode, um sie in einen byteaTyp umzuwandeln :

CREATE TABLE bindump AS SELECT decode(hex, 'hex') FROM hexdump;
Gute Seite
quelle
Diese Lösung führt jedoch dazu, dass \ n Zeichen aus der Datei entfernt werden.
SabreWolfy
2
SabreWolfy: Nein, das tut es nicht. Das tr -d '\n'arbeitet mit der Ausgabe von xxd, die den binären Inhalt der Eingabe als ASCII-Hexadezimalzeichen (0-9 und af) codiert. xxd gibt auch Zeilenvorschübe in regelmäßigen Abständen aus, um die Ausgabe für den Menschen lesbar zu machen. In diesem Fall möchten wir jedoch, dass sie entfernt werden. Zeilenvorschübe in den Originaldaten werden hexadezimal dargestellt und bleiben davon unberührt.
Goodside
5

Die Antwort mit xxd ist nett und für kleine Dateien sehr schnell. Unten ist ein Beispielskript, das ich verwende.

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
Gemeinschaft
quelle
1

Verwenden Sie die Funktion Postgres COPY BINARY . Dies entspricht weitgehend den externen Tabellen von Oracle .

Gaius
quelle
Vielen Dank. Der von Ihnen angegebene Link gibt an, dass die Daten entweder im ASCII- oder im PostgreSQL-Binärtabellenformat vorliegen müssen. Weiter unten wird erwähnt, dass das Binärtabellenformat zuerst mit einem COPY TO-Befehl erstellt wird. Kann ich mit einer dieser Methoden eine Binärdatei (PDF, Dokument, Tabellenkalkulation) in eine byteaSpalte einfügen ?
SabreWolfy
Die PostgreSQL-Dokumentation zu COPY BINARY ( postgresql.org/docs/8.4/interactive/sql-copy.html ) gibt an, dass beim Einfügen von Binärdaten ein spezieller Dateikopf erforderlich ist. Muss ich diesen Header erstellen und an Binärdaten anhängen? Das scheint etwas komplex zu sein, wenn man einfach eine Folge von Binärdaten speichert.
SabreWolfy
Hmm, jetzt, wo Sie es erwähnen, bin ich mir nicht sicher, ich habe mich nur an den Befehl erinnert und angenommen, dass er das tun würde. Vielleicht PL / was auch immer ist der einzige Weg, um es zu tun.
Gaius