ククログ

株式会社クリアコード > ククログ > PGroongaの演算子に新しいデータ型のサポートを追加する方法

PGroongaの演算子に新しいデータ型のサポートを追加する方法

最近、PGroongaの演算子を改良した堀本です。 今回、正規表現を用いた検索で使う演算子に新しいデータ型のサポートを追加したので、どうやって追加したかを紹介します。

開発の背景

今回改良したのは&~演算子(正規表現検索用の演算子)です。

今回の改良を行う前は、text型とvarvhar型のカラムに対してのみ正規表現を使った検索をサポートしていました。 しかし、PostgreSQLには他にもtext[]型やvarchar[]型のように文字列を格納するデータ型があります。

今回は、text[]型のカラムに対して正規表現を用いて後方一致検索を実現したいというケースがあったため、 text[]型をサポートしました。

開発の手順

まず、text[] &~ textの形でクエリーが実行された時に呼び出される関数を定義/実装し その後、&~演算子にtext[]型のサポートを追加します。 最後に、アップグレード/ダウングレード時に今回追加した定義を追加/削除するためのSQLを書いて完了です。

まとめると、以下のような手順で演算子を改良します。

  1. 新しい関数の定義
  2. 新しい関数の実装
  3. 既存の演算子にデータ型を追加
  4. アップグレード/ダウングレードSQLの作成

新しい関数の定義

最初にtext[] &~ textの形でクエリーが実行された時に呼び出される関数を定義します。 関数の実装はまた別途行うので、ここでは定義だけ行います。 関数の定義は、CREATE FUNCTIONを使って行います。

関数の定義を追加する場所は、 https://github.com/pgroonga/pgroonga/blob/main/data/pgroonga.sql です。 関数の名前や引数、戻り値の型等を定義します。 関数名は、pgroonga_regexp_text_arrayとし、引数はtext[] &~ textの左辺と、右辺のデータを取ります。 また、戻り値はtext[] &~ textの条件にヒットするかどうかを返すのでboolになります。 以上から、関数の定義は以下のようになります。

CREATE FUNCTION pgroonga_regexp_text_array(targets text[], pattern text)
	RETURNS bool
	AS 'MODULE_PATHNAME', 'pgroonga_regexp_text_array'
	LANGUAGE C
	IMMUTABLE
	STRICT
	LEAKPROOF
	PARALLEL SAFE
	COST 300;

関数の定義はこれで終わりです。

新しい関数の実装

次は、text[] &~ textの形でクエリーが実行された時に呼び出される関数の実装を行います。 ここはC言語での実装になります。 PostgreSQLにC言語で実装された関数を追加する時は、呼び出し規約に従って実装します。

呼び出し規約に従うと関数のプロトタイプは以下のようになります。

PG_FUNCTION_INFO_V1(pgroonga_regexp_text_array);

関数定義は以下のようになります。

Datum
pgroonga_regexp_text_array(PG_FUNCTION_ARGS){...}

この呼び出し規約により、固定のフォーマットでC言語で実装された関数とやり取りができるようになります。 しかしこれでは、引数はすべてPG_FUNCTION_ARGSですし、戻り値の型はDatumです。

これらを適切な型の値にするには、マクロを利用します。 引数を取得するには、PG_GETARG_xxxを、戻り値にはPG_RETURN_xxxを使います。 今回は、引数をtext[]text、戻り値の型をboolにするので、以下のようにします。

Datum
pgroonga_regexp_text_array(PG_FUNCTION_ARGS)
{
    ArrayType *targets = PG_GETARG_ARRAYTYPE_P(0);
    text *pattern = PG_GETARG_TEXT_PP(1);
    bool matched = false;

    ...

    PG_RETURN_BOOL(matched);
}

これで、基本形はできました。 あとは受け取った引数を使って処理を実装すれば完了です。

既存の演算子にデータ型を追加

次に&~text[]型を追加します。 追加する場所は、 https://github.com/pgroonga/pgroonga/blob/main/data/pgroonga.sql です。 text[]型の追加は、CREATE OPERATORを使って行います。

ここでは、演算子の名前や演算子の左辺と右辺のデータ型、実装を定義した関数名等を定義します。 今回は、text[] &~ textをサポートするので以下のように定義します。

  • 演算子の名前:&~
  • 左辺のデータ型:text[]
  • 右辺のデータ型はtext
  • text[] &~ textの処理を実装した関数名:pgroonga_regexp_text_array

具体的には以下のような定義になります。

CREATE OPERATOR &~ (
	PROCEDURE = pgroonga_regexp_text_array,
	LEFTARG = text[],
	RIGHTARG = text,
	RESTRICT = contsel,
	JOIN = contjoinsel
);

これで、text[]型の追加は終わりです。

アップグレード/ダウングレードSQLの作成

最後にアップグレード/ダウングレード時に実行されるSQLを作成します。 これがないと、古いバージョンからのアップグレード時に新しい定義が追加されません。 また、ダウングレード時には、新しく追加した定義が削除されません。 したがって、新規の定義がある場合は、アップグレード/ダウングレード時のケアも必要です。

アップグレードSQLについては、ここまでの手順で作成したものと同じものを記載します。 記載する場所は、 https://github.com/pgroonga/pgroonga/blob/main/data/pgroonga--3.2.4--3.2.5.sql です。

具体的には以下のようなSQLになります。

CREATE FUNCTION pgroonga_regexp_text_array(targets text[], pattern text)
    RETURNS bool
    AS 'MODULE_PATHNAME', 'pgroonga_regexp_text_array'
    LANGUAGE C
    IMMUTABLE
    STRICT
    LEAKPROOF
    PARALLEL SAFE
    COST 300;

CREATE OPERATOR &~ (
    PROCEDURE = pgroonga_regexp_text_array,
    LEFTARG = text[],
    RIGHTARG = text,
    RESTRICT = contsel,
    JOIN = contjoinsel
);

ダウングレードSQLについては、今回追加したものを削除するSQLを記載します。 記載する場所は、 https://github.com/pgroonga/pgroonga/blob/main/data/pgroonga--3.2.5--3.2.4.sql です。

ここで注意するのは、追加したtext[] &~ textの定義と関数には依存関係があるので、削除する順序によっては失敗します。 依存関係を考慮して、text[] &~ textの定義->関数の順で削除すれば良いです。

具体的には以下のようなSQLになります。

-- ダウングレードSQL

DROP OPERATOR &~ (text[], text);
DROP FUNCTION pgroonga_regexp_text_array;

これでアップグレード/ダウングレードSQLの作成は終わりです。

まとめ

今回は、既存の演算子にデータ型を追加する方法を紹介しました。

もし、PGroongaを使っていて何かお困りのことがありましたら、お問い合わせよりご連絡ください。