SQLパーサー作成メモ

備忘録メモ

Postgresql内部実装のSQLパーサー

https://wiki.postgresql.org/wiki/Query_Parsing

raw_parserという関数があるらしい。

postgres/parser.c at 9d4649ca49416111aee2c84b7e4441a0b7aa2fac · postgres/postgres · GitHub

構文別にノードツリーレベルまで展開してくれるが、おそらく意味解析は行われない。 自分は意味解析までほしいのでもうちょっと実装しないといけないが、いい感じにparseしてくれるなら使うかもしれない。

識別子の判別タイミング

Postgresqlは、date など普通はキーワードだろ?という単語も列名にできる。以下は、テーブル定義によってはエラーとならない。

SELECT date FROM ordertransaction;

構文の評価

例えばSQLにおいて、 ( ) は様々な意味合いを持つ。

  • 関数の引数部
  • サブクエリの始点終点
  • 式の結合優先度を決めるだけの演算子
SELECT * FROM (
  SELECT clientid, orderno, orderym, RANK() OVER(PARTITION BY clientid, orderym ORDER BY orderno) AS idx FROM ordertransaction
) Q ORDER BY clientid, idx;

パーサーは前から順に読み込み構文を判断する。 しかし ( ) のように一節を読み切った後に前まで読んでいた式をどう評価すべきか不確定なときがある。

RDBの実装を見たことはないが、おそらくメタメタなパターンマッチングを行わなければいけない気がする。

SELECT count(*) FROM master;
  1. SELECT まで読む → 構文のはじまりはSELECT、INSERT、WITHなど固定のキーワードから始まるため、これはSELECT句で確定
  2. count まで読む → SELECTのあとは、DISTINCTなどのキーワードか、各抽出列の指定(サブクエリ、関数、項目名、定数など)をとりえる。この時点では、count が列名なのか、関数名なのか不明(謎の識別子状態)
  3. ( まで読む → ここで count が関数名だと確定する?
  4. * まで読む → 各抽出列の指定(サブクエリ、関数、項目名、定数など)か、全列を指定するキーワード * をとりえる。パターンマッチングで唯一である * が確定 ...

単なる列指定するところでもサブクエリで複数階層化できるところがメタメタに難しそう。 いい感じの関数のインタフェース考えながら、EBNFとにらめっこして作ることになりそう