将表中的所有二进制列转储为文件

tzi*_*ppy 4 postgresql bytea

我在 postgresql 数据库中有一个表,有一列类型为bytea。我想使用 id 作为文件名将每个二进制条目作为文件删除。

SQL 查询会是什么样的?

Dan*_*ité 5

要将bytea保存到磁盘服务器端,必须是数据库超级用户。不允许普通用户写入文件系统。

假设具有超级用户权限,最简单的方法是将其实现为“不受信任”语言之一的功能。

pl/perlu 中的示例:

CREATE FUNCTION bytea_to_file(bytea,text) RETURNS void AS $$
  open(my $fd, ">".$_[1]) or die $!;
  binmode($fd);
  print $fd decode_bytea($_[0]);
  close($fd);
$$ language plperlu;
Run Code Online (Sandbox Code Playgroud)

用法:

select bytea_to_file(bytea_column, concat('/path/to/destination/', id_column)) 
from tablename where...
Run Code Online (Sandbox Code Playgroud)

如果此类语言在您的 postgres 环境中不可用,则可以在 pl/pgsql 中制作一个非高效版本。这是低效的,因为它必须创建一个临时大对象并将整个数据复制到其中,然后再将其导出为文件,然后清除大对象。

pl/pgsql 版本(低效)

CREATE FUNCTION bytea_to_file_with_lo(bytea,text) RETURNS void AS $$
declare
 o oid;
 fd integer;
 INV_WRITE int := 131072;
begin
 o:=lo_create(-1);
 fd:=lo_open(o, INV_WRITE);
 if (fd<0) then
   raise exception 'Failed to open large object %', o;
 end if;

 perform lowrite(fd, $1);
 if (lo_close(fd)<>0) then
   raise exception 'Failed to close large object %', o;
 end if;

 perform lo_export(o, $2);
 perform lo_unlink(o);
end;
$$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)

用法:与 pl/perlu 版本相同。您仍然需要成为超级用户,除非此功能的超级用户权限仅授予其他用户(在当前情况下,只有100% 信任用户才可以,否则将是灾难性的):

alter FUNCTION bytea_to_file_with_lo(bytea,text) SECURITY DEFINER;
Run Code Online (Sandbox Code Playgroud)

psql 客户端版本(效率稍低)

当您拥有的只是客户端psql解释器时,无法直接提取二进制内容(归结为 psql 对 '\0' 字节不利),但可以轻松获得十六进制或 base64 的中间表示并在 psql 之外进行后处理。

shell 中的示例,假设存在base64来自 GNU coreutils的命令:

$ psql -Atc "select encode(bytea_column,'base64') from tablename" | \
   base64 -d >/path/to/destination/filename
Run Code Online (Sandbox Code Playgroud)

这一次只输出一行和一列。同时提取另一列(如 ID)对于后处理来说将明显困难得多,因此这个简单的示例假设某种外部循环遍历先前已获得的 ID。