この記事は、Vim Advent Calendar 2011 の29日目の記事です。
Webアプリの開発をしていると、少し複雑なSQLを書かなきゃならないとか、開発用のデータを入れ替えたいなどSQLを実行したいと思うシチュエーションが多々あります。そんなとき、皆さんはどのような方法でSQLを実行してるでしょうか?シェルからコマンドを叩いてみたりphpMyAdminのようなWebベースのDBMSを使ったりといくつか方法があると思います。
私はシェルからコマンドを起動することが多いのですが、ほんの一瞬であってもVimから離れてシェルに戻るのが苦痛です。また、DBMSを使う場合でもVimから離れてSQLの編集を行わなければならなかったり、VimでSQLファイルを編集してからそれをDBMSにもっていったりと何かと煩雑な作業が入るのが辛いです。
そんな私のようなVimmerのためにVim内からSQLを実行するためにdbextというすばらしいプラグインがあります。このプラグインについてはここの記事などで詳しく解説されています。対応しているDBも多く機能も豊富です。
しかし、私はちょろっとSQLを実行して結果を確認してみたいだけなのであまり使うか使わないかわからない機能がごてごてあるのは好きではありません。かといってプラグインを自作するとバッファ管理やなんやらとメイン部分以外に書かなくてはいけないコードが結構多くなり、コスト対効果が悪くなくなってきます。
なんかいい方がないかと考えていたところ、普段からお世話になっているQuickRunを設定すればいけそうなことに気がつきました。
というわけで、vimrcにQuickRunでSQLを実行するための設定を追加してみました。今回は、
- MySQLのみ対応
- .sqlの拡張子を持つファイルを実行できるようにする
- コードに埋め込まれたSQLを実行できるようにする
という内容で設定しました。設定自体は短いのですが、せっかくなので最終形にたどり着くまでにたどった過程をさらしてみたいと思います。
.sqlファイルを実行できるようにする
はじめに、vimrcにQuickRunのsql用の設定を追加します。接続設定などはこの段階ではとりあえず設定の中に埋め込んでしまい、QuickRunからmysqlコマンドを実行する一連の流れが正常に動作することを確認します。
let g:quickrun_config = {} "他でquickrunの設定をしていない場合は、この行が必要です。 | |
let g:quickrun_config['sql'] = { | |
\ 'command': 'mysql', | |
\ 'exec': ['%c -u username -pmypassword testdb < %s'], | |
\ } |
commandの'mysql'は自分の環境にあわせて変更して下さい。また、"username"、"mypassword"、"testdb"については適宜自分の環境にあわせて変更して下さい。
この設定で重要なのは、execの%cはcommandで指定したパスに置き換わり%sはQuickRunが生成する一時ファイルへのパスに置き換えられるという点です。
これで.sqlという拡張子がついたファイルに"show tables;"のようなSQLを書いてQuickRunを起動すれば実行結果が表示されます。QuickRun便利すぎですね。
接続設定を切り離す
次に、さきほどの設定に埋め込んだユーザー名、パスワード、DB名などを変数として定義できるようにして柔軟性を持たせます。
let g:quickrun_config = {} "他でquickrunの設定をしていない場合は、この行が必要です。 | |
let g:quickrun_config['sql'] = { | |
\ 'command': 'mysql', | |
\ 'exec': ['%c %o < %s'], | |
\ 'cmdopt': '%{MakeMySQLCommandOptions()}', | |
\ } | |
let g:mysql_config_host = '' | |
let g:mysql_config_port = '' | |
let g:mysql_config_user = 'root' | |
function! MakeMySQLCommandOptions() | |
if !exists("g:mysql_config_host") | |
let g:mysql_config_host = input("host> ") | |
endif | |
if !exists("g:mysql_config_port") | |
let g:mysql_config_port = input("port> ") | |
endif | |
if !exists("g:mysql_config_user") | |
let g:mysql_config_user = input("user> ") | |
endif | |
if !exists("g:mysql_config_pass") | |
let g:mysql_config_pass = inputsecret("password> ") | |
endif | |
if !exists("g:mysql_config_db") | |
let g:mysql_config_db = input("database> ") | |
endif | |
let optlist = [] | |
if g:mysql_config_user != '' | |
call add(optlist, '-u ' . g:mysql_config_user) | |
endif | |
if g:mysql_config_host != '' | |
call add(optlist, '-h ' . g:mysql_config_host) | |
endif | |
if g:mysql_config_pass != '' | |
call add(optlist, '-p' . g:mysql_config_pass) | |
endif | |
if g:mysql_config_port != '' | |
call add(optlist, '-P ' . g:mysql_config_port) | |
endif | |
if exists("g:mysql_config_otheropts") | |
call add(optlist, g:mysql_config_otheropts) | |
endif | |
call add(optlist, g:mysql_config_db) | |
return join(optlist, ' ') | |
endfunction |
この設定では、さきほどexecに埋め込んでいたオプションを%oに置き換えて、新たにcmdoptというキーを追加しました。%oはcmdoptで指定された式の評価結果によって置換されます。cmdoptはかなり柔軟な設定ができるのですが、今回はMakeMySQLCommandOptions()という関数を定義しその返り値を渡すように設定しました。
MakeMySQLCommandOptions()関数は、
- ホスト名(g:mysql_config_host)
- ポート番号(g:mysql_config_port)
- ユーザー名(g:mysql_config_user)
- パスワード(g:mysql_config_pass)
- DB名(g:mysql_config_db)
のうち未定義のものがあればプロンプトから入力させ、定義されている場合はそれを使ってmysqlコマンドに渡すオプションを組み立てます。また、上記設定以外にmysqlコマンドへ渡したいオプション(ソケットなど)がある場合はg:mysql_config_otheroptsに設定します。
以上で接続情報の設定は完了です。
コードに埋め込まれたSQLを実行できるようにする
最後に、コードに埋め込まれたSQLを実行するための設定をします。もともとQuickRunは選択したコードだけ実行するという機能があるので、ビジュアルモードでSQLを選択して
:'<,'>QuickRun sql
と入力すれば選択した部分をSQLとして実行することができます。毎回入力するのが面倒であれば、
noremap <silent> <Leader>qs :QuickRun sql<CR> |
などとキーマッピングを設定しておけばSQLとして明示的にQuickRunを起動することができます。
まとめと今後の課題
とりあえず、以上でQuickRunからSQLは実行できるようになったのですが実際のところコードに埋め込まれたSQLは、
SELECT id, title, created
FROM posts
WHERE created BETWEEN ? AND ?
のようにプレースホルダーが含まれている場合がほとんどです。場合によっては、$beginやobj.method()のような変数や式が埋め込まれている可能性もあります。最初に紹介したdbextだとこの問題も解決されています。QuickRunを使ってこの問題を解決できるかどうかについてはこれから考えていきたいと思います。解決できれば使い勝手がかなり改善されるのではないかと思います。
参考
QuickRunの設定の仕方はヘルプにかなり詳しく書いてあるのでぜひ読んでみて下さい。また、今回下記のサイトを参考にして設定を行いました。