[Elixir]EctoのQueryを少し読み解く ~ コードに前提条件が書かれるので読み解きやすいですね ~

Ectoは、Elixirが標準として提供するDBへの接続ライブラリです。macroを学んだので、少しmacroを読んでみよう、ということで読んでみました。

ここでは、すべてEcto v0.16.0を元にしています。

Ecto.Queryの補完を見ればわかるのですが、この下にfromなどのSQL構文を持っています。

iex(1)> Ecto.Query.
API             Builder         CompileError    JoinExpr
Planner         QueryExpr       SelectExpr      Tagged
__struct__/0    distinct/3      exclude/2       from/2
group_by/3      having/3        join/5          limit/3
lock/2          offset/3        order_by/3      preload/3
select/3        update/3        where/3

これらを使い、例えば、 Sample.User というリポジトリに含まれる要素に対してSQLを発行したい場合に以下のようにSQLを発行することができます。

iex> from(
       users in Sampe.User,
       where: users.count < 1,
       select: users.user,
       limit: 1)
     |> Sample.Repo.all
[debug] SELECT v0."user" FROM "votes" AS v0 WHERE (v0."count" < 1) LIMIT 1 [] OK query=1.1ms
["user2"]

これらを読んでいこうとすると、まずは以下の fromに関するdefmacro に出会います。

このコードを読んでいくと、このmacroの先で幾つかの条件に分けれられた from が定義されています。

  • defp from([{type, expr}|t], env, count_bind, quoted, binds) when type in @binds do
  • defp from([{type, expr}|t], env, count_bind, quoted, binds) when type in @no_binds do
  • defp from([{join, expr}|t], env, count_bind, quoted, binds) when join in @joins do

ここで、whenにより type@binds @no_bind @joins でそれぞれ処理が分けられていることがわかります。これは何かな?と追ってみると、

https://github.com/elixir-lang/ecto/blob/v0.16.0/lib/ecto/query.ex#L250

に以下のようにリストによる定義があります。

  @binds    [:where, :select, :distinct, :order_by, :group_by,
             :having, :limit, :offset, :preload, :update]
  @no_binds [:lock]
  @joins    [:join, :inner_join, :left_join, :right_join, :full_join]

推測するに、SQLで使われる表現を区分しているようですね。それらは、 lib/ecto/query.ex の同じ階層のコードを覗くと from と同じようにdefmacroで定義されていることがわかります。

つまるとこ、 コードがそのままSQL構文になるようにElixirの文法をmacroで拡張している のがこのqueryの箇所の役割のようです。

他にも幾つかwhenにより定義があるのですが、何も合致しない場合は以下が呼ばれるらしいですね。

  • defp from([], _env, _count_bind, quoted, _binds) do

もう少し読み進めてみます。それぞれのdefmacroの処理の中では xxx.build が呼ばれていることがわかります。これの先に escape などのQueryに対するエスケープ処理などが実装されています。

例えば、 where はこのコードの箇所Filter.build が呼ばれます。Ecto.Query.Builderescape とかはこちら、個別の定義の例えば Ecto.Query.Builder.Select なんかで呼ばれています。

ここまで読んでいると、Elixirは関数の定義の段階で その処理の前提条件 がコードに記述されているので良いですね。個人的に、「この関数は何に焦点を当てて読めば良いのか?」、ということに集中できるので読みやすいです。

ここら辺のショートカットになる関数も用意されているので、すべてがこの記述になるわけではないですが、Elixirの拡張としてmacroを使ったEctoの話でした。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s