小ネタ:PostgreSQLのバージョンチェック

とある事情でPostgreSQLのバージョンチェックについてちょこっと調べてみた。

バージョンチェックとは

PostgreSQLはメジャーバージョン間ではデータベースクラスタの互換性がない。
なので、PostgreSQLサーバの起動プロセス(postgres, 旧postmaster)のバージョンと、データベースクラスタのバージョンが一致するかを起動時にチェックして、バージョンが異なる場合にはバージョン不一致としてエラーとし、PostgreSQLを起動しないようにしている。
このバージョンチェックのために、データベースクラスタ内にある、PG_VERSIONファイルというものを参照している。
PG_VERSIONの内容は単純にメジャーバージョン番号のみが入ったテキストファイルだ。

[nuko]$ cat $PGDATA/PG_VERSION
9.3
[nuko]$

実行例

PostgreSQLサーバのバージョンとして9.3.2を使った例を示す。
まず、PG_VERSIONのファイル自体が存在しない場合。

[nuko]$ FATAL:  "/home/harada/pgdata/9.3" is not a valid data directory
DETAIL:  File "/home/harada/pgdata/9.3/PG_VERSION" is missing.

次にPG_VERSION内のフォーマットが不正な例

[nuko]$ cat $PGDATA/PG_VERSION
9.X
[nuko]$ pg_ctl start
server starting
[nuko]$ FATAL:  "/home/harada/pgdata/9.3" is not a valid data directory
DETAIL:  File "/home/harada/pgdata/9.3/PG_VERSION" does not contain valid data.
HINT:  You might need to initdb.

PG_VERSIONの記述形式は正しいけど、メジャーバージョンが合っていないという例。

[nuko]$ cat $PGDATA/PG_VERSION
9.2
[nuko]$ pg_ctl start
server starting
[nuko]$ FATAL:  database files are incompatible with server
DETAIL:  The data directory was initialized by PostgreSQL version 9.2, which is not compatible with this version 9.3.2.

なお、バージョン番号のチェック"x.x"までしか見てないので、例えばマイナーバージョンに相当する番号をテキトーな文字にしてもスルーしてフツーに起動する。

[nuko]$ cat $PGDATA/PG_VERSION
9.3.X
[nuko]$ pg_ctl start
server starting
[nuko]$ LOG:  database system was shut down at 2014-02-15 23:43:29 PST
LOG:  database system is ready to accept connections
LOG:  autovacuum launcher started

実装

この処理は、 ./backend/utils/init/miscinit.c の void ValidatePgVersion(const char *path) という関数で実装している。
で、この関数は
PostmasterMain()→checkDataDir()→ValidatePgVersion()
という順に呼び出されている。

この関数内でまずPG_VERSIONファイルのオープンを試み、その後で

ret = fscanf(file, "%ld.%ld", &file_major, &file_minor);

fscanfでファイル内に書かれている文字列からメジャーバージョン番号とマイナーバージョン番号を取得する。
fscanf()の戻り値が2でなければ、フォーマットエラーとみなす。
その後、PG_VERSIONファイルから取得したメジャーバージョンとマイナーバージョンを定数値 PG_VERSION (このバージョンの場合は"90302")から抜き出したメジャーバージョンと比較している。
なので、PG_VERSIONファイルに"9.3.X"と書いてあっても、2つ目以降のピリオドは無視されるのだろう。

なお、定数値 PG_VERSIONはこのファイルで定義されている。

./include/pg_config.h:699:#define PG_VERSION "9.3.2"

で、ここの値はPostgreSQLをビルドインストールするときの .configure で生成しているんじゃないかと思う。きちんと確認してないけど。