象と戯れ

 | 

2008-09-11

正規表現

18:01 | 正規表現 - 象と戯れ を含むブックマーク はてなブックマーク - 正規表現 - 象と戯れ

SELECT * FROM _matchpatterns WHERE 'abc' ~ pattern OR 'edf' ~ pattern

みたいな、数千の正規表現をテーブルにいれてマッチする行を取り出す処理をしていたのですが、テーブルの行数が増えるとすごく遅い。かわりに、WHERE句のORを増やしてもそんなに遅くならない。で、ソース見たところ、

postgresql-8.3.1/src/backend/utils/adt/regexp.c

/*
 * RE_compile_and_cache - compile a RE, caching if possible
 *
 * Returns regex_t *
 *
 *	text_re --- the pattern, expressed as a TEXT object
 *	cflags --- compile options for the pattern
 *
 * Pattern is given in the database encoding.  We internally convert to
 * an array of pg_wchar, which is what Spencer's regex package wants.
 */
static regex_t *
RE_compile_and_cache(text *text_re, int cflags)
{

ということで、正規表現は一旦コンパイルされてキャッシュされているらしい。同じファイルの先頭の方に

/* this is the maximum number of cached regular expressions */
#ifndef MAX_CACHED_RES
#define MAX_CACHED_RES	32
#endif

とあるので、32までキャッシュされるようです。テーブルに保存された正規表現は数千なので、毎回コンパイル処理が走っているため、これが遅い。逆に32個までならORをつなげてもpatternのコンパイルはSQLあたり一回だから処理スピードはほとんど変わらない、というわけでした。

ところでこのキャッシュ処理には興味深い点があります。

	/*
	 * We use malloc/free for the cre_pat field because the storage has to
	 * persist across transactions, and because we want to get control back on
	 * out-of-memory.  The Max() is because some malloc implementations return
	 * NULL for malloc(0).
	 */
	re_temp.cre_pat = malloc(Max(text_re_len, 1));

当然といえば当然なのですが、通常のpallocでは関数呼び出しの度に確保した領域がクリアされてしまうのでmallocで確保しています。ちょっとひねった関数を書くときなどの参考になりますね。

テーブルに正規表現をtext型で入れている方がいけないのだけど、pattern型とかってないのかな。誰か作ってください。

 |