Entry

プログラミングメモ - SQLite を C++ で使ってみよう(その2)

2009年09月05日

さて,少し間が空いちゃったけれども,前回に引き続き,SQLite の使い方です。前回は SQLite のプログラミングに必要なモノを用意するところまで終わったのでした。今回は,SQLite のライブラリとリンクして,簡単なプログラムを作るところまでやってみようと思います。

まず,開発環境の確認をば。

OSWindows XP Professional SP3
IDEMicrosoft Visual Studio 2008
C++ CompilerIntel Compiler 11.1

とりあえず今回は,VC が動けば問題ありません。別に IDE がなくちゃダメなわけじゃないんですけどね。それと,前回作った SQLite ライブラリ一式は,以下の場所に置いておくことにしましょう。あくまでも例なので,自分でコンパイル/リンクできる方は,ご自由にどうぞ。

ファイル名種類設置場所
sqlite3.dllSQLite の本体(DLL)PATH の通ったところ
sqlite3.libDLL のスタブライブラリプロジェクトファイル(*.proj)がある場所と同じ場所
sqlite3.hヘッダファイルプロジェクトファイル(*.proj)がある場所と同じ場所
sqlite3ext.hヘッダファイル(使わない)

今回は配布物を作るわけでもないので,プロジェクトのフォルダ階層は最初できたものをそのまま使います。VC 上では,「新規作成」から「空のプロジェクト」を選んで,適当なプロジェクトを作成してください。Windows 上のプログラミングでは,まず最初に,文字列 TCHAR をマルチバイト文字にするかワイド文字(Unicode)にするか決めなくちゃいけないんですけれど,今回は Unicode を使うようにしています。IDE からだと選ぶだけなんですけれど,コマンドラインでコンパイルする場合は,UNICODE と _UNICODE を #define しておいてください。

また,ライブラリとリンクする必要があるので,プロジェクトの「プロパティ」にある[リンカ]-[入力]-[追加の依存ファイル]に,"sqlite3.lib" を追加しておきましょう。

一応,手元で作ったプロジェクトを固めて置いておきます。

プロジェクトは VC9 にしてあるので,Intel Compiler をお使いの方は,適宜切り替えてください。

今回のソース

まず,今回使うソースを紹介。上記のプロジェクトでは,SQLiteTest.cpp として保存してあります。

// C/C++ header
#include <cstdlib>
#include <iostream>
// Windows header
#include <windows.h>
#include <tchar.h>
// SQLite header
#include "sqlite3.h"

int
_tmain(int argc, TCHAR* argv[]) {
    int status = SQLITE_OK;
    LPCTSTR dbname = _T("test.db");
    const char* sqlCreate =
        "CREATE TABLE bookmark ( "
            "id INTEGER PRIMARY KEY, "
            "url TEXT NOT NULL, "
            "title TEXT "
        ") ";
    const char* sqlInsert =
        "INSERT INTO bookmark ( "
            "url, title "
        ") VALUES ( "
            "'http://www.google.com/', 'Google' "
        ") ";

    // (1) ハンドラを作る
    ::sqlite3* dbh = 0;
    status = ::sqlite3_open16(dbname, &dbh);
    if (status != SQLITE_OK) {
        std::cerr << sqlite3_errmsg(dbh) << std::endl;
        ::exit(1);
    }
    // (2) SQLを投げる - CREATE TABLE
    char* errorMessage;
    status = sqlite3_exec(dbh, sqlCreate, 0, 0, &errorMessage);
    if (status != SQLITE_OK) {
        std::cerr << errorMessage << std::endl;
        ::exit(1);
    }
    // (3) SQLを投げる - INSERT
    status = sqlite3_exec(dbh, sqlInsert, 0, 0, &errorMessage);
    if (status != SQLITE_OK) {
        std::cerr << errorMessage << std::endl;
        ::exit(1);
    }
    // (4) ハンドラを閉じる
    ::sqlite3_close(dbh);

    std::cout << "ok" << std::endl;
    return 0;
}

SQLite ライブラリで SQL を投げています。CREATE TABLE でブックマークのテーブルを作って,INSERT 文で Google の URL を登録している。ソースを見てお分かりの通り,sqlCreate と sqlInsert が,投げている SQL です。

SQL というと,何はともあれ SELECT 文も使いたいところですけれど,今回の方法では少し複雑になってしまうので(コールバック関数を使う)今回は扱いません。結果は,前回ダウンロードしたコマンドラインアプリ(sqlite3.exe)で確認することにします。プログラムからの扱いは,次回,プリペアドステートメントを使うときに紹介するので,その時をお楽しみに。

ビルドして実行してみる

では,詳しい説明に入る前に,このプロジェクトをビルドして実行してみましょう。VC の IDE からは,F7 を叩けばビルドできます。実行は F5 ですね。

実行してうまくいくと,"ok" と表示されて終了します。失敗するとエラーメッセージが出力されます。実行ファイルのある場所に,test.db というファイルができているので,これを SQLite のクライアントツール(前回もらってきたもの)で開いて,SELECT してみましょう。こんな風にします。

C:\home\aian\msvs\projects\SQLiteTest\Debug>SQLiteTest
ok

C:\home\aian\msvs\projects\SQLiteTest\Debug>sqlite3 test.db
SQLite version 3.6.17
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> SELECT * FROM bookmark;
1|http://www.google.com/|Google
sqlite> .quit

C:\home\aian\msvs\projects\SQLiteTest\Debug>

ちゃんと登録されています。よかったよかった。

SQLite の使い方(基本)

それでは,プログラムの中身を見ていきましょう。SQLite を使ったプログラムの基本的な流れは,次のようになるので,順に説明していきます。

  1. データベースファイルを開いてハンドラを取得する。
  2. ハンドラを使って SQL を実行する。
  3. ハンドラを片付ける。

ハンドラを作る

ハンドラというのは,SQLite の関数を制御するための情報です。C/C++ をやってる方はご存知ですよね。FILE 構造体と同じようなもの。標準ライブラリでファイルを扱うのに FILE ハンドラが必要なのと同じで,SQLite でも sqlite3 ハンドラを使って制御します。

ハンドラは,SQLite のデータベースファイルを開くことで,取得することができます。ここでは,次のようにしてハンドラを作りました。

    // (1) ハンドラを作る
    ::sqlite3* dbh = 0;
    status = ::sqlite3_open16(dbname, &dbh);
    if (status != SQLITE_OK) {
        std::cerr << sqlite3_errmsg(dbh) << std::endl;
        ::exit(1);
    }

sqlite3_open16() という関数を使って,ファイルを開いています(ファイルがない場合は作られる)。第1引数が,開きたいデータベースファイルのパスで,第2引数は sqlite3 ハンドラへのポインタへのポインタ。成功すると,返値に SQLITE_OK なる定数が返ってきて,sqlite3 ハンドラへのポインタ(dbh)に有効なハンドラへのアドレスがセットされます。簡単ですね。

注意しておく必要があるのは,SQLite では,ファイルを開く方法がいくつかあるということです。これは,パス名にマルチバイト文字を使うか,ワイド文字(Unicode)を使うかで違いが出てきます。今回使った sqlite3_open16() は,ワイド文字用の関数。Windows プログラムの場合,TCHAR をマルチバイト文字にセットしていると(_MBCS が定義されている場合)文字化けしたパス名でファイルを作ろうとしてしまいます。

マルチバイト文字セットを使う場合は,sqlite3_open16() ではなく,sqlite3_open() または sqlite3_open_v2() 関数(推奨)を使いましょう。

ハンドラを正常に取得できると,SQLite のその他の関数を使うことができるようになります。

SQL を投げる

ハンドラができたら,実際に SQL を投げましょう。ここでは,次のようにして投げています。

    // (2) SQLを投げる - CREATE TABLE
    char* errorMessage;
    status = sqlite3_exec(dbh, sqlCreate, 0, 0, &errorMessage);
    if (status != SQLITE_OK) {
        std::cerr << errorMessage << std::endl;
        ::exit(1);
    }
    // (3) SQLを投げる - INSERT
    status = sqlite3_exec(dbh, sqlInsert, 0, 0, &errorMessage);
    if (status != SQLITE_OK) {
        std::cerr << errorMessage << std::endl;
        ::exit(1);
    }

sqlite3_exec() という関数を使って投げています。これは,文字列の SQL を投げるための関数。SQLite だけじゃなく,普通 DB のプログラミングでは,文字列をそのままハンドラなりコネクションなりに渡すことはあまりありません。セキュリティの面からも効率の面からも,プリペアドステートメントを使うのが普通だからです。また,sqlite3_exec() は,ワイド文字列の SQL にも対応していません。つことで,この方法は,普通とは違う簡便なやり方だと思っておいてください。

引数を見ると,第1引数にさっき作った sqlite3 ハンドラを指定して,第2引数に投げる SQL 文字列を指定しています。返値は,sqlite3_open16() の場合と同じで,正常終了時は SQLITE_OK が返ります。ちゃんとチェックしておきましょう。

sqlite3_exec() は,基本これだけで動くんですけれど,他にも指定できる引数があるので,プロトタイプを紹介しておきます。

int sqlite3_exec(
  sqlite3*,                                  /* ハンドラ */
  const char *sql,                           /* 投げる SQL */
  int (*callback)(void*,int,char**,char**),  /* コールバック関数へのポインタ */
  void *,                                    /* コールバック関数の第1引数として渡す値へのポインタ */
  char **errmsg                              /* エラーメッセージへのポインタ */
);

コールバックってのは,SQL を投げた後に,ライブラリ側で呼び出してくれる関数のことです。主な使い道としては,SELECT 文を投げた結果を取得するのに使う。検索結果を取得する関数を,上の callback 関数の形で作っておいて,sqlite3_exec() を投げるときに,その関数へのポインタを渡すわけです。ただ,この方法は面倒だし,やり方としても普通はプリペアドステートメントを使うので,ここでは扱いません。

最後の引数には,エラーメッセージを指すポインタへのポインタを指定できます。関数を実行した後,結果を表すメッセージがこのポインタにセットされます。注意しなくちゃいけないのは,ここで指されるメッセージのバッファが,動的に確保されていることです。ここではちゃんと扱っていないんですけれど,サーバプログラムなどでメモリリークにしっかり配慮しなくちゃいけない場合は,sqlite3_free() で解放しないと確保しっぱなしになるので気をつけましょう。メッセージなんていらねーよ,という方は 0(NULL)を指定しておきます。

と,そんなこんなで,SQL を実行することができました。

ハンドラを片付ける

最後に,ハンドラを片付けましょう。FILE を fopen(3) したら,fclose(3) しなくちゃいけないのと同様に,sqlite3_open*() したら,sqlite3_close() で後片付けします。

    // (4) ハンドラを閉じる
    ::sqlite3_close(dbh);

これは別に何も書くことはないと思います。当たり前だけれども,sqlite3_close() 後のハンドラは,もう一度 sqlite3_open*() でハンドラを取得しないと使えません。

まとめ

どうだったでしょうか。SQLite というと,なんだか特殊なライブラリのような印象が少なからずあるかもしれませんけど,C/C++ のファイル操作と同じくらい簡単に外部データを扱えることが分かったと思います。Windows から扱う方は,TCHAR 型の文字列設定に応じて,呼ぶ関数が変わることを少し念頭に置いておいてください。

SQLite の基本の説明は,まだ終わっていなくて,プリペアドステートメントとプレースホルダを使った制御が残っています。これをやっつければ,もう SQLite の8割方はこなせるようになったと言っていいと思います。

基本の使い方を紹介した後,DAO パターンをはじめとした C++ っぽい使い方を紹介したいと思います(次回に続く)。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN