BCB/SQLite3を使う

BCBでSQLite3を簡単に利用する方法を模索。

探す

同じようなことを考えてた人のページを参考にします。

SQLite DbExpress driver
dbExpress用のドライバ。ダウンロードするには登録が必要です。
A simple Delphi wrapper for SQLite 3
Delphiで書かれたSQLite3のラッパー。
Delphi SQLite Components
Deplhi用コンポーネント。BCBで使えるか分かりません。

Simple Delphi Wrapperを使う

「Simple Delphi Wrapper」を使ってみることにする。

準備

  • VCLアプリケーションのプロジェクトを新規作成。
  • ダウンロードしたアーカイブからSQLite3.pasSQLiteTable3.pasをコピーしてくる。
  • プロジェクトにSQLite3.pasSQLiteTable3.pasを追加する。
  • (1回コンパイルするとSQLite3.hpp,SQLiteTable3.hppが生成される)
  • sqlite3.dllを実行ファイルが作られるフォルダへコピー。
  • インポートライブラリ作成する。
> implib sqlite3.lib .\Debug_Build\sqlite3.dll
  • ソースのいずれかに(Project1.cppが分かりやすい)、sqlite3.libとのリンクを明示する。リンカのライブラリパスを設定するのも忘れずに。
#pragma link "sqlite3.lib"
  • コンパイルしてエラーが出ないことを確認する。
  • SQLiteTable3.hpp内からSQLite3.hppをインクルードする部分が「#include <SQLite3.hpp>」となっているので「#include "SQLite3.hpp"」と修正する。

ここでインポートライブラリ作成時のオプション指定がSQLite3を試すと異なっているのに気づいた方はスルドイ。BCCでコンパイルすると外部に出る関数名はアンダースコアが自動的に付いてしまう。WINAPIで装飾するとそれを防ぐことが出来るようだ。SQLite3を試すではSQLite3のヘッダを出来るだけ変更したくなかったので、インポートライブラリ側でアンダースコアを吸収している。
今回はDelphi側からのDLLの呼び出し定義でアンダースコア無しの関数名を要求しているために、インポートライブラリも標準のアンダースコア無し版を作成している。

メイン記述

というわけで作成したプログラムがコレ。基本的な検索・追加・更新・削除の動作を実装してます。

主な手順

  1. TSQLiteDatabaseオブジェクト生成。
  2. 検索の場合はGetTable()メソッドを使用し、結果をTSQLiteTableオブジェクトで受け取る。
  3. その他のSQL文はExecSQL()メソッドで実行する。
  4. TSQLiteTableオブジェクトがある場合はdeleteで解放。
  5. TSQLiteDatabaseオブジェクトをdeleteで解放。

TSQLiteDatabaseクラス

プロパティ 解説
bool isTransactionOpen トランザクション処理の途中の場合にtrue
メソッド 解説
TSQLiteTable *GetTable(const AnsiString SQL) SQLに検索用SQL文を入れて呼び出す。検索の結果をTSQLiteTableオブジェクトを生成して返す。
void ExecSQL(const AnsiString SQL) SQL文を実行する。
__int64 GetTableValue(const AnsiString SQL) SQLによる検索結果の1行目1カラム目の値を整数値として返す。
AnsiString GetTableString(const AnsiString SQL) SQLによる検索結果の1行目1カラム目の値を文字列として返す。
void UpdateBlob(const AnsiString SQL, Classes::TStream* BlobData) SQL文の「?」の部分にBLOB型をバインドして実行する。
void BeginTransaction(void) トランザクション開始。
void Commit(void) コミットする。
void Rollback(void) ロールバックする。
bool TableExists(AnsiString TableName) 指定のテーブルが存在するか。存在する場合はtrue
__int64 GetLastInsertRowID(void) 一番最近に挿入した行番号を返す。
void SetTimeout(int Value) タイムアウト時間(msec)を設定する。テーブルがロックされていた場合にSQLITE_BUSYを返すまでの待ち時間を設定する。
AnsiString version() バージョン文字列を返す。
__fastcall修飾子は省略してます。

TSQLiteTableクラス

プロパティ 解説
bool EOF 現在の行が保有している行数を超えている場合にtrue
bool BOF 現在の行が先頭行の場合にtrue
AnsiString Fields[unsigned I] I番目のカラムのデータを文字列として返す。NULL値なら0文字の文字列を返す。
AnsiString FieldByName[AnsiString FieldName] カラム名からデータを取得し、文字列として返す。NULL値なら0文字の文字列を返す。
int FieldIndex[AnsiString FieldName] カラム名から何番目のカラムかを返す。
AnsiString Columns[int I] I番目のカラムのカラム名を取得する。
unsigned ColCount データのカラム数を返す。
unsigned RowCount 保有しているデータの行数を返す。
unsigned Row 現在の行を返す。
int Count 保有しているデータの行数を返す。
int CountResult 0番目のカラムの値をint型で返す。
メソッド 解説
__int64 FieldAsInteger(unsigned I) I番目のカラムのデータをInteger型として取得し__int64型で返す。
TMemoryStream* FieldAsBlob(unsigned I) I番目のカラムのデータをBLOB型として取得しTMemoryStreamオブジェクトを生成・格納して返す。
AnsiString FieldAsBlobText(unsigned I) I番目のカラムのデータをBLOB型として取得しAnsiString型で返す。
bool FieldIsNull(unsigned I) I番目のカラムのデータがNULL値ならtrueを返す。
AnsiString FieldAsString(unsigned I) I番目のカラムのデータをText型として取得しAnsiString型で返す。
double FieldAsDouble(unsigned I) I番目のカラムのデータをNumeric型またはInteger型として取得しdouble型で返す。
bool Next(void) 次の行へ進む。
bool Previous(void) 前の行へ戻る。
__fastcall修飾子は省略してます。

ソース

  • 今回作成したプログラムのソース

課題および注意点

integerフィールドからデータ取得時に、FieldAsInteger()メソッドを使ったのだが「Not an integer or numeric field」例外が発生してしまった。
これはTSQLiteTableのコンストラクタ内でフィールドの型を値化する処理において、型名を大文字でしかチェックしていないことに起因する。テーブル作成時に型名を小文字で'integer'などとしてしまうとSqlite3_ColumnDeclType()関数はそのまま小文字で返してしまう。比較は大文字の'INTEGER'としかしていないので、あまりものの'TEXT'と解釈されてしまう。
テーブル作成時に型名に小文字を使わないようにするか、この処理部分でUpperCase処理を行うようにして対処しなくてはならない。


2007-02-24 komina