PostgreSQLのpg_file_write()を参考に、byteaデータをファイルに書き出す関数pg_file_write_binary()を作成します。
以下の内容のpg_file_write_binary.cを用意。
# include "postgres.h"
# include <sys/stat.h>
# include "utils/builtins.h"
# ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
# endif
PG_FUNCTION_INFO_V1(pg_file_write_binary);
Datum
pg_file_write_binary(PG_FUNCTION_ARGS)
{
FILE *f;
char *filename;
text *data;
int64 count = 0;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("only superuser may write file"))));
/*
* Basically convert_and_check_filename() should be called to
* validate the given filepath. But text_to_cstring() is used instead
* for simplicity's sake.
*/
filename = text_to_cstring(PG_GETARG_TEXT_P(0));
data = PG_GETARG_BYTEA_P(1);
if (!PG_GETARG_BOOL(2))
{
struct stat fst;
if (stat(filename, &fst) >= 0)
ereport(ERROR,
(ERRCODE_DUPLICATE_FILE,
errmsg("file \"%s\" exists", filename)));
f = fopen(filename, "wb");
}
else
f = fopen(filename, "ab");
if (!f)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open file \"%s\" for writing: %m",
filename)));
if (VARSIZE(data) != 0)
{
count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f);
if (count != VARSIZE(data) - VARHDRSZ)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write file \"%s\": %m", filename)));
}
fclose(f);
PG_RETURN_INT64(count);
}
pg_file_write_binary.cをコンパイル。
/PATH_TO_PGSQLにはPostgreSQLのインストール先ディレクトリを指定。
$ gcc -fpic -c pg_file_write_binary.c -I/PATH_TO_PGSQL/include/server
$ gcc -shared -o /PATH_TO_PGSQL/lib/pg_file_write_binary.so pg_file_write_binary.o
pg_file_write_binary()をPostgreSQLに登録。
CREATE FUNCTION pg_file_write_binary(text, bytea, boolean default false) RETURNS bigint AS 'pg_file_write_binary' LANGUAGE C STRICT VOLATILE;
pg_file_write_binary()は、以下の引数を受けとり、書き出したデータのバイト数をbigint型で返却します。
- 引数1: データを書き出すファイルのパス (text型)
- 引数2: 書き出すデータ (bytea型)
- 引数3: データをファイルに追記するかどうか (boolean型)
- 未指定の場合はfalseで、追記しない
pg_read_binary_file()で読み込んだバイナリデータ(bytea型)を、pg_file_write_binary()で別ファイルに書き出してみる。
SELECT pg_file_write_binary('pg_control.bak', pg_read_binary_file('global/pg_control'));
元のファイルglobal/pg_controlと書き出し先のファイルpg_control.bakを比較する。
$ diff $PGDATA/global/pg_control $PGDATA/pg_conrol.bak