web-dev-qa-db-fra.com

Comment insérer (fichier) des données dans une colonne bytea PostgreSQL?

Cette question ne concerne pas bytea v. Oid v. Blobs v. Gros objets, etc.

J'ai une table contenant un champ de clé primaire integer et un champ bytea. Je souhaite saisir des données dans le champ bytea. Cela peut probablement être fait par l'un des PL/ langues, et je pourrais envisager de le faire avec PL/Python A l'avenir.

Comme je teste et expérimente encore, je voudrais simplement insérer des données à partir d'un fichier (sur le serveur) en utilisant des instructions SQL "standard". Je suis conscient que seuls les administrateurs disposant d'une autorisation d'écriture sur le serveur pourraient insérer des données de la manière que je souhaiterais. Je ne suis pas préoccupé par cela à ce stade car les utilisateurs n'inséreraient pas de données bytea pour le moment. J'ai recherché les différents sites StackExchange, les archives PostgreSQL et Internet en général, mais je n'ai pas pu trouver de réponse.

Edit: This discussion from 2008 implique que ce que je veux faire n'est pas possible. Comment les champs bytea sont-ils alors utilisés?

Edit: This question similaire de 2005 reste sans réponse.

Résolu: Les détails fournis ici sur le site Web psycopg ont fourni la base d'une solution que j'ai écrite en Python. Il peut également être possible d'insérer des données binaires dans une colonne bytea à l'aide de PL/Python. Je ne sais pas si cela est possible en utilisant du SQL "pur".

40
SabreWolfy

en tant que superutilisateur:

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 a été introduit en 9.4, donc pour les anciennes versions, vous auriez besoin de:

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

puis:

insert into my_table(bytea_data) select bytea_import('/my/file.name');

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

Par exemple,

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

Manuel

19
User2397

Cette solution n'est pas exactement efficace en termes d'exécution, mais elle est très simple par rapport à la création de vos propres en-têtes pour COPY BINARY. De plus, il ne nécessite aucune bibliothèque ou langage de script en dehors de bash.

Tout d'abord, convertissez le fichier en hexdump, en doublant la taille du fichier. xxd -p nous rapproche assez, mais il apporte quelques nouvelles lignes ennuyeuses dont nous devons prendre soin:

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

Ensuite, importez les données dans PostgreSQL en tant que très grand champ text. Ce type peut contenir jusqu'à 1 Go par valeur de champ, nous devrions donc être d'accord pour la plupart des utilisations:

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

Maintenant que nos données sont une chaîne hexadécimale gratuite, nous utilisons decode de PostgresQL pour les entrer dans un type bytea:

CREATE TABLE bindump AS SELECT decode(hex, 'hex') FROM hexdump;
15
goodside

Le réponse avec xxd est sympa et, pour les petits fichiers, très rapide. Voici un exemple de script que j'utilise.

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
5
user1555

Utilisez la fonction Postgres COPY BINARY . C'est globalement équivalent à Oracle tables externes .

1
Gaius

Voici comment le faire sans privilèges de superutilisateur (comme sur Heroku).

\lo_import '/cygdrive/c/Users/Chloe/Downloads/Contract.pdf'
update contracts set contract = lo_get(:LASTOID) where id = 77;

Vous pouvez utiliser \lo_list pour voir les gros objets, et \lo_unlink pour les supprimer. Le champ contract dans mon exemple est bytea.

     Column      |            Type             |
contract         | bytea                       |
0
Chloe