最近、MroongaのMySQL 8.0対応を進めている須藤です。あとはコンディションプッシュダウンまわりを実装すればMySQL 8.0対応は完了しそうです。MySQL 8.0対応をしていて「お!」という思うコードがあったことを思い出したので15回目のノータブルコードとして紹介します。
MySQLの全文検索機能はMATCH (...) AGAINST (...)
という構文を使います。この構文をWHERE
で書けば絞り込み条件になり、SELECT
直後の出力リストのところやORDER BY
のところに書けばその全文検索でのスコアーになります。
たとえば、次のSQLでは3つ同じMATCH (...) AGAINST (...)
がありますが、2番目のMATCH (...) AGAINST (...)
だけが条件で残りのMATCH (...) AGAINST (...)
は全文検索でのスコアーになります。
SELECT MATCH (content) AGAINST ('+Hello' IN BOOLEAN MODE)
FROM memos
WHERE MATCH (content) AGAINST ('+Hello' IN BOOLEAN MODE)
ORDER BY MATCH (content) AGAINST ('+Hello' IN BOOLEAN MODE) DESC;
1つのSELECT
中に違う種類のMATCH (...) AGAINST (...)
を複数書くこともできます。たとえば、次のように書くこともできます。
SELECT MATCH (content) AGAINST ('+Hello' IN BOOLEAN MODE),
MATCH (content) AGAINST ('+World' IN BOOLEAN MODE)
FROM memos
WHERE MATCH (content) AGAINST ('+Hello' IN BOOLEAN MODE) AND
MATCH (content) AGAINST ('+World' IN BOOLEAN MODE)
ORDER BY MATCH (content) AGAINST ('+Hello' IN BOOLEAN MODE) DESC;
このため、内部的には全文検索の種類(↑の例だとHello
とWorld
での全文検索2種類)ごとにそれぞれリソースを確保します。このリソースには検索結果やどのレコードがどのくらいのスコアーかといった情報を格納することになります。
このリソースは各ストレージエンジンがFT_INFO
構造体を親にした構造体の領域を確保してMySQLに返します。FT_INFO
構造体は次のように定義されています。_ft_vft
構造体の中は「次のレコードを取得」・「対象レコードのスコアーを取得」といった機能を実装する関数ポインターの集まりになっていて、各ストレージエンジンの実装をMySQLがコールバックできるようになっています。
struct FT_INFO {
struct _ft_vft *please; /* INTERCAL style :-) */
};
MySQLはストレージエンジンからFT_INFO
構造体を受け取り、必要に応じてストレージエンジンにコールバックして必要な情報を得ます。たとえば、スコアーを取得する場合は次のようになっています。ft_handler
がFT_INFO
構造体を参照している変数です。
ft_handler->please->get_relevance(ft_handler);
なんかplease
付きでスコアー(relevance
)を取得していて丁寧な感じがしますね!
ところで、FT_INFO
構造体の定義のコメントの「INTERCAL style :-)」には気づいていましたか?実は、これはINTERCALというプログラミング言語のマネをしています。INTERCALでは命令を実行する前に「DO」か「PLEASE」か「PLEASE DO」を指定しないといけません1。FT_INFO
構造体のメンバー名をplease
にするとplease->XXX()
と呼び出さないといけなくなるのでINTERCALのような書き方になるというわけです。なお、WikipediaによるとINTERCALは1972年に開発されたプログラミング言語だそうなのでその頃からPLEASEと言っていたことになります。一方、FT_INFO
構造体は2001年に導入されている2のでINTERCALから30年ほど遅れてスタイルを踏襲したことになります。
MySQLはINTERCALと違って今でも活発に開発が続いています。全文検索まわりも改良されています。2012年には次のようなFT_INFO_EXT
構造体が追加されました。InnoDBの全文検索機能のためです。
struct FT_INFO_EXT {
struct _ft_vft *please; /* INTERCAL style :-) */
struct _ft_vft_ext *could_you;
};
FT_INFO
構造体との違いは_ft_vft_ext
構造体のメンバーが増えていることだけです。ft_vft_ext
構造体も_ft_vft
構造体と同じようにコールバック用の関数ポインターが集まっています。追加のコールバックを互換性を壊さずに追加するためにFT_INFO_EXT
構造体を新設したというわけです。
MySQLが_ft_vft_ext
構造体のコールバックを呼ぶときは次のようになります。(キャストは省略しています。)
ft_handler->could_you->count_matches(ft_handler);
please->XXX()
じゃなくてcould_you->XXX()
になっているのでFT_INFO
構造体のときよりさらに丁寧になっている気がしますね!10年ちょいの時を経ている3のに、INTERCALのスタイルをマネするという既存のコードのスタイルを壊さずに拡張しています。OSSプロジェクトにコントリビュートするときは一貫性を壊さないという話はよくありますが、こういうことなんですね!みなさんも、既存のコードを変更するときは既存のコードの意図をしっかり読み取ってそれに合わせて拡張していってください。
さらに10年経ってPLEASE SELECT
が書けるようになっているので、MySQLはまだまだINTERCALの影響を受け続けていそうですね。
MySQL 8.0対応をしていたらMySQLのおもしろコードを思い出したのでノータブルコードとして紹介しました。みなさんも「お!」と思うコードがあったらノータブルコードとして紹介してみてください。
クリアコードはユーザーがソースコードから学習する自由がある自由なソフトウェアを推進しています。今回のMroongaのMySQL 8.0対応のように業務の一環で自由なソフトウェアのソースコードを読むことも多いです。そんな仕事がいいなと思う人は採用情報を確認してみてください。最近、採用情報ページを刷新したんです!