Skip to main content.

sql-mode: Emacs から SQL 文を実行する

bash などの高機能シェルが提供するコマンド補完や行編集機能にどっぷり漬かってしまうと、 SQL Plus(Oracle の SQL クライアント)や isql(SQL Server が提供する SQL クライアント)が提供する貧弱な編集機能が使い辛くて我慢ならないことがありますが、sql.el を利用すれば Emacs の強力な編集機能を利用した SQL 文の記述・実行が可能になります。

概要

sql.elAlex Schroeder 氏がメンテナンスしている SQL 用の Emacs Lisp であり、sql-mode / sql-interactive-mode という二つのメジャーモードを提供しています。

sql-mode はその名の通り、SQL 文を編集するためのモードであり、SQL 文に対する構文ハイライティングが行われます。 更に、後述する sql-interactive-mode へと編集中の SQL を送ることも出来ます。

sql-interactive-mode は SQL クライアントと対話するためのモードです。 ぶっちゃけた話、shell-mode でシェルが動作する代わりに SQL クライアントが動作している、と考えれば問題ないでしょう。 この「SQL クライアントが動作している」バッファを「SQLi バッファ」と呼びます。

入手とインストール

配布元

最近の Emacs には sql.el が含まれていますが、せっかくなので最新版を拾ってきましょう。

sql.el の最新版は http://www.geocities.com/kensanata/elisp/ から取得出来ます。ここには sql.el を始めとして、以下のような SQL 関係の Emacs Lisp が置かれています。

名前 バージョン 説明
sql.el 1.6.3 SQL インタプリタ用の comint
sql-indent.el 1.1.2 SQL 文のインデント
sql-transform.el 2.2.0 SQL 文の変換
sql-complete.el 0.0.1 テーブル / カラムの補完機能
master.el 1.0.2 現在のバッファから他のバッファのスクロールを行う

インストール

普通の Emacs Lisp ですから、普通の Emacs Lisp らしくインストールして、.emacs に設定をすれば完了です。

「説明を聞くのも面倒だ」という人は、とりあえず上記 5 つのファイルを site-lisp あたりにブチ込んでから、以下の設定を .emacs に追加しておくと幸せになれるかもしれません。

;;;
;;; sql-mode
;;; http://www.emacswiki.org/cgi-bin/wiki.pl?SqlMode
;;;

(autoload 'sql-mode "sql" "SQL Edit mode" t)
(autoload 'sql-oracle "sql" "SQL Edit mode" t)
(autoload 'sql-ms "sql" "SQL Edit mode" t)
(autoload 'master-mode "master" "Master mode minor mode." t)

;; SQL mode に入った時点で sql-indent / sql-complete を読み込む
(eval-after-load "sql"
  '(progn
     (load-library "sql-indent")
     (load-library "sql-complete")
     (load-library "sql-transform")
     ))

;; デフォルトのデータベースの設定
(setq sql-user "scott")
(setq sql-database "orcl")

;; SQL モードの雑多な設定
(add-hook 'sql-mode-hook
    (function (lambda ()
                (setq sql-indent-offset 4)
                (setq sql-indent-maybe-tab t)
                (local-set-key "\C-cu" 'sql-to-update) ; sql-transform 
                 ;; SQLi の自動ポップアップ
                   (setq sql-pop-to-buffer-after-send-region t)
                ;; master モードを有効にし、SQLi をスレーブバッファにする
                   (master-mode t)
                (master-set-slave sql-buffer)
                ))
    )
(add-hook 'sql-set-sqli-hook
          (function (lambda ()
                      (master-set-slave sql-buffer))))       
(add-hook 'sql-interactive-mode-hook
          (function (lambda ()
                      ;; 「;」をタイプしたら SQL 文を実行
                         (setq sql-electric-stuff 'semicolon) 
                      ;; comint 関係の設定
                         (setq comint-buffer-maximum-size 500)
                      (setq comint-input-autoexpand t)
                      (setq comint-output-filter-functions 
                            'comint-truncate-buffer)))
          )

;; SQL モードから SQLi へ送った SQL 文も SQLi ヒストリの対象とする
(defadvice sql-send-region (after sql-store-in-history)
  "The region sent to the SQLi process is also stored in the history."
  (let ((history (buffer-substring-no-properties start end)))
    (save-excursion
      (set-buffer sql-buffer)
      (message history)
      (if (and (funcall comint-input-filter history)
               (or (null comint-input-ignoredups)
                   (not (ring-p comint-input-ring))
                   (ring-empty-p comint-input-ring)
                   (not (string-equal (ring-ref comint-input-ring 0)
                                      history))))
          (ring-insert comint-input-ring history))
      (setq comint-save-input-ring-index comint-input-ring-index)
      (setq comint-input-ring-index nil))))
(ad-activate 'sql-send-region)

基本的な使い方

M-x sql-help で簡単なヘルプが出てきますが、一応ざっと解説します。

SQLi バッファを開く

Emacs から各種 SQL クライアントを起動する方法は各 SQL クライアントごとに異なりますが、基本的には「 M-x sql-XX 」という形になっています。

各 SQL クライアントを起動する方法の一覧を挙げておきます。 見ての通り沢山の RDBMS (に付属の SQL クライアント)がサポートされていますが、私が知っているのは Oracle と Microsoft SQL Server だけですので、 これ以降の記述はほぼ全てこの両 RDBMS に特化したものとなります。悪しからず。

RDBMS 関数 SQL クライアント
PostgreSQL sql-postgres psql
MySQL sql-mysql mysql
SOLID sql-solid solsql
Oracle sql-oracle sqlplus
Informix sql-informix dbaccess
SQL Server(Sybase) sql-sybase isql
Ingres sql-ingres sql
SQL Server(Microsoft) sql-ms isql
InterBase sql-interbase isql
DB2 sql-db2 db2

Oracle の場合であれば M-x sql-oracle を実行するとユーザ名、パスワード、接続文字列が尋ねられ、SQL Plus が起動します。SQL Server の場合は M-x sql-ms を実行するとユーザ名、パスワード、サーバ名、データベース名が尋ねられ、isql が起動します。 これらの「SQL クライアントが起動しているバッファ」こそが「SQLi バッファ」です。

SQLi バッファには SQL クライアントのプロンプト (Oracle の SQL Plus であれば「SQL> 」)が表示されており、端末から利用するのと同様の感覚で SQL クライアントを利用出来ます。 なお、SQL Server の isql では SQL 文を入力した後に、 「go 」を入力しないと、SQL 文が実行されないということに注意して下さい。

SQLi バッファが正しく開かない場合は、環境変数 PATH に利用する SQL クライアントが置かれているディレクトリが含まれているかどうかを確認しましょう。 例えば、Microsoft SQL Server の SQL クライアントである isql は c:\Program Files\Microsoft SQL Server\80\Tools\Binn などにインストールされているはずです。

SQL クライアントへ引数を与える

SQL クライアントにオプションを与えたい場合は、事前に変数 sql-XX-options にそのオプションを指定しておきます。 各 RDBMS ごとの変数は以下のようになっていますが、全ての RDBMS でオプションが指定出来るわけではありません。

RDBMS 変数
PostgreSQL sql-postgres-options
MySQL sql-mysql-options
Oracle sql-oracle-options
SQL Server(Sybase) sql-sybase-options
InterBase sql-interbase-options
DB2 sql-db2-options

また、デフォルトのユーザ名やサーバ名などの接続に必要な情報は、それぞれ以下のような変数で設定出来ます。

変数名 意味
sql-user デフォルトのユーザ
sql-passwd デフォルトのパスワード
sql-database デフォルトの接続先データベース(Oracle の場合は接続文字列)
sql-server デフォルトの接続先サーバ

SQLi バッファと isql の問題

Microsoft SQL Server の isql は、SQLi バッファでは何故か出力が乱れます。 端末から isql を利用した場合は

$ isql -S server -U user -P passwd
1> select top 1 * from tb_ikan
2> go
 EMP_NO      OLD_DEPT_CD  NEW_DEPT_CD
 ----------- ------------ ------------
        1177 1001         1002

(1 row affected)
1> exit

と表示されるのに対して、SQLi バッファでは、

select top 1 * from tb_ikan
go
1> 2>  EMP_NO      OLD_DEPT_CD  NEW_DEPT_CD
 ----------- ------------ ------------ 
        1177 1001         1002         
exit

(1 row affected)
1> 
Process SQL finished

のように表示されてしまいます。原因はわかりません。

sql-mode

デフォルトでは拡張子 .sql のファイルを開くと、SQL モードに入ります。 が、これだけでは sql-mode は単なる「プログラミング言語用モード」であり、SQL 文がハイライトされるだけのものでしかありません。 sql-mode の機能をフルに利用するなら、先に述べた方法で SQLi バッファを事前に開いておく必要があります(*1)

sql-mode 上で SQL 文を書き、行の最後(別に最後じゃなくてもよいのですが)でおもむろに C-c C-c を叩くと、SQLi バッファでその SQL 文が実行されます。但し C-c C-c では一行しか SQLi バッファに送ることが出来ませんから、バッファ単位やリージョン単位で送る場合は、別のキーバインドを利用します。

キーバインド 関数 説明
C-c C-b sql-send-buffer バッファの内容を SQLi バッファに送る
C-c C-r sql-send-region リージョンの内容を SQLi バッファに送る
C-c C-c sql-send-paragraph 現在の行を SQLi バッファに送る

実際のところ、一行で完結するような簡単な SQL 文ならそれこそ sql-mode なぞ使わずに SQLi バッファで実行すればよいのですから、一番使うのは sql-send-region かもしれません。 複数行に渡る SQL 文を Emacs の編集機能を利用してガンガン編集し、最後に sql-send-region するのが良いと思います。

なお、SQLi バッファに SQL 文を送ったときに SQLi バッファをポップアップさせるには、sql-pop-to-buffer-after-send-regiont を設定しておきます。

sql-interactive-mode

SQLi バッファで利用されているモードが sql-interactive-mode です。ですが sql-interactive-mode は SQLi バッファでしか利用出来ないので、以後は「SQLi バッファ」で統一します。

SQLi バッファは comint(command interpreter)の機能を利用していますから、shell-mode などで使い慣れた M-pM-n などが使えます。つまり SQL Plus では出来なかった「履歴を手繰る」、或いは「行編集」などが出来るわけです。

SQLi バッファ独自の機能として C-c C-w (sql-copy-column)があります。 SQL ではその性質上、どうしてもカラムを沢山並べなければならない状況が頻発しますが、 そのような場合にこれを利用できます。が、大げさな名前の割にやっていることはごく単純で、 カーソルがある位置の単語をバッファの最後にコピーしているだけです。

まず、カラムの一覧をバッファに表示するために「desc テーブル名 」などを実行します。 カラムの一覧が表示されたら、目的のカラムの上にカーソルを移動し、おもむろに C-c C-w を実行すると、バッファの最後にそのカラム名がコピーされます。 しかも連続して実行すれば途中のカンマを補ってくれるというおまけつきです。 後は「SELECT」や「WHERE」などを追加してやれば、あっという間に SQL 文の完成です。

…などと書くと何とも凄そうにみえますが、個人的にはいちいちカラムを探して C-c C-w するくらいなら、素直に「desc テーブル名」(Oracle の場合)した後に abbrev を使ったほうが楽だとは思っています。

適切な設定を行わないと利用出来ないのですが、「; 」をタイプすると即座に SQL 文が実行されるという機能もあります。変数 sql-electric-stuffsemicolon を定義(*2)しておけばこの機能が有効になります。 便利だと思うかどうかは人それぞれなので、試してみて下さい。

SQL 文の自動インデント

sql-mode で SQL 文の自動インデントを行うには、sql-indent.el を利用します。 sql-indent-offset でタブ位置を好みの位置に設定し、必要であれば sql-indent-maybe-tabt を設定する程度で十分でしょう。

master.el を利用する

master.el は、他のバッファ(スレーブバッファと呼びます)のスクロールを、 現在のバッファ(マスタバッファと呼びます)から行うためのマイナーモードです。 SQL モードをマスタバッファとし、SQLi バッファとスレーブバッファとして利用すると、SQL モードで SQL 文を書きながら、SQLi バッファをスクロールさせることが出来ます。 組み込み方については冒頭に挙げた例を参照して下さい。

なお、Meadow 1.14 では master.el に以下のパッチを当てないと駄目なようです。

--- master.el.orig    Thu Apr 25 22:10:27 2002
+++ master.el         Thu Apr 25 22:10:52 2002
@@ -69,7 +69,7 @@
;;; Define master mode.

;;;###autoload
-(define-minor-mode master-mode
+(easy-mmode-define-minor-mode master-mode
"Toggle Master mode.
With no argument, this command toggles the mode.
Non-null prefix argument turns on the mode.

SQL バッファから SQLi バッファのスクロールを制御するには、以下のキーバインドを利用します。

キーバインド 関数 説明
C-c C-l master-says-recenter SQLi バッファのカーソル位置をバッファ中央へ移動する
C-c > master-says-end-of-buffer SQLi バッファのカーソル位置を SQLi バッファの終端へ移動する
C-c < master-says-beginning-of-buffer SQLi バッファのカーソル位置を SQLi バッファの先頭へ移動する
C-c C-p master-says-scroll-down SQLi バッファを下へスクロールする
C-c C-n master-says-scroll-up SQLi バッファを上へスクロールする

なお、master モードの状態については M-x master-show-slave で知ることが出来ます。

*1: SQL モードを開いてから SQLi バッファを開いた場合、SQL モードから SQL 文を投げようとしてもうまくいきません。必ず「SQLi バッファ」→「SQL モード」の順に開いて下さい。

*2: 「semicolon」という文字列ではなく、シンボルとして定義します。