[PR] あなたが Kindle で読みたいその本、Kindle に対応したら Twitter でお知らせします。

PostgreSQL の PITR で指定できるタイムスタンプの形式

Posted on

PostgreSQL の Point In Time Recovery (PITR) は任意の時点までデータベースをリカバリできる機能です。 recovery.conf に recovery_target_time を指定することでリカバリする時間を決めますが、ドキュメントを読んでも指定できる値が timestamp としか書いてなく非常にわかりにくいです。

PostgrSQL 全機能バイブル」で調べてみると、こんな感じで指定できることがわかりました(この本はおすすめです)。

recovery_target_time = '2014-03-11 15:00:00 JST'

ググってみると、タイムゾーンを JST ではなく +09 と指定する方法もあるようです。 recovery_target_time に指定できる正しい形式が気になってきました。

ソースコードを読んでみる

ドキュメントがアテにならないのならソースコードを読むしかありません。 3/11 時点の最新版である PostgrSQL 9.3.3 のソースコードを読んでみます。

recovery_target_time で grep すると、postgresql-9.3.3/src/backend/access/transam/xlog.c:4271 にそれらしいコードが見つかります。

/*
 * Convert the time string given by the user to TimestampTz form.
 */
recoveryTargetTime =
    DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
                                    CStringGetDatum(item->value),
                                    ObjectIdGetDatum(InvalidOid),
                                            Int32GetDatum(-1)));

今度は呼び出されている timestamptz_in で grep すると、postgresql-9.3.3/src/backend/utils/adt/timestamp.c:409 に定義されていました。

/* timestamptz_in()
 * Convert a string to internal form.
 */
Datum
timestamptz_in(PG_FUNCTION_ARGS)
{
...

処理は 433 行目から行われています。

    dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
                          field, ftype, MAXDATEFIELDS, &nf);
    if (dterr == 0)
        dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
    if (dterr != 0)
        DateTimeParseError(dterr, str, "timestamp with time zone");

実際の変換処理をしているであろう DecodeDateTime でさらに grep すると、postgresql-9.3.3/src/backend/utils/adt/datetime.c:763 のコメントに答えがありました。

/* DecodeDateTime()
 * Interpret previously parsed fields for general date and time.
 * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
 * (Currently, all callers treat 1 as an error return too.)
 *
 *      External format(s):
 *              "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
 *              "Fri Feb-7-1997 15:23:27"
 *              "Feb-7-1997 15:23:27"
 *              "2-7-1997 15:23:27"
 *              "1997-2-7 15:23:27"
 *              "1997.038 15:23:27"     (day of year 1-366)
 *      Also supports input in compact time:
 *              "970207 152327"
 *              "97038 152327"
 *              "20011225T040506.789-07"
 *
 * Use the system-provided functions to get the current time zone
 * if not specified in the input string.
 *
 * If the date is outside the range of pg_time_t (in practice that could only
 * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
 * 1997-05-27
 */

"Interpret previously parsed fields for general date and time." とあるので一般的な日付と時刻の形式は使えるようです。さらに、"20011225T040506.789-07" のようなミリ秒を指定した形式も受け付けるみたいです。

まとめ

ドキュメントに書いてなければソースコードを読む!(でも、ソースコードにだけ例を載せるのはやめてほしい…)