最近、PostgresSQL互換サーバーを実装する目的で、Go言語向けのPostgreSQL互換サーバー構築用フレームワークである「go-postgresql」と、それを活用したマルチプロトコル/マルチモデルをコンセプトとする「PuzzleDB」を開発する過程で、PostgreSQLのプロトコル実装を調査しました。 以前、RDBMSの代表的なPostgreSQL・MySQLの通信プロトコルの概要を調査[3]をしましたが、今回は、実装におけるポイントや、異なるクライアント実際についても考察し、PostgreSQLプロトコルの実装における柔軟性とその必要性について整理してみます。 メッセージの共通形式 PostgreSQL仕様書では、データベースのクライアント側をフロントエンド(Frontend)、サーバー側をバックエンド(Backend)とした用語の定義があります[3]。 PostgreSQLの通信プロトコルは、フロントエンドからの要求パケット、バックエンドからの応答パケットのいずれにおいても、基本的には、以下に示すパケット仕様に準じています[3]。 PostgreSQLの通信メッセージは、最初の1バイトはメッセージ種別(Cmd)、次の4バイト(Length of message)は自身を含むメッセージ部(Message body)長を示す共通ヘッダから始まります。また、後述するように、クライアントからのコネクション確立時においては、いくつかの例外があります。 メッセージ形式の特徴 特徴としては、MySQL[1]やMongoDB[2]のようにメッセージの識別子および応答の識別子となるメッセージ番号(ID)が含まれないことです。そのため、基本的にはフロントエンドからのメッセージ要求順に、バックエンドからのメッセージを順次応答する形式となります。 PostgreSQLプロトコルによる通信は、要求および応答共に複数のメッセージを合成的に組み合わせます。MySQLにおいては、その組み合わせは厳密に定義[7][8]されていますが、PostgreSQLでは厳密な定義はなく、順次要求されるメッセージおよび、それに対する応答メッセージは、各自の実装において、省略される場合があるのが特徴です。 メッセージの処理フロー PostgreSQLのサーバー側のコネクション確立以降の基本フローについては、公式ドキュメント[4]に概要の説明があります。この公式ドキュメントから今回実装してみた、正常系の主要メッセージを整理すると、以下のような状態マシン図となります。 基本的には後述するスタートアップ時の特殊通信メッセージによる認証および認可終了後は、バックエンド側は、クライアント側からの、静的な文字列である単純なクエリー(Simple Query)と拡張クエリー(Extended Query)などのメッセージ要求を処理をして、待機(Ready)状態への遷移繰り返えすのが基本フローとなります。 スタートアップ時の例外 メッセージの共通形式には例外があり、歴史的な経緯[3]から、クライアントからのコネクション確立時の最初のメッセージはからは、メッセージ種別(Cmd)が省略されています。その例外として、以下に示す、スタートアップメッセージ(StartupMessage)と、SSL要求メッセージ(SSLRequest)があります。 いずれも、共通形式と比較して、最初のメッセージ種別(Cmd)が省略されています。メッセージ種別(Cmd)はありませんが、スタートアップメッセージ(StartupMessage)はメッセージ内容とプロトコルバー順により可変ですが、SSL要求メッセージ(SSLRequest)は固定値であるため、SSL要求メッセージのヘッダ部で識別となります。 SSL要求メッセージの長さは8で固定、SSL要求(Request)部は80877103(=04D2162F)、つまり、1234(0x04D2)と5679(0x162F)の2つのマジックナンバーの固定値による識別となります。いずれにしろ、ネクション確立後のスタートアップは、このような例外的な処理となります。 拡張クエリーの処理フロー PostgreSQLでは、クライアントからの単純なクエリー(Simple Query)要求に加えて、拡張クエリー(Extended Query)が定義されています。拡張クエリー(Extended Query)は、SQL文を複数のステップに分割して送信するクエリー形式です。拡張クエリーにつき、公式ドキュメント[4]から今回実装してみた、正常系の主要メッセージを整理すると、以下のような状態マシン図となります。 拡張クエリプロトコルは上記図の一連のコマンドで実現されますが、フライアント側はバックエンド側の応答を待たずに、一連のクエリを送信するパイプライン処理を可能とします。また、パイプラインの目的としてはメッセージ往復回数が削減があり[4]、実際の実装でもバックエンド側からの応答が省略される場合があります。 プロトコル実装のポイント PostgreSQLプロトコルの実装においては、以下に示す「Frontend/Backend Protocol」公式資料を参照しながら作業を進めます。とは言え、MySQLのように厳密な定義[7][8]はなく、RFC形式のような必須・推奨・任意の定義もなく、実装においては、解釈の余地があります。…
PostgreSQLプロトコル実装に必要な柔軟性
