Phoenix1.6でDBにSQLite3を指定した際のコード差異を調べてみた

日本時間の2021年9月25日、ElixirのWebフレームワーク「__Phoenix__」の__バージョン1.6__が、に正式リリースされました。[^rc-0]

[^rc-0]: なお、1.6.0-rc.0版は、2021年8月26日に公開されています。

1.6からの機能[^enhancements]の中で、正式サポートされるデータベースに「__SQLite3__」が追加されています。[^kako]
正確には、データベース接続で利用されるライブラリ「__Ecto__」[^ecto1][^ecto2]がSQLite3をサポートできるようになったから、というものです。

[^kako]: Phoenix 1.2より前のバージョンではSQLiteも扱えていたようですが、Ecto側でsqlite_ectoがEcto 2.x系以降でNGになって以降、Phoenixでも正式サポートはしていなかったよらしい、とのこと。: https://twitter.com/piacere_ex/status/1447419385304420360?s=20

[^enhancements]: https://github.com/phoenixframework/phoenix/blob/master/CHANGELOG.md#160-rc0-2021-08-26

[^ecto1]: https://hexdocs.pm/phoenix/1.6.0/ecto.html#content

[^ecto2]: https://github.com/elixir-ecto/ecto#usage

# コードの差分が気になった

さて、ここからが本題です。
Phoenixでは、データベースの指定はプロジェクトを作成するコマンド`mix phx.new`を実行するタイミングで行います。
たとえば、データベースにPostgreSQLを指定する場合、以下のように`--database`オプションの後に`postgres`と入力[^default_db]します。

```
mix phx.new <アプリケーション名> --database postgres
```

[^default_db]: Phoenixでは、データベースの指定はデフォルトでPostgreSQLが指定されるので、実はこのオプションを指定する必要はないのですが。

そして、その時に指定したデータベースの種類によって、指定したデータベースを扱うために必要なライブラリや設定のスケルトンコードが生成されます。

今回、あらたに「SQLite3」を指定できるようになったので、どこに差分があるかが気になったので調べてみました。

## 各種バージョン

ソース生成に利用した各種バージョンは以下の通りです。
また、コード作成日時は__2021年10月10日__です。

* erlang: 24.1.2
* elixir: 1.12.3-otp-24
* Phoenix: 1.6.2

## 比較対象

デフォルト指定のPostgreSQLとSQLite3のコードを比較してみました。
`postgresql`ディレクトリと`sqlite3`ディレクトリに同一アプリケーション名のプロジェクトを生成。
`--database`オプションの差異で、コードのどこに差異が出るかを`diff`コマンドで比較してみました。

それぞれの生成コマンドは以下の通りです。
アプリケーション名は`teck_fanzine`としています。

* PostgreSQLの場合:`mix phx.new teck_fanzine --database postgres`
* SQLite3の場合:`mix phx.new teck_fanzine --database sqlite3`

## 生成結果の差分

両方のコマンドを実行結果をそのまま比較すると、`〜*.beam`や`_build`ディレクトリ配下のバイナリファイルも差分として出てきます。
そのため、これらのファイルを除いた形でファイルを比較してみました。
結果、以下の内容となりました。

```bash
$diff -qr postgresql/ sqlite3/ | grep -v "beam" | grep -v "_build"
Files postgresql/teck_fanzine/.gitignore and sqlite3/teck_fanzine/.gitignore differ
Files postgresql/teck_fanzine/config/config.exs and sqlite3/teck_fanzine/config/config.exs differ
Files postgresql/teck_fanzine/config/dev.exs and sqlite3/teck_fanzine/config/dev.exs differ
Files postgresql/teck_fanzine/config/runtime.exs and sqlite3/teck_fanzine/config/runtime.exs differ
Files postgresql/teck_fanzine/config/test.exs and sqlite3/teck_fanzine/config/test.exs differ
Only in sqlite3/teck_fanzine/deps: ecto_sqlite3
Only in sqlite3/teck_fanzine/deps: elixir_make
Only in sqlite3/teck_fanzine/deps: exqlite
Only in postgresql/teck_fanzine/deps: postgrex
Files postgresql/teck_fanzine/lib/teck_fanzine/repo.ex and sqlite3/teck_fanzine/lib/teck_fanzine/repo.ex differ
Files postgresql/teck_fanzine/lib/teck_fanzine_web/endpoint.ex and sqlite3/teck_fanzine/lib/teck_fanzine_web/endpoint.ex differ
Files postgresql/teck_fanzine/mix.exs and sqlite3/teck_fanzine/mix.exs differ
Files postgresql/teck_fanzine/mix.lock and sqlite3/teck_fanzine/mix.lock differ
```

上記を整理すると、以下のような分類になります。
※アプリケーション名のルートパス名は省略。また、diffの結果とは一部ファイルの並び順を変更しています。

* postgresの時にだけ生成されている
    * deps/postgrex/ ・・・ PostgreSQLを扱うライブラリ「__postgrex__」[^postgrex]のディレクトリ
* sqlite3の時にだけ生成されている
    * deps/ecto_sqlite3/ ・・・ SQLite3を扱うライブラリ「__ecto_sqlite3__」[^ecto_sqlite3]のディレクトリ
    * deps/exqlite/ ・・・ ecto_sqlite3が必要としているライブラリ「__exqlite__」[^exqlite]のディレクトリ
    * deps/elixir_make/ ・・・ exqliteのruntime時に必要としているライブラリ「__elixir_make__」[^elixir_make]のディレクトリ
* 両方に生成されて、かつ差分があるファイル
    * .gitignore
    * mix.exs
    * mix.lock
    * config/config.exs
    * config/dev.exs
    * config/test.exs
    * config/runtime.exs
    * lib/teck_fanzine/repo.ex
    * lib/teck_fanzine_web/endpoint.ex
    
[^postgrex]: https://github.com/elixir-ecto/postgrex
[^ecto_sqlite3]: https://github.com/elixir-sqlite/ecto_sqlite3
[^exqlite]: https://github.com/elixir-sqlite/exqlite
[^elixir_make]: https://github.com/elixir-lang/elixir_make

### 生成結果の差分：詳細

以下、「両方に生成されて、かつ差分があるファイル」の差分を比較してみます。
diffコマンドベースの結果であるので、差分は「<」と「>」で表示されています。
「<」がPostgreSQL、「>」がSQLite3を指定した場合のファイルです。

#### .gitignore

SQLite3はファイルデータベースなので、「*.db」ファイルをgitリポジトリには保存しないようにしているようです。

```bash
$diff postgresql/teck_fanzine/.gitignore  sqlite3/teck_fanzine/.gitignore
34a35,38
> # Database files
> *.db
> *.db-*
>
```

#### mix.exs

depsで指定するライブラリ名の差分ですね。

```bash
MacBook-Air-3:_diff_project $diff postgresql/teck_fanzine/mix.exs sqlite3/teck_fanzine/mix.exs
39c39
<       {:postgrex, ">= 0.0.0"},
---
>       {:ecto_sqlite3, ">= 0.0.0"},
```

#### mix.lock

具体的なライブラリの差分ですね。ここのバージョンは、`mix.exs`で指定されたライブラリのバージョンによっても依存して変わってくるので、プロジェクトを生成するタイミングによっても内容は変わってきます。

```bash
$ diff postgresql/teck_fanzine/mix.lock  sqlite3/teck_fanzine/mix.lock
10a11,12
>   "ecto_sqlite3": {:hex, :ecto_sqlite3, "0.7.2", "667338c1e0f7af13f75ab9eec13afcea216eb71dac9daf7897c8f0acc8b5722b", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.7", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.6", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "b003804132f183d1d6dc759f6c2ccc60c1fb5d62e1db4aa4fe0d38577096f7c4"},
>   "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
11a14
>   "exqlite": {:hex, :exqlite, "0.7.4", "c35308cf6cac5d18451e4c47a96db7bacf3bec8265a8821bce62bb97b8557412", [:make, :mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "15bc5534d404939fe9ecba29517e3d117a6195c04572b9d31a6c7b4bddfe3e51"},
29d31
<   "postgrex": {:hex, :postgrex, "0.15.11", "50abbb50f33d22d79af402e549b9a566ba4f0451b4f5fd39b72d9bbd49743d24", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "6f0e5c3ea10f97468f5ff852277cb207f068399eb68b0c06c142ef68a4e82952"},

```


#### config/config.exs

ここは、LiveViewで利用するソルトの値の差分でした。
そのため、実質的には、__差分は無い　__ことになります。
※ソルトの値にはマスクをかけました。

```bash
$diff postgresql/teck_fanzine/config/config.exs  sqlite3/teck_fanzine/config/config.exs
18c18
<   live_view: [signing_salt: "xxxxxxxx"]
---
>   live_view: [signing_salt: "XXXXXXXX"]
```


#### config/dev.exs

開発時の設定を記載するファイルです。ここのファイルは差分が大きいですね。
もっとも、DBへの接続に関する情報が書かれているので、差異は大きくはなってしまうのですが。
PostgreSQL側の設定がユーザ名／パスワード名／データベース名／ホスト名などに対して、SQLite3の場合はDBファイル名となっています。

なお、Endpointに対しての「__secret_key_base__」にも差分がありましたが、これも生成のたびに変わるので、実質的には、__差分は無い　__ことになります。
※ソルトの値にはマスクをかけました。

```bash
$diff postgresql/teck_fanzine/config/dev.exs  sqlite3/teck_fanzine/config/dev.exs
5,10c5,7
<   username: "postgres",
<   password: "postgres",
<   database: "teck_fanzine_dev",
<   hostname: "localhost",
<   show_sensitive_data_on_connection_error: true,
<   pool_size: 10
---
>   database: Path.expand("../teck_fanzine_dev.db", Path.dirname(__ENV__.file)),
>   pool_size: 5,
>   show_sensitive_data_on_connection_error: true
25c22
<   secret_key_base: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
---
>   secret_key_base: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
```


#### config/test.exs

テスト実行時の設定を記載するファイルです。ここのファイルもdev.exs同様に差分が大きいですね。
基本的な差分は、dev.exsの時と同じです。
dev.exsとの違いでは、こちらには`Ecto.Adapters.SQL.Sandbox`がありますね。

なお、こちらでもEndpointに対しての「__secret_key_base__」にも差分がありましたが、これも生成のたびに変わるので、実質的には、__差分は無い　__ことになります。
※ソルトの値にはマスクをかけました。

```bash
$ diff postgresql/teck_fanzine/config/test.exs  sqlite3/teck_fanzine/config/test.exs
9,14c9,11
<   username: "postgres",
<   password: "postgres",
<   database: "teck_fanzine_test#{System.get_env("MIX_TEST_PARTITION")}",
<   hostname: "localhost",
<   pool: Ecto.Adapters.SQL.Sandbox,
<   pool_size: 10
---
>   database: Path.expand("../teck_fanzine_test.db", Path.dirname(__ENV__.file)),
>   pool_size: 5,
>   pool: Ecto.Adapters.SQL.Sandbox
20c17
<   secret_key_base: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
---
>   secret_key_base: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
```


#### config/runtime.exs

起動時に読み込まれる`runtime.exs`にも差分がありました。
ここには、DBの本番用設定（＝prodの場合の設定）があらかじめ出力されており、読み込むための環境変数の差異が出ているようです。
PostgreSQLの際には、「__DATABASE_URL__」が、SQLite3の場合は「__DATABASE_PATH__」を設定することになるようです。
ただし、これはあくまでテンプレートとして出力されている内容なので、環境変数名を変更することもできるでしょう。

```bash
$ diff postgresql/teck_fanzine/config/runtime.exs  sqlite3/teck_fanzine/config/runtime.exs
10,11c10,11
<   database_url =
<     System.get_env("DATABASE_URL") ||
---
>   database_path =
>     System.get_env("DATABASE_PATH") ||
13,14c13,14
<       environment variable DATABASE_URL is missing.
<       For example: ecto://USER:PASS@HOST/DATABASE
---
>       environment variable DATABASE_PATH is missing.
>       For example: /etc/teck_fanzine/teck_fanzine.db
18,21c18,19
<     # ssl: true,
<     # socket_options: [:inet6],
<     url: database_url,
<     pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
---
>     database: database_path,
>     pool_size: String.to_integer(System.get_env("POOL_SIZE") || "5")
```


#### lib/teck_fanzine/repo.ex

データストアに対するAPIを提供する設定が、「Repo」（Ecto.Repo）[^repo]になります。
[^repo]: Repoは「Repositories」からきているようです。
その設定の中で、アダプター（adapter）の設定が、利用するデータベースによって違いが出ています。

```bash
$diff postgresql/teck_fanzine/lib/teck_fanzine/repo.ex sqlite3/teck_fanzine/lib/teck_fanzine/repo.ex
4c4
<     adapter: Ecto.Adapters.Postgres
---
>     adapter: Ecto.Adapters.SQLite3
```


#### lib/teck_fanzine_web/endpoint.ex

sessionで利用するソルト「__signing_salt__」にも差分がありましたが、これも生成のたびに変わるので、実質的には、__差分は無い　__ことになります。
※ソルトの値にはマスクをかけました。


```bash
$diff postgresql/teck_fanzine/lib/teck_fanzine_web/endpoint.ex  sqlite3/teck_fanzine/lib/teck_fanzine_web/endpoint.ex
10c10
<     signing_salt: "xxxxxxxx"
---
>     signing_salt: "XXXXXXXX"
```

## まとめ

調査の結果、実質的な差分があるファイルは以下とわかりました。

* .gitignore
* mix.exs
* mix.lock
* config/dev.exs
* config/test.exs
* config/runtime.exs
* lib/teck_fanzine/repo.ex

今回は、初期生成されたコードに対しての差分を確認してみただけですが、それでも差分からでもわかってくる内容もありました。

SQLite3を本番環境で利用する機会は少ないでしょうが、やはりざっとデータベースを利用するアプリを作る時にはSQLite3を手軽に利用できるのは良いですね。
せっかく差分を調べたので、この勢いでSQLite3を利用したアプリケーションのコードも書いてみようと考えています。