2013-07-03 1 views
1

Я работаю с устаревшей системой и должен найти способ вставки файлов в ранее существовавший столбец Postgres 8.2 bytea с использованием Perl.Вставка файла в столбец bytea Postgres с использованием perl/SQL

До сих пор мой поиск привел меня верить следующее:

  1. нет консенсуса лучшего подхода для этого.
  2. lo_import выглядит многообещающим, но я, видимо, слишком заперт, чтобы заставить его работать.

Я надеялся, что сделать что-то вроде следующего

my $bind1 = "foo" 
my $bind2 = "123" 
my $file = "/path/to/file.ext" 

my $q = q{ 
     INSERT INTO generic_file_table 
     (column_1, 
     column_2, 
     bytea_column 
     ) 
     VALUES 
     (?, ?, lo_import(?)) 
}; 
my $sth = $dbh->prepare($q); 
$sth->execute($bind1, $bind2, $file); 
$sth->finish();` 

Мой сценарий работает б/н lo_import/BYTEA части. Но с ним я получаю эту ошибку:

DBD::Pg::st execute failed: ERROR: column "contents" is of type bytea but expression is >of type oid at character 176 HINT: You will need to rewrite or cast the expression.

То, что я думаю, что я делаю неправильно, что я не передавая фактический двоичный файл в БД правильно. Я думаю, что передаю путь к файлу, но не сам файл. Если это правда, то мне нужно выяснить, как открыть/прочитать файл в буфере tmp, а затем использовать буфер для импорта.

Или я отходит от базы здесь? Я открыт для любых указателей или альтернативных решений, если они работают с Perl 5.8/DBI/PG 8.2.

+0

Что такое столбец «Содержимое»? Это не упоминается нигде в вашем коде. – Flimzy

+0

Вы правы, что передаете имя файла, а не файл. Сначала вам нужно прочитать файл. – Flimzy

+1

+1 спасибо за упоминание ваших версий, объяснение и показ того, что вы пробовали. Образцовый вопрос, спасибо. Теперь переходите к чему-то цивилизованному ;-) –

ответ

3

Pg предлагает два способа хранения бинарных файлов:

  • крупные объекты, в pg_largeobject таблице, которые упоминаются с помощью oid. Часто используется через расширение lo. Может быть загружен lo_import.

  • bytea columns в обычных таблицах. Представлен как восьмеричные escape-последовательности, такие как \000\001\002fred\004 в PostgreSQL 9.0 и ниже, или как шестнадцатеричные экраны по умолчанию в Pg 9.1 и выше, например \x0102. Параметр bytea_output позволяет выбирать между форматами escape (октал) и hex в версиях, имеющих формат hex.

Вы пытаетесь использовать lo_import, чтобы загрузить данные в bytea колонку. Это не сработает.

Что вам нужно сделать, это отправить PostgreSQL правильно экранированные данные bytea. В поддерживаемой текущей версии PostgreSQL вы просто отформатируете ее как шестнадцатеричный, запустите \x спереди, и все будет готово. В вашей версии вам придется избегать этого как восьмеричные последовательности обратного слэша и (потому что вы на старом PostgreSQL, который не использует standard_conforming_strings), вероятно, придется удвоить обратную косую черту.

This mailing list post представляет собой хороший пример, который будет работать над вашей версией, а последующее сообщение объясняет, как исправить его, чтобы работать с менее доисторическими версиями PostgreSQL. Он показывает, как использовать привязку параметров к цитированию force bytea.

В принципе, вам необходимо прочитать данные файла. Вы не можете просто передать имя файла в качестве параметра - как бы база данных server получить доступ к локальному файлу и прочитать его? Он будет искать путь на сервере.

После того, как вы прочитали данные, вам нужно сбежать от него в качестве байта и отправить его на сервер в качестве параметра.

Update: Как это:

use strict; 
use warnings; 
use 5.16.3; 

use DBI; 
use DBD::Pg; 
use DBD::Pg qw(:pg_types); 
use File::Slurp; 

die("Usage: $0 filename") unless defined($ARGV[0]); 
die("File $ARGV[0] doesn't exist") unless (-e $ARGV[0]); 
my $filename = $ARGV[0]; 


my $dbh = DBI->connect("dbi:Pg:dbname=regress","","", {AutoCommit=>0}); 
$dbh->do(q{ 
     DROP TABLE IF EXISTS byteatest; 
     CREATE TABLE byteatest(blah bytea not null); 
}); 
$dbh->commit(); 

my $filedata = read_file($filename); 
my $sth = $dbh->prepare("INSERT INTO byteatest(blah) VALUES (?)"); 
# Note the need to specify bytea type. Otherwise the text won't be escaped, 
# it'll be sent assuming it's text in client_encoding, so NULLs will cause the 
# string to be truncated. If it isn't valid utf-8 you'll get an error. If it 
# is, it might not be stored how you want. 
# 
# So specify {pg_type => DBD::Pg::PG_BYTEA} . 
# 

$sth->bind_param(1, $filedata, { pg_type => DBD::Pg::PG_BYTEA }); 
$sth->execute(); 
undef $filedata; 

$dbh->commit(); 
0

Спасибо тем, кто помог мне. Потребовалось время, чтобы прибить это. Решение заключалось в том, чтобы открыть файл и сохранить его. затем специально вызывать переменную связывания, которая является типом bytea. Вот подробное решение:

..... 
##some variables 
my datum1 = "foo"; 
my datum2 = "123"; 
my file = "/path/to/file.dat"; 
my $contents; 

##open the file and store it 
open my $FH, $file or die "Could not open file: $!"; 
{ 
local $/ = undef; 
$contents = <$FH>; 
}; 
close $FH; 
print "$contents\n"; 

##preparte SQL 
my $q = q{ 
    INSERT INTO generic_file_table 
    (column_1, 
    column_2, 
    bytea_column 
    ) 
    VALUES 
    (?, ?, ?) 
    }; 
my $sth = $dbh->prepare($q); 
##bind variables and specifically set #3 to bytea; then execute. 
$sth->bind_param(1,$datum1); 
$sth->bind_param(2,$datum2); 
$sth->bind_param(3,$contents, { pg_type => DBD::Pg::PG_BYTEA }); 
$sth->execute(); 
$sth->finish(); 

 Смежные вопросы

  • Нет связанных вопросов^_^