1

In SQLite if I prepare a SELECT statement and begin stepping through it, then before the last row of the results is reached I execute another statement that has an effect on the SELECT statement that I am stepping through, what is the expected result?

I can't find anything in the SQLite documentation about what is supposed to happen but it seems like an extremely common case when programming in a multi-threaded environment.

Below is a c++ file that can be compiled and run on Windows to demonstrate the situation.

#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调用时锁定数据库。这可以防止数据损坏,但是您仍然会遇到自动事务在其最后一个活动语句结束时结束的问题,并且COMMIT如果有其他活动语句,显式事务将失败。

多线程程序最好每个线程使用(至少)一个连接。

于 2013-04-24T09:21:28.940 回答