Let's start Scheme

2015-12-13

S式SQL その3

なんとなく必要そうな部分が動くようになってきた。SQL 2003のBNFをほぼそのままSchemeにした形なので無駄に冗長かつ、こんなのいつ使うんだ?的な構文までサポートされている。ほとんどyak shavingに近いような感じで時間だけ取られ、モチベーションを保つのが大変だった。おかげで役に立たなさそうなSQLの構文的な知識が増えた気もする。まぁ、すぐに忘れるだろうけど。

とりあえず、こんな感じで使える。
(import (rnrs) (text sql))

(define sql "select * from t")

(sql->ssql (open-string-input-port sql))
;; -> (select * (from t))
これくらいだとCLにあるSQL扱うのとそんなに変わらない形式のS式なのだが(あれらは基本マクロなので、こんな風に取り出せないけど)、いくらか直感的ではない感じのものがある。例えば以下:
(import (rnrs) (text sql))

(define sql "select * from t inner join a using (id)")

(sql->ssql (open-string-input-port sql))
;; -> (select * (from (t (inner-join a (using id)))))
SxQLだと上記のは
(select :* (from :t) (inner-join :a :on (:= :t.id a.id))) 
見たいな風に書ける(はず、README.markdownから推測しただけなので自信ない)。これは理由があって、FROM句の中にJOIN句を入れた方がSQLのBNF的には楽にパースできたのと、こんなのも有効なSQLだったから:
select * 
from t inner join a using (id)
   , b /* who the heck would write like this? */
SxQL的な記法だと上記が書けないなぁと思ったので、涙を飲んだ。この辺は用途の違いなんだけど、既存のS式SQLはS式からSQLを出力できればよいというものであるのに対して、僕のは既存のSQLをS式に変換するという用途が必要だったから。理由はS式SQLにあるのでそっち参照。単に全てをS式のみで終わらせられる世界の住人ではないというだけだが。INSERT節のVALUES句もそんな感じで直感的ではないものになってる。

パーサがSELECT、INSERT、UPDATEとDELETEをサポートした段階でS式SQLからSQL文字列を取り出すようなのも作った。こっちはかなり簡単で、パターンマッチとマクロを駆使してひたすらゴリゴリ書くだけ。大変なのはSQLに定義されてるほぼ全ての演算子を書かないといけない点。まだ全部は終わってないけど、必要な分からやれるのでそんなに大変でもない。(逆に漏れが出る可能性がパーサより高い・・・)


いくつか宣伝できそうなところ

これが宣伝になるとも思えないけど、パースしたS式SQLはかなり冗長になっているので多少の簡素化を行うようにしている。
  • 識別子の連結
  • Unicode文字列のデコード
  • likesimilar toESCAPE演算子
最初のはパースしただけの状態だとa.b.c(~ a b c)となるので、これをa.b.cというシンボルにする。これはUnicodeやdelimited識別子もいい感じに扱ってくれる。ただ、書き出す際に大文字小文字の情報を失うので、delimitedな識別子はちょっと考える必要があるかもしれない。
二つ目のはU&で始まる識別子もしくは文字列に含まれるUnicodeエスケープをいい感じに文字にするもの。現状surrogate pairとかは全く考慮しない(integer->charするだけな)ので際どい系の文字は危ないかもしれないが。
三つ目のはあんまり使われることがなさそうなESCAPE演算子の除去。

もう少し何かできそうな気がするけど、思いつかなかったのでこれだけ。

ここまでできたので後は使いながら調整していく感じになりそう。0.7.0辺りでドキュメント化できたら嬉しいが、もう少し後になる気がしないでもない。

No comments:

Post a Comment