SQLite3 の C インタフェースを使っていた時の話です。
以下のようなテーブルを定義したとします。
CREATE TABLE example (
id integer primary key autoincrements,
name varchar(1024) not null unique
);
関数 main の第1引数に文字列リストを渡し、それを example テーブルに登録していくプログラムを書きます。
当初は以下のようなプログラムを書いていました。
#include <iostream>
#include <fstream>
#include <string>
#include <sqlite3.h>
#pragma comment(lib, "sqlite3.dll")
class MyException {
int errcode;
std::wstring reason;
MyException(int errcode, const std::wstring &reason) : reason(reason) {}
public void write() {
std::wcout << this->reason << L"(" << this->errcode << L")" << std::wendl;
}
};
void trim(std::wstring &s) {
auto pos = s.find_first_not_of(L" \t\r\n");
if (pos != std::wstring::npos) {
s = s.substr(pos + 1);
}
pos = s.find_last_not_of(L" \t\r\n");
if (pos != std::wstring::npos) {
s = s.substr(0, pos);
}
}
int main(int argc, wchar_t **argv) {
int err;
sqlite3 *db;
sqlite3_stmt *stmt;
std::locatle("Japanese");
if (argc < 2) {
std::wcout << "Usage: example <string list file>" << std::wendl;
return 1;
}
err = sqlite3_open16("mydatabase.db", &db);
if (err != SQLITE_OK) {
std::wcout << "Error:" << err << std::wendl;
return 1;
}
try {
std::wifstream ifs(argv[1]);
err = sqlite3_exec(db, "BEGIN TRANSACTION", -1, nullptr, nullptr, nullptr);
if (err != SQLITE_OK) {
throw MyException(err, "Begin Transaction");
}
std::wstring line;
while (getline(ifs, line)) {
trim(line);
err = sqlite3_prepare16_v2(db, L"select id from example where name=?", -1, &stmt, nullptr);
if (err != SQLITE_OK) {
throw MyException(err, "select");
}
sqlite3_bind_text16(stmt, 1, line.data(), line.length(), nullptr);
int id = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
id = sqlite3_column_int(stmt, 0);
}
slite3_finalize(stmt);
if (id == 0) {
err = sqlite3_prepare16_v2(db, L"insert into example (name) values (?)", -1, &stmt, nullptr);
if (err != SQLITE_OK) {
throw MyException(err, "insert");
}
sqlite3_bind_text16(stmt, 1, line.data(), line.length(), nullptr);
sqlite3_step(stmt);
id = (int)sqlite3_last_insert_rowid(db);
std::wcout << L"New item " << line << L"(" << id << L")" << std::weol;
sqlite3_finalize(stmt);
}
}
sqlite3_exec(db, "COMMIT", -1, nullptr, nullptr, nullptr);
} catch (MyException e) {
e.write();
sqlite3_exec(db, "ROLLBACK", -1, nullptr, nullptr, nullptr);
}
sqlite3_close(db);
return 0;
}
しかし、これだと謎動作が。期待した文字列「ではない」文字列にマッチして id 値が出鱈目になってしまいます。
SQLITE3 のマニュアルにはちゃんと書いてありませんが、どうやら sqlite3_bind_text16() の第4引数はバイト数を与えないといけないようです。
sqlite3_bind_text16(stmt, 1, line.data(), line.size() * sizeof(wchar_t), nullptr);
ぐぐっても第4パラメータを -1 としている記事しかなかったので、書いてみました。
コメントを残す