複数ステートメントを同一セッションで通すための拡張
集計業務で長く使われてきた .sql ファイル群がある。1ファイルで1つの集計が閉じているものもあれば、中間テーブルの DROP / CREATE / INSERT や、複数の集計を一連で処理する流れが、1ファイルにまとめて書かれているものもある。これらは、日々の集計を回してきた人たちが、自分たちの手に馴染むかたちで積み上げてきた資産である。ファイルを開いて読み、必要があれば書き足す。差分を追うのも、レビューをするのも、ファイル単位で行われていることが多い。
この .sql 群を Python から実行する機会があり、ひとつの段差に出会った。SQL ドライバが、1回の実行呼び出しで複数ステートメントをそのまま受け取れない。複数ステートメントが並んだ .sql を素直に渡すと、最初の1文しか走らないか、ドライバ側でエラーになる。選択肢はふたつあった。ひとつは、ドライバの前提に合わせて .sql ファイルを分割し直す形。もうひとつは、ファイルの形はそのまま残し、呼び出し側でステートメントを分解して順に実行する形。採ったのは後者だった。.sql の側に積み上げられてきた作業の単位を、実行経路の都合で崩すことに、合理性がない ── この見立てが、以降の判断を通す軸になった。
判断1: ステートメント分割は、呼び出し側に寄せる
軸に素直に従えば、.sql の形を変えないまま、実行単位だけを呼び出し側で調整することになる。採ったのはその形で、.sql ファイルを読み込んだあと、呼び出し側の Python でステートメント単位に分解してからドライバに渡すことにした。
ここで、単純な split(';') は採れなかった。文字列リテラル、行コメント、ブロックコメントの内部に現れる ; を境界として拾うと、文の途中で誤って切れる。分割の処理は、これらをまたぐ ; を除外した上で境界を決める形にした。
この段階では、.sql ファイル側には手を入れていない。
判断2: 分解したステートメントは、同一セッションで順に実行する
ステートメント単位に分解できたとして、次に決める必要があったのは、それらを1つのセッションで順に流すか、それぞれ独立のセッションで実行するか、だった。
別々のセッションに分ける構成も一度は検討した。分解した後のステートメントは、形の上ではそれぞれ独立した SQL として実行できる。ただし、.sql ファイルの中身を見ていくと、前のステートメントが用意した中間テーブルを後続のステートメントが参照する構成が含まれていた。セッションを分けると、一時テーブルやセッション内の参照関係が切れる。
一時テーブルや SET したセッション変数は、同じ接続の中でだけ生きている。SQL の世界では、この範囲が一つの作業単位として扱われていることが多い。今回の .sql ファイル群も、その扱いの上に書かれていた。
.sql ファイル内のまとまりは、ステートメントの並びだけでなく、参照の生存範囲まで含んだまとまりである。ファイルに畳み込まれているのは、構文の並びだけではなく、セッションという実行文脈を共有しているという前提でもある。
実行順だけでなく、参照の生存範囲も .sql ファイル内のまとまりに合わせる必要がある。分解したステートメントは、同一セッションのカーソル上で順に実行する形になった。
判断3: .sql ファイル自体の分割は、選択肢に入れない
ここまでの判断はどれも、「.sql の形は変えない」という前提の上に載っている。そもそも .sql ファイル群を複数ファイルへ分割し直す案は、別の選択肢としてあり得るので、これも一度外から見ておく必要があった。
ファイルを分けてしまえば、ドライバ側の前提(1実行 = 1ステートメント)にそのまま合わせられる。呼び出し側のステートメント分割も要らなくなる。ただ、ファイルを分けると、どこで分けたか、どの順に呼ぶかという知識が、ファイル群の側ではなく、呼び出し順を管理する別の場所へ移る。.sql 側にまとまっていた処理単位が崩れ、順序依存の把握も分散する。
今回の対象では、ファイルの単位がそのまま作業の単位になっていた。この重なりが、.sql 群がここまで運用されてきた土台になっていた。この単位を外して、実行単位だけをそろえるのは、割に合わない。
軸 ── .sql の側の単位を、実行経路の都合で崩す理由はない ── は、外側から改めて確かめられた。
拡張後の実行単位
.sql ファイルはファイル単位で読み込まれ、内部ではステートメント単位に分解される。実行は同一セッション上で順に進み、ファイル側の分割境界や記述順はそのまま保持される。
ドライバの呼び出し側を拡張することで、.sql を書き、読み、差分を追う側の作業単位は保たれる。
