Posted at

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

More than 5 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