如何从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_largeobject
table里面的数据是:
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_dump
的 pg_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 中。
我的解决方案(针对我的情况):
- 分别从 Postgres9.4 导出普通数据(使用
pg_dump
,排除 LO)和 LO 数据(使用 lo_export
)。
- 将纯数据转储导入 Greenplum
- 用
lo_import
导入LO数据,这将生成一些新的oid
(lo_import
和oid
从Postgres8.4开始),同时更新相应的oid
的引用 table 与这些新的 oid
。
示例脚本:
从 Postgres9.4 导出普通数据
$ pg_dump fhir -O -a -n public -f fhir.dmp
从一些包含 LO 数据的 table 导出 LO,用原始 oids 命名导出的文件
SELECT lo_export(res_text, '/usr/local/pgsql/export/res_lo/'||res_text) FROM hfj_resource;
将纯数据导入 Greenplum
\i fhir.dmp
在 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;
导入 LO
SELECT import_lo('hfj_resource', '/home/gpadmin/export/res_lo');
我使用 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_largeobject
table里面的数据是:
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_dump
的 pg_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 中。
我的解决方案(针对我的情况):
- 分别从 Postgres9.4 导出普通数据(使用
pg_dump
,排除 LO)和 LO 数据(使用lo_export
)。 - 将纯数据转储导入 Greenplum
- 用
lo_import
导入LO数据,这将生成一些新的oid
(lo_import
和oid
从Postgres8.4开始),同时更新相应的oid
的引用 table 与这些新的oid
。
示例脚本:
从 Postgres9.4 导出普通数据
$ pg_dump fhir -O -a -n public -f fhir.dmp
从一些包含 LO 数据的 table 导出 LO,用原始 oids 命名导出的文件
SELECT lo_export(res_text, '/usr/local/pgsql/export/res_lo/'||res_text) FROM hfj_resource;
将纯数据导入 Greenplum
\i fhir.dmp
在 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;
导入 LO
SELECT import_lo('hfj_resource', '/home/gpadmin/export/res_lo');