简体   繁体   English

使用perl / SQL将文件插入Postgres bytea列

[英]Inserting a file into a Postgres bytea column using perl/SQL

I'm working with a legacy system and need to find a way to insert files into a pre-existing Postgres 8.2 bytea column using Perl. 我正在使用遗留系统,需要找到一种方法,使用Perl将文件插入到预先存在的Postgres 8.2 bytea列中。

So far my searching has lead me to believe the following: 到目前为止,我的搜索引导我相信以下内容:

  1. there is no consensus best approach for this. 对此没有达成共识的最佳方法。
  2. lo_import looks promising, but I'm apparently too perl-tarded to get it to work. lo_import看起来很有希望,但我显然太过于无法让它发挥作用。

I was hoping to do something like the following 我希望做类似以下的事情

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();`

My script works w/o the lo_import/bytea part. 我的脚本没有lo_import / bytea部分。 But with it I get this error: 但有了它我得到这个错误:

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. DBD :: Pg :: st执行失败:错误:列“contents”的类型为bytea,但表达式为>类型为oid的字符176 HINT:您需要重写或转换表达式。

What I think I'm doing wrong is that I'm not passing the actual binary file to the DB properly. 我认为我做错了是我没有正确地将实际的二进制文件传递给DB。 I think I'm passing the file path, but not the file itself. 我想我正在传递文件路径,但不是文件本身。 If that's true then what I need to figure out is how to open/read the file into a tmp buffer, and then use the buffer for the import. 如果这是真的那么我需要弄清楚的是如何打开/读取文件到tmp缓冲区,然后使用缓冲区进行导入。

Or am I way off base here? 还是我离开基地? I'm open to any pointers, or alternative solutions as long as they work with Perl 5.8/DBI/PG 8.2. 只要他们使用Perl 5.8 / DBI / PG 8.2,我就会接受任何指针或替代解决方案。

Pg offers two ways to store binary files: Pg提供了两种存储二进制文件的方法:

  • large objects , in the pg_largeobject table, which are referred to by an oid . pg_largeobject表中的大对象 ,由oid引用。 Often used via the lo extension. 经常通过lo扩展使用。 May be loaded with lo_import . 可以加载lo_import

  • bytea columns in regular tables. 常规表中的bytea列 Represented as octal escapes like \\000\\001\\002fred\\004 in PostgreSQL 9.0 and below, or as hex escapes by default in Pg 9.1 and above eg \\x0102 . 在PostgreSQL 9.0及更低版本中表示为八进制转义\\000\\001\\002fred\\004\\000\\001\\002fred\\004 ,或者在Pg 9.1及更高版本中默认为十六进制转义\\x0102 ,例如\\x0102 The bytea_output setting lets you select between escape (octal) and hex format in versions that have hex format. bytea_output设置允许您在具有hex hex格式的版本中选择escape (八进制)和hex hex格式。

You're trying to use lo_import to load data into a bytea column. 您正在尝试使用lo_import将数据加载到bytea列中。 That won't work. 那不行。

What you need to do is send PostgreSQL correctly escaped bytea data. 你需要做的是发送PostgreSQL正确转义的bytea数据。 In a supported, current PostgreSQL version you'd just format it as hex, bang a \\x in front, and you'd be done. 在支持的,当前的PostgreSQL版本中,你只需将其格式化为十六进制,在前面敲一个\\x ,然后你就完成了。 In your version you'll have to escape it as octal backslash-sequences and (because you're on an old PostgreSQL that doesn't use standard_conforming_strings ) probably have to double the backslashes too. 在你的版本中,你必须以八进制反斜杠序列的形式将其转义,并且(因为你在一个不使用standard_conforming_strings的旧PostgreSQL上)可能也需要加倍反斜杠。

This mailing list post provides a nice example that will work on your version, and the follow-up message even explains how to fix it to work on less prehistoric PostgreSQL versions too. 这个邮件列表帖子提供了一个适用于您的版本的好例子,后续消息甚至解释了如何修复它以便在较少史前的PostgreSQL版本上工作。 It shows how to use parameter binding to force bytea quoting. 它显示了如何使用参数绑定来强制bytea引用。

Basically, you need to read the file data in. You can't just pass the file name as a parameter - how would the database server access the local file and read it? 基本上,您需要读取文件数据。您不能只将文件名作为参数传递 - 数据库服务器如何访问本地文件并读取它? It'd be looking for a path on the server. 它正在寻找服务器上的路径。

Once you've read the data in, you need to escape it as bytea and send that to the server as a parameter. 一旦读入数据,就需要将其作为bytea转义,并将其作为参数发送到服务器。

Update: Like this: 更新:像这样:

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();

Thank you to those who helped me out. 谢谢那些帮助过我的人。 It took a while to nail this one down. 花了一段时间才把这个钉下来。 The solution was to open the file and store it. 解决方案是打开文件并存储它。 then specifically call out the bind variable that is type bytea. 然后专门调出类型为bytea的绑定变量。 Here is the detailed solution: 这是详细的解决方案:

.....
##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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM