SQLite User Forum

Overflowing DbPage refcount
Login

Overflowing DbPage refcount

(1) By Roger Binns (rogerbinns) on 2023-03-19 19:46:57 [source]

I discovered this in 3.41 (and still in current fossil) by getting an assertion failure when stressing my statement cache. If 32,768 statements (SELECT x FROM table) have been stepped once (ie all sitting at SQLITE_ROW) then this assertion fails in a call to sqlite3_step:

sqlite3PagerGetData: Assertion `pPg->nRef>0 || pPg->pPager->memDb'

The reason is because pPg->nRef which is defined as i16 has wrapped around to -32,768.

To repeat in English:

  1. Create a table with one row of data
  2. Prepare SELECT * FROM table
  3. Run sqlite3_step once
  4. Go to 2

To repeat in code:

#include <stdio.h>
#include <assert.h>

/* avoid syscall on linux */
#define HAVE_MREMAP 0

/* turn sqlite assert on */
#define SQLITE_DEBUG 1
/* include the .c file so we can be certain which is used */
#include "sqlite3.c"

int main(void)
{
    int i, rc;
    sqlite3 *pdb;
    sqlite3_stmt *pstmt;

    rc = sqlite3_open("", &pdb);
    assert(rc == SQLITE_OK);

    rc = sqlite3_exec(pdb, "create table foo(x); insert into foo values(1)", NULL, NULL, NULL);
    assert(rc == SQLITE_OK);

    for (i = 0; i < 33000; i++)
    {
        fprintf(stderr, "%d\n", i);
        rc = sqlite3_prepare_v3(pdb, "select x from foo", -1, 0, &pstmt, NULL);
        assert(rc == SQLITE_OK);
        rc = sqlite3_step(pstmt);
        assert(rc == SQLITE_ROW);
    }

    return 0;
}

Tail end of running it:

32760
32761
32762
32763
32764
32765
32766
32767
t: sqlite3/sqlite3.c:62698: sqlite3PagerGetData: Assertion `pPg->nRef>0 || pPg->pPager->memDb' failed.

Program received signal SIGABRT, Aborted.