1

SQLite では、SELECTステートメントを準備してステップ実行を開始すると、結果の最後の行に到達する前に、ステップ実行中のステートメントに影響を与える別のステートメントを実行しSELECTます。期待される結果は何ですか?

何が起こるかについて SQLite のドキュメントには何も見つかりませんが、マルチスレッド環境でプログラミングする場合は非常に一般的なケースのようです。

以下は、状況を示すためにコンパイルして Windows で実行できる c++ ファイルです。

#include "stdafx.h"
#include "sqlite3.h"
#include <Windows.h>
#include <iostream>
#include <Knownfolders.h>
#include <Shlobj.h>
#include <wchar.h>
#include <comdef.h>

using namespace std;

int exec_sql(sqlite3 *db, const char* sql)
{
    char *errmsg;
    int result = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
    if (result != SQLITE_OK) {
        cout << errmsg << endl;
        return -1;
    }

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    cout << "Running jsqltst with SQLite version: ";
    cout << sqlite3_libversion();
    cout << endl;

    PWSTR userhome;

    if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Profile, NULL, NULL, &userhome))) {
        cout << "Failed getting user home dir\n";
        return -1;
    }

    wcout << "User home: " << userhome << endl;

    wchar_t *ws1 = userhome, *ws2 = L"\\test.sqlite";
    wstring dbpath_str(ws1);
    dbpath_str += wstring(ws2);
    _bstr_t dbpath(dbpath_str.c_str());

    cout << "DB path: " << dbpath << endl;

    sqlite3 *db;

    int result = sqlite3_open_v2(dbpath, &db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);
    if (result != SQLITE_OK) {
        cout << sqlite3_errmsg(db) << endl;
        return -1;
    }

    const char * create_stmt = "CREATE TABLE IF NOT EXISTS atable (id INTEGER PRIMARY KEY, name TEXT, number INTEGER);";
    if (exec_sql(db, create_stmt) != 0) {
        return -1;
    }

    const char * delete_stmt = "DELETE FROM atable;";
    if (exec_sql(db, delete_stmt) != 0) {
        return -1;
    }

    const char * insert_stmt = "INSERT INTO atable (name,number) VALUES ('Beta',77),('Alpha',99);";
    if (exec_sql(db, insert_stmt) != 0) {
        return -1;
    }

    sqlite3_stmt* select_ss;
    const char * select_stmt = "SELECT * FROM atable;";
    result = sqlite3_prepare_v2(db, select_stmt, -1, &select_ss, NULL);
    if (result != SQLITE_OK) {
        cout << sqlite3_errmsg(db) << endl;
        return -1;
    }

    int i = 0;
    boolean gotrow;
    do {
        result = sqlite3_step(select_ss);
        gotrow = result == SQLITE_ROW;
        if (gotrow) {
            i++;
            cout << "I got a row!" << endl;

            if (i == 1) {
                if (exec_sql(db, insert_stmt) != 0) {
                    return -1;
                }
            }
        }
    } while (gotrow);

    cout << "Last result: " << result << ", errstr: " << sqlite3_errstr(result) << endl;

    result = sqlite3_finalize(select_ss);
    if (result != SQLITE_OK) {
        cout << sqlite3_errmsg(db) << endl;
        return -1;
    }

    return 0;
}
4

1 に答える 1

3

同じトランザクション内の同時実行ステートメントに対する SQLite の動作は、文書化も定義もされていません。

これまで見てきたように、SELECTのカーソルがまだテーブルのその部分に到達していないときに、新しく挿入されたレコードが表示される場合があります。ただし、SQLite がソートまたはグループ化のために一時的な結果テーブルを作成する必要がある場合、後でテーブルに変更を加えてもその結果には反映されません。一時テーブルがあるかどうかは、クエリ オプティマイザーによる決定に依存する可能性があるため、多くの場合、これは予測できません。

複数のスレッドが同じ接続にアクセスする場合、SQLite はsqlite3_step呼び出しごとに DB をロックします。これによりデータの破損が防止されますが、最後のアクティブなステートメントが終了すると自動トランザクションが終了し、COMMIT他のアクティブなステートメントがある場合は明示的なトランザクションが失敗するという問題が残ります。

マルチスレッド プログラムでは、スレッドごとに (少なくとも) 1 つの接続を使用する方が適切です。

于 2013-04-24T09:21:28.940 に答える