6.3 - DieselでPostgreSQLに接続する
DieselでPostgreSQLに接続する
データベースが稼働しているので、このセクションではこのデータベースへの接続を構築します。そのために、DIESEL CRATEを使用します。Dieselクレートは、Rustコードでデータベースへの接続を可能にします。他のクレートではなく、dieselクレートを使っているのは、dieselクレートが最も確立されており、多くのサポートとドキュメントを持っているからです。これを実現するために、以下のステップを踏んでみましょう。
- まず、diesel crateを利用します。そのために、cargo.tomlファイルに以下の依存関係を追加します。
diesel = { version = "1.4.8", features = ["postgres", "chrono", "r2d2"] }
dotenv = "0.15.0"
chrono = "0.4.19"
先のコードでは、diesel crateにpostgresの機能を入れています。diesel crateの定義には、chronoとr2d2機能も含まれています。chrono機能により、Rustのコードでdatetime構造体を利用できるようになります。r2d2機能では、コネクションプールを実行することができます。コネクションプーリングについては、本章の最後で説明します。また、dotenvクレートも含まれています。このクレートは、.envファイルで変数を定義し、それをプログラムに渡すことができます。これを使用して、データベースの認証情報を渡し、さらにプロセスに渡すことにします。
- ここで、アプリではなくターミナルからデータベースへのマイグレーションを実行するために、Dieselクライアントをインストールする必要があります。次のコマンドでこれを行うことができます。
- 次に、環境DATABASE_URL URLを定義する必要があります。これにより、クライアント・コマンドが以下のコマンドでデータベースに接続できるようになります。
先のURLでは、ユーザー名をユーザー名、パスワードをパスワードと表記しています。データベースは自分のコンピュータ上で動作しており、localhostと表記され、データベースはto_doと呼ばれています。これにより、ルートファイル内に以下の内容を出力する.envファイルが作成されます。
- これで変数が定義されたので、データベースのセットアップを開始することができます。docker-compose upコマンドで、docker-composeでデータベースコンテナを立ち上げる必要があります。そして、以下のコマンドでデータベースをセットアップします。
そして、先のコマンドで、ルートに以下のような構造のmigrationsディレクトリが作成されます。
up.sqlファイルはマイグレーションがアップグレードされたときに、down.sqlファイルはマイグレーションがダウングレードされたときに発行されるファイルです。
- さて、ToDo項目を作成するためのマイグレーションを作成する必要があります。これは、次のコマンドでマイグレーションを生成するようにクライアントに命令することで可能です。
これを実行すると、コンソールに以下のような印字が出るはずです。
Creating migrations/2022-04-23-201747_create_to_do_items/up.sql
Creating migrations/2022-04-23-201747_create_to_do_items/down.sql
先のコマンドにより、マイグレーションでは以下のようなファイル構成になります。
├── migrations
│ ├── 00000000000000_diesel_initial_setup
│ │ ├── down.sql
│ │ └── up.sql
│ └── 2022-04-23-201747_create_to_do_items
│ ├── down.sql
│ └── up.sql
ディーゼルクレートでは、独自のSQLファイルを作成する必要があるため、額面上は不幸に思えるかもしれません。しかし、これは強制的に良い習慣です。crateが自動的にSQLを書くようにするのは簡単ですが、これは私たちのアプリケーションとデータベースを結合させるものです。例えば、かつて私はマイクロサービスシステムのリファクタリングに取り組んでいたことがあります。私が抱えていた問題は、データベースのマイグレーションをすべてPythonパッケージで管理していたことでした。しかし、私はサーバーのコードを変更したいと思いました。私がサーバーをPythonからRustに変えようとしたことを聞いても、皆さんは驚かないでしょう。しかし、移行はPythonライブラリによって自動生成されたため、私は、今日に至るまで、新しいリリースが行われたときにスピンアップするヘルパーDockerコンテナを構築し、構築時にデータベースのスキーマをRustアプリケーションにコピーする処理を行う必要がありました。これは面倒なことです。これはまた、私が金融技術の研究開発ソフトウェアエンジニアだったときに、なぜすべてのSQLを手動で書かなければならなかったかを私に正当化するものでした。
データベースは、アプリケーションとは別のものです。このため、両者を隔離しておく必要があり、SQLを手動で書くことが障害になるとは考えません。将来的に頭痛の種にならないような良いスキルを学んでいるのだから、受け入れよう。1つのツールを無理に使うのではなく、適切な仕事に適切なツールでなければならないことを忘れてはいけません。create to-do items migrations フォルダで、up.sql ファイルに以下の SQL エントリを追加して、to_do テーブルを定義します。
CREATE TABLE to_do (
id SERIAL PRIMARY KEY,
title VARCHAR NOT NULL,
status VARCHAR NOT NULL,
date timestamp NOT NULL DEFAULT NOW()
)
先のコードでは、アイテムのidがあり、これは一意である。そして、タイトルとステータスがあります。また、デフォルトでは、ToDo項目がテーブルに挿入された現在の時刻を持つ日付フィールドを追加しています。これらのフィールドは、CREATE TABLEコマンドにまとめられています。down.sqlファイルでは、次のSQLコマンドで、マイグレーションをダウングレードする場合にテーブルを削除する必要があります。
さて、up.sqlとdown.sqlファイルのSQLコードを書いたので、このコードが何をするのかを次の図で説明します。
図6.4 「up.sqlとdown.sqlのスクリプトの効果
- これでマイグレーションの準備が整いましたので、以下のターミナルコマンドでマイグレーションを実行します。
先のコマンドは、to_doテーブルを作成するマイグレーションを実行します。時には、SQLで異なるフィールドタイプを導入することがあります。これを修正するには、up.sqlとdown.sqlファイルのSQLを変更し、次のredoターミナルコマンドを実行します。
先のコマンドは、down.sqlファイルを実行した後、up.sqlファイルを実行します。
- 次に、データベースDockerコンテナでコマンドを実行して、定義した正しいフィールドを持つto_doテーブルがデータベースにあることを検査します。これは、データベースDockerコンテナ上で直接コマンドを実行することで可能です。ユーザー名usernameでコンテナに入り、以下のターミナルコマンドを使用してto_doデータベースをポイントすることができます。
注意しなければならないのは、先のコマンドで、私のコンテナIDは5fdeda6cfe43ですが、あなたのコンテナIDは異なるということです。正しいコンテナIDを入力しないと、正しいデータベースで正しいデータベースコマンドを実行することができません。このコマンドを実行すると、次のようなプロンプトが表示されたシェル・インターフェイスが表示されます。
先ほどのプロンプトの後、"˶‾‾‾"と入力すると、データベースに接続されます。これは通常、to_doデータベースにユーザー名userで接続されているという文で示される。
Schema | Name | Type | Owner
--------+----------------------------+----------+----------
public | __diesel_schema_migrations | table | username
public | to_do | table | username
public | to_do_id_seq | sequence | username
先の表から、データベースの移行バージョンを記録するためのmigrationsテーブルがあることがわかる。
- また、to_doテーブルとto_doアイテムIDのシーケンスも用意されています。スキーマを調べるには、" \d+ to_do "と入力すれば、ターミナルに以下のスキーマが表示されます。
Table "public.to_do"
Column | Type | Collation |
--------+-----------------------------+-----------+
id | integer | |
title | character varying | |
status | character varying | |
date | timestamp without time zone | |
| Nullable | Default | Storage |
+----------+-----------------------------------+----------+
| not null | nextval('to_do_id_seq'::regclass) | plain |
| not null | | extended |
| not null | | extended |
| not null | now() | plain |
Indexes:
"to_do_pkey" PRIMARY KEY, btree (id)
先ほどの表で、スキーマが設計通りになっていることがわかります。日付の列でもう少し情報を得ることができ、ToDo項目が作成された時刻を得ることができないことが明確になりました。移行がうまくいったので、次のステップでは、移行がどのようにデータベースに表示されるかを調べてみましょう。
- 次のSQLコマンドを実行することで、migrationsテーブルを検査することができます。
先のコマンドで、次のような表が出力されます。
version | run_on
----------------+----------------------------
00000000000000 | 2022-04-25 23:10:07.774008
20220424171018 | 2022-04-25 23:29:55.167845
このように、データモデルを更新した後にマイグレーションを実行するのを忘れてしまうことがあるため、これらのマイグレーションはデバッグに役立ちます。さらに詳しく調べるために、Dockerコンテナの外で次のコマンドを実行すると、前回のマイグレーションを元に戻すことができます。
その後、ロールバックが実行されたことを知らせる次のようなプリントアウトが得られました。
これでマイグレーションがロールバックされたので、マイグレーションテーブルは以下のようなプリントアウトになります。
version | run_on
----------------+----------------------------
00000000000000 | 2022-04-25 23:10:07.774008
先のプリントアウトでは、最後のマイグレーションが削除されていることがわかります。したがって、migrationsテーブルはログではないことが推測できます。現在アクティブなマイグレーションを追跡しているだけです。マイグレーションの追跡は、ディーゼル・クライアントがマイグレーションを実行する際に、正しいマイグレーションを実行するための助けとなる。
このセクションでは、Dockerコンテナ内のデータベースに接続するために、Dieselクライアントを使用しました。そして、環境ファイルでデータベースのURLを定義しました。次に、いくつかのマイグレーションを初期化し、データベース内にテーブルを作成しました。さらに、Dockerコンテナに直接接続し、そこでさまざまなコマンドを実行してデータベースを探索することができました。これで、ターミナルにあるクライアントからデータベースが完全に対話できるようになったので、Rustアプリがデータベースと対話できるように、ToDo項目データベースモデルの構築を開始します。