tclsqlite

tclsqliteは、オープンソースのRDBMSである SQLiteに接続するTclインターフェース(バインディング)で、 SQLite本体と同じサイトで配布されています。

SQLiteについて簡単に紹介すると、これは2004年に PHP 5.0にバンドルされたことで一気に知名度を上げた、 オープンソースRDBMSです。 ただし、MySQLやPostgreSQLのようなサーバーアプリケーションとは異なり、 1DB1ファイル構成のローカル・ファイルに、 C言語など幾つかのプログラミング言語のAPIから接続してデータ操作をします (マイクロソフトのAccessに近いイメージ)。

C言語自身を除くバインディングを持つプログラミング言語はいくつかあります。 SQLiteの開発元自身がバインディングも開発している言語は2つだけで、 一つがTcl、もう一つはC#(ADO.NET Provider)です。
他にバインディングを持つ言語の代表例は、もちろんPHPです。 そのほか、Pythonなどの言語でも使えるようになっています。

なお、SQLiteのバージョンは現在3台になっています。バージョン2と3の互換性はありません。


バイナリ配布のインストール

tclsqliteは、ソース配布からビルドできるほか、 Windows用のDLL、Linux用の共有ライブラリ(.so)のバイナリが配布されているので、 両環境ではそれらが利用できます。
Linux環境では、 tclsqlite-3.*.*.so.gz をダウンロードし、gzip -d で圧縮を解除します。 tclsqliteのバージョンは、インストールされているSQLiteのバージョンと同じものを使う必要があります。
.soファイルができたら、適当な位置にコピーして、pkgIndex.tclを作ります。

mkdir /usr/local/lib/tcl8.4/tclsqlite3
mv -i tclsqlite-3.3.2.so /usr/local/lib/tcl8.4/tclsqlite3/.
cd /usr/local/lib/tcl8.4/tclsqlite3
chmod 0755 tclsqlite-3.3.2.so
tclsh
% pkg_mkIndex .
% exit

これでtclshを実行し、「package require sqlite3」と打って、バージョン番号が表示されればOKです。 (バージョン3からパッケージ名が変更になっています。「sqlite」ではなく、「sqlite3」です。)
Windows環境でもほぼ同様です。
tclsqlite-3_*_*.zipをダウンロードして展開し、できた「tclsqlite-3_*_*」 をTclライブラリディレクトリ(lib)の下に移動します。
pkgIndex.tclは上記のようにtclshを起動してpkg_mkIndexコマンドで作ればOKですが、 以下のような警告が出ます。

% pkg_mkIndex .
warning: "tclsqlite3.dll" provides more than one package ({sqlite3 3.7.2} {sqlite 3.7.2})
%

一応問題はないのでしょうか、次のようなpkgIndex.tclが作られます。

package ifneeded sqlite 3.7.2 [list load [file join $dir tclsqlite3.dll]]
package ifneeded sqlite3 3.7.2 [list load [file join $dir tclsqlite3.dll]]


ソース配布からのインストール

tclsqliteのソースは、本体であるSQLiteのソース配布に一緒についており、 SQLiteの本体に付属している唯一の言語インターフェース(もちろんC言語を除きます)になっています。 標準では、SQLiteをコンパイルすると、tclsqliteのライブラリも一緒にコンパイルされます。 ここではSQLite 3.3.1をVine Linux 3.1上でビルドした例をご紹介します。

% ./configure --with-tcl=/usr/local/lib
% make
% su
# make install
# make tcl_install

--with-tclには、tclConfig.shがあるディレクトリを指定します。 またmake tcl_installするとtclinstaller.tclがtclshコマンドによって解釈されるので、 "tclsh"と打ったときに実行されるTcl処理系の下にインストールされることになります。 インストール位置は、make tcl_installする際に環境変数「DESTDIR」 を指定していればそのディレクトリに、 そうでなければtclのグローバル変数auto_pathの最初に現れるディレクトリに、 いずれもsqlite3というディレクトリが作られ、その中にインストールされます。

% tclsh
% package require sqlite3
3.3.1

のように、バージョン番号が表示されればインストールは成功です。


基本的な使い方

package require sqlite3
sqlite3 d D:/usr/dbms/sqlite3/stdb/stdb

set n 1
d eval {
  SELECT SHITEN_CODE, SHITEN_NAME, AREA_CODE, ADDRESS, EMPLOYEE_NUM FROM SHITEN
   WHERE AREA_CODE = '5' ORDER BY SHITEN_CODE
} values {
    puts "*** Row $n ***"
    parray values
    puts "Columns=$values(*)"
    puts "Code=$values(SHITEN_CODE)"
    puts "Name=$values(SHITEN_NAME)"
    puts ""
    incr n
}
d close

tclsqliteを使うには、sqlite3パッケージをロードします。

SQLiteデータベースを開くには、 「sqlite3 識別子 データファイルのパス」 とします。識別子は普通のTclの命名規則に従えば自由に決めることができ、 以後はこの識別子をコマンドとして使います。 そのサブコマンドのうち重要な2つはevalとcloseです。 evalはSQL文を実行します。closeは接続を終了します。

evalの基本的な引数はSQL文だけですが、 例のようにその後ろに配列変数名(ここではvalues)をつけると、 SQL文がSELECT文である場合に、結果集合1行に対し一度ずつ、 さらにその後ろの引数として指定したTclスクリプト (ここではvaluesの後ろにある、波括弧で囲んだ部分)を実行します。

valuesの値は、フェッチしたレコードのカラム名を要素とする配列変数(ハッシュ)です。 values(*)とすると、結果集合に含まれる各列の名前の配列が取得できます。 values(列名)とすると、その値が取得できます。
ここで、これらで使える「列名」についての注意ですが、 英大文字と小文字は区別され、 表を作った際の列定義が大文字だったか、小文字だったかがこの列名に反映されるようです。 そのため、内部的に列名が"SHITEN_CODE"となっているのに、 $values(shiten_code)と小文字で参照しようとするとエラーになります。
また例にあるように、parrayというコマンドを使うと、配列内の要素、 つまりレコードの内容がダンプされます。その内容は完全にデバッグ用です。


日本語データの扱い

日本語データの扱いは?文字化けの心配はないのでしょうか?

心配ゴム用。このサンプルでは全ての動作を表現しきれていませんが、 Windows10では、次のようにすれば大丈夫です。

  • SQLiteデータベース内のデータはUTF-8(UTF-8N)にすること。
  • このTclスクリプト自身はシフトJIS(CP932)で書けばいいです。
  • このプログラムをコマンドプロンプトからtclsh86.exeで実行するとき、 chcpコマンドでコードページを変更する必要はないです。 (普通にchcp 932の状態で実行すれば日本語が正常に表示されます。chcp 65001にする必要はありません)


別の書き方の例

SELECT文を発行するときにおなじみなのがバインド変数(パラメータ渡し)。 Tclsqliteのevalコマンドでは次のように行います。

package require sqlite3
sqlite3 d D:/usr/dbms/sqlite3/stdb/stdb

set areaCode "3"
d eval {
  SELECT SHITEN_CODE, SHITEN_NAME, AREA_CODE, ADDRESS, EMPLOYEE_NUM FROM SHITEN
   WHERE AREA_CODE = $areaCode ORDER BY SHITEN_CODE
} {
    puts "${SHITEN_CODE}\t${SHITEN_NAME}"
}
d close

バインド変数はTcl変数名と自動的、透過的に連動します。
またはほかの言語でよく見られる、 ":変数名"のようにコロンをつける方法も可能です。つまりこの場合は 「$areaCode」の代わりに「:areaCode」と書いても全く同じ意味になります。

また、上のようにevalの配列変数名(最初の例にあったvalues)を省略すると、 $SHITEN_CODE、$SHITEN_NAMEなどのように、 表の列名そのままのTcl変数名が使え、記述の短縮に役立ちます。


更新処理

INSERT、UPDATEなどのDML文も普通の記述で実行可能です。

d eval {
    UPDATE ORDERS SET DELIVERED_DATE = DATE('now','localtime')
     WHERE ORDER_ID = $orderId
}
puts "[d changes]行のデータを処理しました。"

この例ではSQLiteの組み込み関数であるDATEを使って、今日の日付を設定しています。
DATEの代わりに時分秒までの情報を持つDATETIMEという関数もあります。
またDML文で更新した行数はchangesサブコマンドで取得できます。


トランザクション

複数のDML文を一貫性を保持して更新するには、 transactionサブコマンドを使って他のevalコマンドを囲みます。

d transaction {
    d eval {
        UPDATE ORDERS SET DELIVERED_DATE = DATE('now','localtime')
         WHERE ORDER_ID = $orderId
    }

    d eval {
        UPDATE INV SET STOCKS = STOCKS + $ORDERED_QUANTITY
         WHERE ITEM_ID = $itemId
    }
}

拡張レビュー分室 top
(first uploaded 2004/07/19 last updated 2020/05/02, MISUMI URANO)