PostgreSQLからTiDBを利用する

## おことわり
まず、筆者の所属するPingCAP株式会社はこの使い方について言及も推奨もしていないということを明確にしておきます :sweat_smile: 以降の記載は筆者が個人として試したものです。ご利用は自己責任で! 

## PostgreSQLからTiDB使えないんですか？

気持ちは分かります。というかTiDBのストレージレイヤであるTiKVはSQLからは中立ですし、TiDBにしてもSQLをパースしてASTにしてからは比較的SQLの文法的な部分の影響は薄いはず（未確認）です。なのでpgsqlプロトコルとPostgreSQL方言のSQLパーサーがあれば、PostgreSQL互換にもできるはず・・・というところで実際そのような[派生プロジェクト](https://github.com/DigitalChinaOpenSource/TiDB-for-PostgreSQL)も見つけたりしました。

しかし恐らくこのような質問をする方の意図というのは公式リリースのTiDBをPostgreSQLから使いたいということであり、そのような実現方法がないか考えるべきでしょう。いや単に興味あっただけですすいません。

## FDW - Foreign Data WrapperでTiDBを利用する

dbtsで[劔](https://www.nautilus-technologies.com/service/tsurugi.html)や[SCALARDB](https://www.scalar-labs.com/ja/scalardb)の話を聞いたときに、しきりにPostgreSQLのFDWを使っているということを言っていました。調べてみるとpostgreSQLから外部テーブルとして他のDBを利用出来る機能ということじゃないですか！
更に[EDB](https://www.enterprisedb.com/)が作成したmysql用のFDW[mysql_fdw](https://www.enterprisedb.com/docs/mysql_data_adapter/latest/)というものも発見し、これはdbtsコラボ（勝手に）だ！ ということで実際にpostgreSQLからmysql_fdw経由でTiDBを利用できるか試して見ました。

AWSにt3.xlargeのインスタンスを立ち上げ、postgreSQLとTiDB(Playground)を同一のマシンで動かして連携してみます。以降の作業は全てAWSインスタンス上の作業です。EC2にPublic IPを付与して、一台構成で行っています。

### 1. [PostgreSQL] EPAS + mysql_fdwのインストール

EDB Postgres Advanced Server(EPAS) とmysql_fdwのインストールには[EDB repository](https://www.enterprisedb.com/accounts/login)へのアクセスが必要です。mysql_fdwはgithub上でソースも公開されているので、そちらをコンパイルしてPostgreSQLと組み合わせても動くのですが、今回はバイナリインストールすればできる上記の組み合わせで試します。アカウントがない場合はページ下部の`Create EDB Account`から作成できます。

ログイン後、メニューから `Repo Access`を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/8c37f1b1-4224-5ba4-acec-f74ffa188e3d.png) その後に出てくる画面で`Access Repo 2.0`ボタンを押して、リポジトリに進みます。

その後に出てくるOSと製品選択画面で、`Ubuntu 22.04`、`EDB Postgres Advanced Server 16`、`MySQL 8 FDW for EPAS 16` を選択します。リストが膨大なので、Advanced ServerやMySQLで検索すると見つけやすいです。

選択すると画面右側にリポジトリの自動セットアップスクリプトと、インストール手順が表示されます。自動セットアップは個人トークンが含まれるようですのでここでは割愛します。リポジトリが設定できればインストールは簡単です。

```bash
> sudo apt-get -y install edb-as16-server edb-as16-mysql8-fdw
```

なおこのリポジトリからはPostgreSQLとPostgreSQL用のmysql_fdwもインストールできるので、そちらでも同様に動作するものと思いますが未検証です。

インストール後の設定等は[公式ガイド](https://www.enterprisedb.com/docs/epas/latest/installing/linux_x86_64/epas_ubuntu_22/) に記載のものを実施しています。

### 2. [TiDB] TiDBのインストール

TiDBはEDBと同じサーバで動かせるように、一台で動作するTiDB Playgroundを利用します。TiDB Playgroundについては[Advent Calendarの三日目](https://zenn.dev/shigeyuki/articles/8663e1598ecfa5)を参照ください。
インストールは簡単で
```bash
> curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
> tiup playground
```
です。これでTiDBがlocalhostで起動します。

### 3. [TiDB] サンプルテーブルの作成

アクセス確認用に適当なテーブルを作成します。Ubuntuサーバの別のコンソールを開き、TiDBに接続します。
```bash
> tiup client connect
```
適当なテーブルを作成します。ChatGPTに作ってもらいました。
```sql
CREATE TABLE employees (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    position VARCHAR(100),
    salary DECIMAL(10, 2),
    hire_date DATE
);

-- サンプルデータの挿入
INSERT INTO employees (name, position, salary, hire_date)
VALUES
    ('田中 一郎', 'マネージャー', 600000, '2018-06-23'),
    ('鈴木 花子', '開発者', 450000, '2019-07-15'),
    ('佐藤 太郎', 'アナリスト', 500000, '2020-01-10');

```

### 4. [PostgreSQL] Foreign Tableの作成

PostgreSQL側でTiDBを参照するForeign Tableを作っていきます。
初回はまず`mysql_fdw`拡張機能を有効にする必要があります。
```sql
CREATE EXTENSION mysql_fdw;
```
次に、TiDBサーバの情報と、利用するユーザーのマッピングを定義します。今回はお試しなので特権ユーザー同士をマッピングしていますが、実際に運用するときはこの辺ちゃんと考えないといけないでしょう。
```sql
CREATE SERVER mysql_server
         FOREIGN DATA WRAPPER mysql_fdw
         OPTIONS (host '127.0.0.1', port '4000');

CREATE USER MAPPING FOR enterprisedb
    SERVER mysql_server OPTIONS (username 'root', password '');
```
最後に、PostgreSQL側にTiDBと同じテーブルを定義する必要があります。これは自分で定義を合わせて作ってもよいですが(ChatGPTにMySQLのDDL食わせてPostgresに変換を依頼すると簡単にできます。便利ですね！）、TiDB側の定義をIMPORTする手段があるのでそれを利用します。
TiDBのtestデータベースにあるテーブル定義をpublicに読み込んでいます。
```sql
IMPORT FOREIGN SCHEMA test
    FROM SERVER mysql_server INTO public;
```

### 5. [PostgreSQL] クエリ

ここまで問題なくできていれば、後はクエリを投げるだけです。前段の準備も含めた実行結果です。
![20231211-053224.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/42782173-2e55-3ab6-a808-ad8ec9a5dfc2.jpeg)

## まとめ

今回使った道具はどれもTiDBと動かすことは想定していないので、予期しない挙動が発生することは十分に考えられます。
今回使った範囲でも、ローカルのTiDBクラスタは大丈夫なのですが、TLSが必須のTiDB Serverlessでは動作しませんでした。クエリは通っているので恐らくレスポンス返却時の処理のどこかで詰まっている気がしますがちょっとわかりません。

ただこんなことも出来るんだ、というのでちょっと面白かったです。興味のある方はPrivate LinkなどTLSが不要な環境で試して見るとTiDB Cloudとでも動かせるかもしれません。動いたら是非ブログで！