Vim

vimから直接DB触ったりSQL補完したり

More than 3 years have passed since last update.

vimから直接データベースを触る場合は、dbext.vimを使用すれば良いとのこと。
今回接続したいのDBはSQLiteだけだが、dbext.vimはその他多くのDB(Oracle, Sybase, Microsoft, MySQL, DBI,..)に対応しているとのこと。

dbextのインストール

.vimrc
NeoBundle 'vim-scripts/dbext.vim', '18.0'

公式のバージョンは20.0だが、githubには19.0までが上がっている。
ただし19.0で加えられた修正に少し問題があったため、ここでは特にこだわりもないため18.0を使用した。
(具体的にはcmd_terminatorの定義に問題あり。明示的にcmd_terminatorを指定するなどで回避はできる)

接続設定

接続先の指定方法には何種類かの方法があるが、
接続パラメータをまとめた接続プロファイルを使用するのが簡単かと思う。

  • 接続パラメータを個別に.vimrcに設定
  • 接続プロファイルを.vimrcに設定
  • ウィザート的に接続パラーメタを指定(<Leader>sbpで利用可能)
  • Modelineを使用して接続パラメータを設定

接続プロファイルの設定

プロファイルは好きなだけ作成可能。
詳細はヘルプに用例がたくさんあるのでそちらを参照。

.vimrc
" 環境に合わせてクライアントプログラムを変更
" (ver.20.0 からはデフォルトでsqlite3)
let g:dbext_default_SQLITE_bin = 'sqlite3'
" プロファイルの定義
let g:dbext_default_profile_MySQL_test = 'type=SQLSRV:integratedlogin=1:dbname=myDB'
let g:dbext_default_profile_SQLServer_test = 'type=SQLSRV:integratedlogin=1:dbname=myDB'
let g:dbext_default_profile_test = 'type=SQLITE:dbname=/dbpath/test.db'
"デフォルトで使用するプロファイルを指定
let g:dbext_default_profile = 'test'

プロファイルの切り替え

プロファイルを切り替える場合は :DBSetOption profile=testとして直接指定するか、
<Leader>sbpによるウィザートから選択する。

スクリーンショット 2014-08-02 14.07.37.png

DBアクセス

テーブル一覧取得

<Leader>sltとすれば画面下部のResultウィンドウに接続先DBのテーブル一覧が表示される。

Result
Connection: T(SQLITE)  D(/dbpath/test.db)   at 03:33
Tables
------
Alphabeticallistofproducts
OrdersQry                 
Categories
ProductSalesfor1997       
CategorySalesfor1997
Products                  
CurrentProductList

テーブル内容表示

テーブル名にカーソルを置いて<Leader>stにてテーブル内容が表示される。
SQL文字列上で<Leader>seでもSQLが実行されその結果が表示される。

Result
Connection: T(SQLITE)  D(/dbpath/test.db)   at 03:35
TerritoryID  TerritoryDescription              RegionID  
-----------  --------------------------------  ----------
01581        Westboro                          1         
01730        Bedford                           1         
01833        Georgetow                         1         
02116        Boston                            1         
02139        Cambridge                         1         
02184        Braintree                         1         
02903        Providence                        1         

この時Javascriptファイルなどで、以下のように結合された文字列でも、<Leader>seにてちゃんとSQLとして解釈し実行してくれる。
なお、この場合はプレースホルダの値を入力するプロンプトが表示される。

スクリーンショット 2014-08-09 23.18.02.png

他のコマンド

他にもいろいろできますが、以下に一部だけ掲載。
テーブル以外にもViewやProcedureにも対応しているので利用したい場合はヘルプ参照。

デフォルトでのマッピングはすべて<Leader>sで開始される。
これはg:dbext_map_prefixで変更可能。

normalモード

キー コマンド 意味
<Leader>sbp DBPromptForBufferParameters 接続先設定ウィザート
<Leader>sh DBHistory 実行履歴表示
<Leader>slt DBListTable テーブルリスト表示
<Leader>sdt DBDescribeTable カーソル上のテーブル定義表示
<Leader>sdta DBDescribeTableAskName 任意のテーブル定義表示
<Leader>st DBSelectFromTable カーソル上のテーブルデータ表示
<Leader>sta DBSelectFromTableAskName 任意のテーブルデータ表示
<Leader>stw DBSelectFromTableWithWhere カーソル上テーブルデータ表示。Where条件指定可能
<Leader>sq DBExecSQL 任意のSQL実行
<Leader>se DBExecSQLUnderCursor カーソル上のSQL実行

visualモード

キー コマンド 意味
<Leader>sdt DBDescribeTable 選択テーブル定義表示
<Leader>st DBSelectFromTable 選択テーブルの内容を表示
<Leader>se DBExecVisualSQL 選択SQLの実行

補完

SQLファイルを開き、
例えばOを入力した状態で<C-c>tを押すと、Oで始まるテーブルリストが表示される。

スクリーンショット 2014-08-09 23.21.52.png

この状態で<Right>を押すとそのテーブルのカラムリストが表示される。
スクリーンショット 2014-08-09 23.22.42.png

単純に<C-c>cだけでもカラムリストが表示される。
その場合のテーブルは前回選択したテーブルとなるが、これはSQLの文脈によって動的に切り替わる。(テーブル別名に合わせて表示されるカラムリストが変わる)

スクリーンショット 2014-08-09 23.22.57.png

ちなみにテーブルリストで<C-c>L押すとカラムを一気に出力できる。
この時の表別名ODは自動で生成され、出力時に表示されるプロンプト画面にて変更も可能。

スクリーンショット 2014-08-09 23.23.36.png

あるいはカーソルがテーブル上にある状態で:DBListColumn<Leader>slcとすればカラム一覧がコピーされるので、それを貼り付けても同じようなことができる。

キーマッピング

補完に関するキーマッピンはこんな感じ。インサートモードです。

キー 補完対象
<C-c>a syntax
<C-c>k sqlKeyword
<C-c>f sqlFunction
<C-c>o sqlOption
<C-c>T sqlType
<C-c>s sqlStatement
<C-c>t table
<C-c>p procedure
<C-c>v view
<C-c>c column
<C-c>l column_csv
<C-c>R キャッシュクリア
<Right> テーブルリストで押すとカラムリストへ
<Left> カラムリストで押すとテーブルリストへ
<C-c>L テーブルリストで押すとカラム一覧出力

キーマッピング変更

<Right><Left>は遠いのでこれも変更可能。

.vimrc
let g:ftplugin_sql_omni_key = '<C-c>'
let g:ftplugin_sql_omni_key_right = '<C-d>'
let g:ftplugin_sql_omni_key_left  = '<C-u>'

Javascriptからの利用

上記の補完はSQLファイルでしか使用できず、
Javascriptファイルにて補完したかったのでちょっと考えてみた。

ファイルタイプの変更

単純に:set ft=sqlでファイルタイプを変更すれば補完できる。
これはヘルプ(:h ft_sql)にも書いてある方法なのだが、後で戻す必要もありよろしくない。

omnifuncの設定

JavascriptでSQLのomnifuncを利用できれば良いので以下のように設定する。
neocompleteを使用している前提。

.vimrc
autocmd FileType javascript setlocal omnifunc=sqlcomplete#Complete
if !exists('g:neocomplcache_omni_functions')
  let g:neocomplete#sources#omni#functions = {}
endif
let g:neocomplete#sources#omni#functions.javascript = ['javascriptcomplete#CompleteJS']

こうすれば普段はneocompleteによるJavascriptのオムニ補完がされ、
<C-x><C-o>にて明示的にオムニ補完した場合にはSQLの補完が行われる。

ただこの方法では<C-c>tなどの補完は使えないので。

SQLの補完を直接呼び出す

シンプルに必要な補完は明示的に割り当ててやる方法も採用した。
これでSQLの場合と同じ方法で補完が利用できる。

.vimrc
exec 'inoremap <silent> '.g:ftplugin_sql_omni_key_right.' <C-R>=sqlcomplete#DrillIntoTable()<CR>'
exec 'inoremap <silent> '.g:ftplugin_sql_omni_key_left.'  <C-R>=sqlcomplete#DrillOutOfColumns()<CR>'
exec 'inoremap <buffer> '.g:ftplugin_sql_omni_key.'t <C-\><C-O>:call sqlcomplete#Map("table")<CR><C-X><C-O>'
exec 'inoremap <buffer> '.g:ftplugin_sql_omni_key.'c <C-\><C-O>:call sqlcomplete#Map("column")<CR><C-X><C-O>'
exec 'inoremap <buffer> '.g:ftplugin_sql_omni_key.'l <C-\><C-O>:call sqlcomplete#Map("column-csv")<CR><C-X><C-O>'
exec 'inoremap <buffer> '.g:ftplugin_sql_omni_key.'R <C-\><C-O>:call sqlcomplete#Map("resetCache")<CR><C-X><C-O>'

まとめ

個人的な用途としては十分利用可能になった。
ただJavascriptからSQLite接続でしか利用していないものの多少問題もある。

例えば..

  • シングルクォーテーションで囲ったSQL文字列は実行できない(ダブルクオーテーションならOK)
  • テーブル定義にてカラム名が[ ]で囲まれているとカラムリストが正しく生成されない

シングルクォーテーションの問題は、
コマンド呼び出しを少し修正してプラグインに渡す前に置換することで対応した。
また[ ]で囲ったテーブルは現在使用していないので今のところは関係ない。

最悪、DB毎で解析に使用される関数が以下のように分離されているので、
案外簡単に修正できそうなため問題が生じればその時に対応を考える感じでいく。

SQLITEの場合

  • DB_SQLITE_describeProcedure
  • DB_SQLITE_describeTable
  • DB_SQLITE_execSql
  • DB_SQLITE_getDictionaryProcedure
  • DB_SQLITE_getDictionaryTable
  • DB_SQLITE_getDictionaryView
  • DB_SQLITE_getListColumn
  • DB_SQLITE_getListProcedure
  • DB_SQLITE_getListTable
  • DB_SQLITE_getListView
  • DB_SQLITE_stripHeaderFooter