構文解析過程は2つの部分から構成されています。
gram.y
とscan.l
で定義されているパーサは、Unixのツールbisonとflexを使って構築されます。
変換プロセスは、パーサから返されたデータ構造の変更や追加を行います。
パーサは、(平文のテキストとして渡される)問い合わせ文字列が正しい構文になっているかチェックしなければいけません。 もし構文が正しい場合は構文解析ツリーが作られて返されます。 正しくない場合はエラーが返されます。 パーサと字句解析はUnixでよく知られたツールのbisonとflexを使用して実装されています。
字句解析はファイルscan.l
で定義され、識別子やSQLキーワードなどの確認を担当します。
検出された全てのキーワードや識別子に対しトークンが生成されパーサに渡されます。
パーサはファイルgram.y
の中で定義され、文法ルールとルールが実行された時に実行されるアクションの組から構成されています。
アクションのコード(実際はC言語コードです)は構文解析ツリーを作るのに使われます。
ファイルscan.l
はプログラムflexを使ってCのソースファイルscan.c
に変換されます。
そしてgram.y
はbisonを使ってgram.c
に書き換えられます。
これらの書き換えが終わると、パーサを作るために通常のCコンパイラが使えるようになります。
生成されたCのファイルには絶対に変更を加えないでください。
と言うのは次にflexもしくはbison が呼ばれた時に上書きされるからです。
ここで言及した書き換えやコンパイルは通常PostgreSQLのソースと一緒に配布されるmakefileを使って自動的に行われます。
bisonまたはgram.y
で定義される文法ルールの詳細は本稿では説明しきれません。
flexやbisonについては本や資料がたくさん出ています。
gram.y
の文法の勉強を始める前にbisonの知識が必須となります。
その知識なしではそこで何が起こっているのかを理解することは難しいでしょう。
構文解析過程ではSQLの構文構造に関する固定ルールのみを使って構文解析ツリーを作成します。 システムカタログの参照を行わないので、要求されている操作の詳細な語義は理解しません。 構文解析が終わった後に入力としてパーサから戻されたツリーを書き換えプロセスが引き受け、どのテーブル、関数、そして演算子が問い合わせによって参照されているのかの判断に必要な語義翻訳を行います。 この情報を表すために作成されるデータ構造を問い合わせツリーと呼びます。
語義解釈と入力の構文解釈を切り分ける理由は、システムカタログの参照はトランザクション内でのみ行うことができますが、問い合わせ文字列を受け取ってすぐにトランザクションを開始することは好ましくないと考えられるからです。
入力に対する構文解析過程ではトランザクション管理コマンド(BEGIN
、ROLLBACK
など)を特定するだけで十分であるとともに、それ以上の分析を行わなくても正しい処理が実行されます。
実際の問い合わせ(例えばSELECT
もしくはUPDATE
)に関わっていると言うことがわかっていて既にあるトランザクション内にいなければ新規トランザクションを開始することは問題ありません。これ以降に限り書き換えプロセスを起動することができます。
書き換えプロセスで作成された問い合わせツリーはほとんどの箇所で加工されていない構文解析ツリーに構造的には似ていますが、細部では数多くの相違が存在します。
例えば、構文解析ツリーのFuncCall
ノードは構文的には関数呼び出しのように見える何かを表わしています。
これは参照された名前が通常の関数になるか集約関数となるかによってFuncExpr
もしくはAggref
に書き換えられることがあります。
さらに、列の実際のデータ型と式の結果についての情報が問い合わせツリーに書き加えられます。