如何从Postgres 9.4转储大对象数据,然后导入到Postgres8.x?

How to dump the large object data from Postgres 9.4, and then import it to Postgres8.x?

我使用 pg_dump 从 Postgres 9.4 中导出包含大对象 (LO) 的数据,如下所示:

$ pg_dump fhir -O -b > fhir.sql  

我生成的fhir.sql中的LO语句是这样的:

SET standard_conforming_strings = on;
SELECT pg_catalog.lowrite(0, '\x1f8b0800000000000000a5903b6ec3300c86efa2d9b1dad5a728daa2');

当我在我的 Postgres8.2 中执行 \i fhir.sql 时,我得到了这个错误:

ERROR:  invalid input syntax for type bytea

当我SET standard_conforming_strings = off的时候插入了数据,但是得到了警告,我的pg_largeobjecttable里面的数据是:

14 |      0 | 78b0800000000000000a5903b6ec3300c86efa2d9b1dad5a728daa2

原来的\x1f改成了7,我测试了一下,已经不是我原来的zip文件了…

我该如何解决这个问题?

更新:

我用Hibernate程序把同样的原始数据插入到Greenplum(基于Postgresql8.2)中,然后用pg_dump导出,它的格式是这样的:

SELECT pg_catalog.lowrite(0, '\037\213\010\000\000\000\000\000\000\000\245\220;n\3030\014')

问题在于转储使用函数 pg_catalog.lowrite(integer, bytea) 创建大对象,而 PostgreSQL 中 bytea 文字的默认语法在 9.0 版中发生了变化。

有一个参数bytea_output,可以设置为escape,在PostgreSQL以后的版本中以旧格式输出bytea。 las,pg_dump 在创建转储时不遵守该参数,它始终使用“新”hex 格式。

结果是无法将包含来自 PostgreSQL 9.0 或更高版本的大对象的转储还原到 9.0 之前的数据库中。

您必须以其他方式转移这些大对象,可能是通过编写迁移程序。

您可以(在 pgsql-hackers 邮件列表上)向 pg_dump 提议一个允许为转储设置 bytea_escape 的选项,但您可能会遇到阻力,因为从不支持较旧的 PostgreSQL 版本。


更新

我发现了一个更简单的工作方法:只需使用 pg_dump -b -Fc 将包括 LO 的数据导出到自定义文件中,稍后使用与您相同版本的 pg_dumppg_restore用于导出数据将自定义文件数据导入greenplum。

脚本:

$ pg_dump fhir -O -a -Fc  -f fhir.dump
$ pg_restore -h mdw -d fhir -U gpadmin -a fhir.dump > errors.log 2>&1

我错过的一个事实是 lo_export 导出的二进制数据可以完美地导入到 greenplum 中。

我的解决方案(针对我的情况):

  1. 分别从 Postgres9.4 导出普通数据(使用 pg_dump,排除 LO)和 LO 数据(使用 lo_export)。
  2. 将纯数据转储导入 Greenplum
  3. lo_import导入LO数据,这将生成一些新的oidlo_importoid从Postgres8.4开始),同时更新相应的oid 的引用 table 与这些新的 oid

示例脚本:

  1. 从 Postgres9.4 导出普通数据

     $ pg_dump fhir -O -a -n public -f fhir.dmp
    
  2. 从一些包含 LO 数据的 table 导出 LO,用原始 oids 命名导出的文件

    SELECT lo_export(res_text, '/usr/local/pgsql/export/res_lo/'||res_text) FROM hfj_resource;
    
  3. 将纯数据导入 Greenplum

    \i fhir.dmp
    
  4. 在 Greenplum 中创建一个函数来导入 LO 并更新引用的 oid

    CREATE FUNCTION import_lo(tab_name text, lo_path text) RETURNS void AS $$
    
     DECLARE
    
        res record;
    
        new_oid oid;
    
     BEGIN
    
        FOR res in EXECUTE 'select res_text from '|| LOOP
    
            new_oid := lo_import(||'/'||res.res_text);
    
            RAISE NOTICE 'res_text from % to %', res.res_text, new_oid;
    
            EXECUTE 'update '||||' set res_text='||new_oid||'where res_text='||res.res_text;
    
        END LOOP;
    
        RAISE NOTICE 'import large object into % finished .....', ;
    
     END;
    
     $$ LANGUAGE plpgsql;
    
  5. 导入 LO

    SELECT import_lo('hfj_resource', '/home/gpadmin/export/res_lo');