Igor Tandetnik wrote:
> On 2/4/2014 5:23 AM, Yuriy Kaminskiy wrote:
>> How sqlite is supposed to behave when
>> *) there are read-only transaction;
>> *) there are update transaction on other connection;
>> *) cache space is exhausted by update transaction;
>> *) sqlite was not able to upgrade RESERVED lock to EXCLUSIVE due to
>> presence of SHARED lock, even after waiting for busy_timeout;
>> ?
> 
> SQLite should return SQLITE_BUSY error for the statement that triggered
> the cache spill. From the documentation of sqlite3_step:

> SQLITE_BUSY means that the database engine was unable to acquire the
> database locks it needs to do its job. If the statement is a COMMIT or
> occurs outside of an explicit transaction, then you can retry the
> statement. If the statement is not a COMMIT and occurs within an
> explicit transaction then you should rollback the transaction before
> continuing.
> 
> This second part is precisely for the case where a statement other than
> a COMMIT wants to acquire EXCLUSIVE lock due to cache overflow, and
> fails. If I recall previous discussions of this topic correctly, SQLite
> cannot guarantee that the in-memory cache is in a good state at this
> point, and so the only safe course of action is to discard it by rolling
> back.

But in fact, it *does not return error* (except for COMMIT).
Verified on sqlite 3.7.7 and 3.8.2.

>> It can either fail update statement (but I don't see that in test
>> below: it
>> sleeps for busy_timeout, but *does not* return error), or keep on
>> storing data
>> in memory [it *does not* write anything] (thus using over specified
>> cache size?
>> [and eventually overflow virtual memory?]), or what?
> 
> It's supposed to return an error. There might be some flaw in your test;
> I myself don't speak Perl (and am too lazy to conduct a test of my own).

Phew. Do you speak C? Enjoy.

#include <stdio.h>
#include <sqlite3.h>
#include <assert.h>
#include <inttypes.h>
int main(int argc, char *argv[])
{
    sqlite3 *dbh;
    sqlite3 *dbh2;
    sqlite3_stmt *sth;
    sqlite3_stmt *sth2;
    sqlite3_stmt *ins_sth;
    char *errmsg;
    int rc;
    int i, j;
    rc = sqlite3_open("ttt.db3", &dbh);
    assert(rc == SQLITE_OK);
    rc = sqlite3_open("ttt.db3", &dbh2);
    assert(rc == SQLITE_OK);
    rc = sqlite3_busy_timeout(dbh, 10000);
    assert(rc == SQLITE_OK);
    rc = sqlite3_busy_timeout(dbh2, 10000);
    assert(rc == SQLITE_OK);
#define sql_exec(dbh, sql) do { \
    rc = sqlite3_exec(dbh, sql, NULL, NULL, &errmsg); \
    printf("exec: %p, '%s' -> %d\n", dbh, sql, rc); \
    assert(errmsg == NULL); \
    sqlite3_free(errmsg); \
} while(0)
    sql_exec(dbh, "PRAGMA page_size = 1024");
    sql_exec(dbh, "PRAGMA cache_size = 4");
    sql_exec(dbh, "CREATE TABLE IF NOT EXISTS t (i)");
    sql_exec(dbh2, "PRAGMA cache_size = 4");
    rc = sqlite3_prepare_v2(dbh, "SELECT * FROM t", -1, &sth, NULL);
    assert(rc == SQLITE_OK);
    rc = sqlite3_prepare_v2(dbh2, "SELECT * FROM t", -1, &sth2, NULL);
    assert(rc == SQLITE_OK);
    rc = sqlite3_prepare_v2(dbh, "INSERT INTO t VALUES(?)", -1, &ins_sth, NULL);
    assert(rc == SQLITE_OK);
    sql_exec(dbh, "BEGIN IMMEDIATE");
    sql_exec(dbh2, "BEGIN");
    rc = sqlite3_step(sth2);
    assert(rc == SQLITE_DONE || rc == SQLITE_ROW);
    j = 0;
    printf("insert...\r"); fflush(stdout);
    for(i = 0; i < 1000; i++) {
        rc = sqlite3_bind_int(ins_sth, 1, i);
        assert(rc == SQLITE_OK);
        rc = sqlite3_step(ins_sth);
        assert(rc == SQLITE_DONE);
        j += sqlite3_changes(dbh);
        printf("insert: %04d\r", i); fflush(stdout);
        rc = sqlite3_reset(ins_sth);
        assert(rc == SQLITE_OK);
    }
    printf("insert done\n");
    printf("j = %04d\n", j);
    i = 0;
    printf("select...\r"); fflush(stdout);
    while((rc = sqlite3_step(sth)) == SQLITE_ROW) {
        i++;
        printf("select: %04d\r", i); fflush(stdout);
    }
    printf("select done\n");
    printf("i = %04d\n", i);
    assert(rc == SQLITE_DONE);
    rc = sqlite3_reset(sth2);
    assert(rc == SQLITE_OK);
    rc = sqlite3_reset(sth);
    assert(rc == SQLITE_OK);
    sql_exec(dbh2, "COMMIT");
    sql_exec(dbh, "COMMIT");
    rc = sqlite3_finalize(sth); sth = NULL;
    assert(rc == SQLITE_OK);
    rc = sqlite3_finalize(sth2); sth2 = NULL;
    assert(rc == SQLITE_OK);
    rc = sqlite3_finalize(ins_sth); ins_sth = NULL;
    assert(rc == SQLITE_OK);
    rc = sqlite3_close(dbh); dbh = NULL;
    assert(rc == SQLITE_OK);
    rc = sqlite3_close(dbh2); dbh2 = NULL;
    assert(rc == SQLITE_OK);
    return 0;
}

_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to