BCB/SQLite3を試す

BorlandC++BuilderでSQLiteを利用してみる実験。

目次

準備

ダウンロード

SQLite本家サイト→Download。DLLバイナリ(sqlitedll-3_3_11.zip)とソースファイル(sqlite-source-3_3_11.zip)をダウンロードしてくる。後々のメンテ用にコマンドラインツール(sqlite-3_3_11.zip)もダウンしておく。

インポートライブラリ

今回はインポートライブラリを使ってDLLを利用する。
まずDLLのアーカイブを解凍し(sqlite3.dll, sqlite3.defが生成)、コマンドプロンプトを開きそのフォルダへ移動。

> implib -c sqlite3.lib sqlite3.dll

implibにてインポートライブラリを作成。sqlite3.libが出来ていればOK。

ヘッダファイル

ソースファイルを解凍しsqlite3.hが入っていることを確認する。今回ソースファイルの中で使用するのはこのヘッダファイルのみ。

プロジェクトの作成

とりあえず、新規作成→VCLフォームアプリケーションを作成する。
先ほど抽出した、sqlite3.dll, sqlite3.lib, sqlite3.h をプロジェクトのフォルダへコピーする。sqlite3.dllはパスの通った場所か実行ファイルが出来るフォルダへ。sqlite3.libはソースと同じフォルダで構わない。ビルド設定でsqlite3.libのあるフォルダを指定する。

ヘッダファイルの修正

そのままではstruct sqlite3_index_infoの定義でエラーが出てしまう。暫定措置として原因となっているconstを取り除いた。これで良いのかどうかは検証中。以下修正部分。

struct sqlite3_index_info {
  /* Inputs */
  /*const*/ int nConstraint;     /* Number of entries in aConstraint */
  const struct sqlite3_index_constraint {
     int iColumn;              /* Column on left-hand side of constraint */
     unsigned char op;         /* Constraint operator */
     unsigned char usable;     /* True if this constraint is usable */
     int iTermOffset;          /* Used internally - xBestIndex should ignore */
  } * /*const*/ aConstraint;      /* Table of WHERE clause constraints */
  /*const*/ int nOrderBy;        /* Number of terms in the ORDER BY clause */
  const struct sqlite3_index_orderby {
     int iColumn;              /* Column number */
     unsigned char desc;       /* True for DESC.  False for ASC. */
  } * /*const*/ aOrderBy;         /* The ORDER BY clause */

  /* Outputs */
  struct sqlite3_index_constraint_usage {
    int argvIndex;           /* if >0, constraint is part of argv to xFilter */
    unsigned char omit;      /* Do not code a test for this constraint */
  } * /*const*/ aConstraintUsage;
  int idxNum;                /* Number used to identify the index */
  char *idxStr;              /* String, possibly obtained from sqlite3_malloc */
  int needToFreeIdxStr;      /* Free idxStr using sqlite3_free() if true */
  int orderByConsumed;       /* True if output is already ordered */
  double estimatedCost;      /* Estimated cost of using this index */
};

リンクするライブラリの指定

リンクするライブラリ名を指定する。ソースに一つに

#pragma link "sqlite3.lib"

という記述を追加する。

データの用意

コマンドラインツールでデータベースを作成。

データはゆうびんホームページから。とりあえず「読み仮名データの促音・拗音を小書きで表記しないもの」を利用する。

コマンドラインツールは、CSV形式には対応していないので項目のダブルコーテーションをキャンセルしない。よってエディタの置換機能で予め取り除いておく。

sqlite> create table zip (
  x int,
  zip1 text, zip2 text,
  huri1 text, huri2 text, huri3 text,
  addr1 text, addr2 text addr3 text,
  x1 int, x2 int, x3 int,
  x4 int, x4 int, x5 int
);
sqlite> .separator ,
sqlite> .import ken_all.csv zip

.import機能を利用して一気に取り込む。

フォームの設計

郵便番号を入力し、住所を検索するアプリケーションを作ります。

画面イメージ

主なプロパティの設定

コンポーネント オブジェクト プロパティ
TButton Button1 Caption 検索
コンポーネント オブジェクト プロパティ
TEdit Edit1 MaxLength 3
コンポーネント オブジェクト プロパティ
TEdit Edit1 MaxLength 4
コンポーネント オブジェクト プロパティ
TLabel Label1 Caption 郵便番号
コンポーネント オブジェクト プロパティ
TMemo Memo1 ScrollBars ssVertical

コーディング

デバッグ出力関数

メモにメッセージを出力する関数。

void AddMessage(AnsiString text)
{
    Form1->Memo1->Lines->Add(text);
}

コールバック関数

select文によるデータを受け取るための関数。1行取得するたびに呼ばれる。

int select_callback(void*, int argc, char** argv, char** columnNames)
{
    AnsiString addr1 = argv[0];
    AnsiString addr2 = argv[1];
    AnsiString addr3 = argv[2];
    AddMessage(addr1 +addr2 + addr3);

    return 0;
}

検索ボタン押下ハンドラ

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    sqlite3* db;

    if (sqlite3_open("TEST.DB",&db) == SQLITE_OK) {
        AddMessage("DBオープン");

        int err;
        char *sql;

        if (Edit2->Text != "") {
            sql = sqlite3_mprintf(
                "SELECT addr1,addr2,addr3 FROM zip "
                "WHERE zip2=%s%s",
                Edit1->Text.c_str(), Edit2->Text.c_str());
        } else {
            sql = sqlite3_mprintf(
                "SELECT addr1,addr2,addr3 FROM zip "
                "WHERE zip1 LIKE \"%s%%\"",
                Edit1->Text.c_str());
        }
        AddMessage(sql);

        err = sqlite3_exec(db, sql, select_callback, NULL, NULL);
        if (err != SQLITE_OK) {
            AddMessage("コマンドエラー");
        }
        sqlite3_free(sql);
        sqlite3_close(db);
        AddMessage("DBクローズ");
    } else {
        AddMessage("DBオープンエラー");
    }
}

検索ボタン押下で、郵便場号をキーとして住所を検索し、結果を表示します。

言うまでも無く、AddMessage(), select_callback() 関数のプロトタイプを書いていないので、コピー&ペースト時には順序に注意。

コンパイル&実行

コンパイルして実行。郵便番号を入力し(3桁と7桁に対応)検索すると該当する住所が出力されます。

実行イメージ

感想・考察

sqlite3_open(), sqlite3_close(), sqlite3_exec()の3つの関数を駆使すれば基本的な操作は網羅できる。呼び出しもCの関数なので新しい知識もいらず、簡単といえる。

その他の関数を利用すればもっと細かく利用することが可能なようだ。

リンク


2007-01-28 komina