HOOKを試してみた

本業が一段落ついたので、以前から気になっていたHOOKの組み込みをやってみた。

HOOKの調査

HOOKってPostgreSQL文書にもきちんと書いていないような気がするんだけど、幸いcontribでHOOKを使った実装例

  • contrib/auth_delay
  • contrib/auto_explain
  • contrib/pg_stat_statements
  • contrib/passwordcheck

がいくつもあるので、それを元にちょっと勉強がてら試してみよう。

今回はとっつきやすそうなExecutorの開始(ExecutorStart_hook)と終了(ExecutorFinish_hook)にHOOKをかけて、elog()でメッセージを出すだけのものを作ってみる。

このHOOKなるものがどうやって呼ばれているのかも気になったので、PostgreSQLのソースを探してみたけど、どうやら src/backend/executor/execMain.c の中でHOOKの呼び出しをやっているっぽい(Finishもほぼ同じ)。

void
ExecutorStart(QueryDesc *queryDesc, int eflags)
{
        if (ExecutorStart_hook)
                (*ExecutorStart_hook) (queryDesc, eflags);
        else
                standard_ExecutorStart(queryDesc, eflags);
}

ExecutorStart_hook がNULLならstandard_ExecutorStart()を呼び出して、NULLじゃない(関数ポインタが設定されている?)なら、ExecutorStart_hookに設定した関数を呼び出すようだ。
なので、自作HOOK関数内で、自作コードを実行した後に、standard_ExecutorStart()を呼ばないと切ないこと(PostgreSQLのExectorが動かない)になるんだな。

実装

ということで、こんな感じで実装してみた。

/*-------------------------------------------------------------------------
 * exec.c
 *-------------------------------------------------------------------------
 */
#include "postgres.h"
#include "executor/executor.h"

PG_MODULE_MAGIC;

extern void _PG_init(void);

extern ExecutorStart_hook_type ExecutorStart_hook ;
extern ExecutorFinish_hook_type ExecutorFinish_hook;

/*
 * myExecutorStart_hook
 */
static void
myExecutorStart_hook(QueryDesc *queryDesc, int eflags)
{
    elog(NOTICE, "エグゼQ太 ハジマタ\(^o^)/");
    standard_ExecutorStart(queryDesc, eflags);  // 本来のExecutorのエントリ
}

/*
 * myExecutorFinish_hook
 */
static void
myExecutorFinish_hook(QueryDesc *queryDesc)
{
    elog(NOTICE, "エグゼQ太 オワタ\(^o^)/");
    standard_ExecutorFinish(queryDesc);  // 本来のExecutorのエントリ
}

/*
 * Module initialization function
 */
void
_PG_init(void)
{
        /* activate hook module is loaded */
        ExecutorStart_hook = myExecutorStart_hook;
        ExecutorFinish_hook = myExecutorFinish_hook;
}

これを make; make install して myexechook.so という共有ライブラリを作成して /lib に突っ込む。

実行

このHOOKをPostgreSQLに組み込むために、 postgresql.conf に以下のエントリを追加する。

shared_preload_libraries = 'myexechook'

これで、PostgreSQLを起動。
すると起動メッセージに共有ライブラリの組み込みのメッセージが出力される。組み込み完了!

$ pg_ctl start -D  ~/pgdata/
server starting
LOG:  loaded library "myexechook"
LOG:  database system was shut down at 2012-10-30 17:11:10 JST
LOG:  autovacuum launcher started
LOG:  database system is ready to accept connections

PostgreSQLが起動したので、psqlからテキトーにSQLを実行してみる。

$ psql postgres
psql (9.2.1)
Type "help" for help.

postgres=# SELECT 1;
NOTICE:  エグゼQ太 ハジマタ\(^o^)/
NOTICE:  エグゼQ太 オワタ\(^o^)/
 ?column? 
----------
        1
(1 row)

elog()が出力されたので、HOOK関数経由でExecutorが呼ばれたことが確認できた。

今後は、各HOOK関数の引数の中を吟味して、どういうイケてるHOOKが出来そうか考えないとなあ・・・。