Como inserir (arquivo) dados em uma coluna de bytea do PostgreSQL?

38

Esta questão não é sobre bytea v. Oid v. Blobs v. Objetos grandes, etc.

Eu tenho uma tabela que contém um integercampo de chave primária e um byteacampo. Eu gostaria de inserir dados no byteacampo. Presumivelmente, isso pode ser feito por um dos PL/idiomas, e eu posso tentar fazê-lo PL/Pythonno futuro.

Como ainda estou testando e experimentando, gostaria de inserir dados de um arquivo (no servidor) usando instruções SQL "padrão". Estou ciente de que apenas os administradores com permissão de gravação no servidor poderão inserir dados da maneira que gostaria. Não estou preocupado com isso nesse estágio, pois os usuários não estariam inserindo byteadados no momento. Pesquisei nos vários sites StackExchange, nos arquivos do PostgreSQL e na Internet em geral, mas não consegui encontrar uma resposta.

Edit: Esta discussão de 2008 implica que o que eu quero fazer não é possível. Como os byteacampos são usados ​​então?

Edit: Esta pergunta semelhante de 2005 permanece sem resposta.

Resolvido: Os detalhes fornecidos aqui no psycopgsite forneceram a base para uma solução que eu escrevi em Python. Também pode ser possível inserir dados binários em uma byteacoluna usando PL/Python. Não sei se isso é possível usando SQL "puro".

SabreWolfy
fonte
11
O link para os documentos do psycopg está quebrado e minha edição parece ter sido rejeitada (!?). Aqui está a localização atual .
Aryeh Leib Taurog
@AryehLeibTaurog: Obrigado. Rejeitei a edição porque não estava claro para mim que seu texto alterado era um hiperlink. Se você quiser fazer a edição novamente, eu aprovarei.
SabreWolfy
@Andriy_M Por que você acha que "Esta edição se desvia da intenção original da postagem". (A edição feito por informatik01?)
miracle173
@ miracle173: Porque tive a impressão de que algumas das tags sugeridas eram irrelevantes (bem, apenas uma na verdade blob). Se isso foi um erro, peço desculpas sinceramente.
Andriy M

Respostas:

27

como superusuário:

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 foi introduzido na 9.4, portanto, para versões mais antigas, você precisaria 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;$$;

então:

insert into my_table(bytea_data) select bytea_import('/my/file.name');
Jack Douglas
fonte
Para o processo inverso, eu não tentei isso , mas se ele funciona, lo_export será tudo que você precisa
Jack Douglas
17

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

Por exemplo,

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

Manual

sudalai
fonte
2
Ou um pouco mais simples pg_read_binary_file('/path/to/file'). Veja postgresql.org/docs/current/static/functions-admin.html
Arto Bendiken
15

Essa solução não é exatamente eficiente em termos de tempo de execução, mas é trivialmente fácil se comparada à criação de seus próprios cabeçalhos COPY BINARY. Além disso, ele não requer bibliotecas ou linguagens de script fora do bash.

Primeiro, converta o arquivo em um hexdump, dobrando o tamanho do arquivo. xxd -pnos aproxima bastante, mas lança algumas novas linhas irritantes que precisamos cuidar:

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

Em seguida, importe os dados no PostgreSQL como um textcampo muito grande . Esse tipo possui até um GB por valor de campo, portanto, devemos concordar com a maioria dos propósitos:

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

Agora que nossos dados são uma sequência hexadecimal grande e gratuita, usamos o PostgresQL decodepara inseri -lo em um byteatipo:

CREATE TABLE bindump AS SELECT decode(hex, 'hex') FROM hexdump;
lado bom
fonte
Esta solução resulta em \ n caracteres sendo removidos do arquivo.
SabreWolfy
2
SabreWolfy: Não, não faz. O tr -d '\n'está operando na saída do xxd, que codifica o conteúdo binário da entrada como caracteres hexadecimais ASCII (0-9 e af). O xxd também acontece com feeds de linha de saída em intervalos regulares para tornar a saída legível por humanos, mas, neste caso, queremos que sejam removidos. Os feeds de linha nos dados originais estarão em formato hexadecimal e não serão afetados.
goodside
5

A resposta com o xxd é boa e, para arquivos pequenos, muito rápida. Abaixo está um exemplo de script que estou usando.

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
Comunidade
fonte
1

Use a função COPY BINARY do Postgres . Isso é amplamente equivalente às tabelas externas da Oracle .

Gaius
fonte
Obrigado. O link que você forneceu indica que os dados devem estar no formato de tabela binária do ASCII ou PostgreSQL. Mais abaixo na página, é mencionado que o formato da tabela binária é criado pela primeira vez com um comando COPY TO. Qualquer uma dessas abordagens me permitia inserir um arquivo binário (PDF, documento, planilha) em uma byteacoluna?
SabreWolfy
A documentação do PostgreSQL em COPY BINARY ( postgresql.org/docs/8.4/interactive/sql-copy.html ) indica que um cabeçalho de arquivo especial é necessário ao inserir dados binários. Preciso criar esse cabeçalho e anexá-lo aos dados binários? Isso parece um pouco complexo para simplesmente armazenar uma sequência de dados binários.
SabreWolfy
Hmm, agora que você mencionou, não tenho certeza, lembrei-me do comando e assumi que ele faria isso. Talvez PL / seja qual for a única maneira de fazê-lo.
Gaius