I found a bug in latest (3.7.14.1) with a very specific #define that can causes 
a database corruption after truncation because of missing backup pages.

If you specify this define:
#define SQLITE_OMIT_AUTOVACUUM

And then execute a VACUUM operation that shrinks the database, due to the 
#ifndef at sqlite3.c:42830

The code that would otherwise proceed to backup pages before truncation will 
not be executed.

Even the comment is wrong, it CAN and WILL happen for a normal vacuum too, not 
just in auto vacuum.

/* If this transaction has made the database smaller, then all pages
      ** being discarded by the truncation must be written to the journal
      ** file. This can only happen in auto-vacuum mode.
      **
      ** Before reading the pages with page numbers larger than the
      ** current value of Pager.dbSize, set dbSize back to the value
      ** that it took at the start of the transaction. Otherwise, the
      ** calls to sqlite3PagerGet() return zeroed pages instead of
      ** reading data from the database file.
      */

So here is my fix... (just removed the #ifndef)

/* If this transaction has made the database smaller, then all pages
      ** being discarded by the truncation must be written to the journal
      ** file. This can happen in auto-vacuum mode and during a normal
      ** vacuum operation.
      **
      ** Before reading the pages with page numbers larger than the
      ** current value of Pager.dbSize, set dbSize back to the value
      ** that it took at the start of the transaction. Otherwise, the
      ** calls to sqlite3PagerGet() return zeroed pages instead of
      ** reading data from the database file.
      */
      if( pPager->dbSize<pPager->dbOrigSize
       && pPager->journalMode!=PAGER_JOURNALMODE_OFF
      ){
        Pgno i;                                   /* Iterator variable */
        const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
        const Pgno dbSize = pPager->dbSize;       /* Database image size */
        pPager->dbSize = pPager->dbOrigSize;
        for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){
          if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
            PgHdr *pPage;             /* Page to journal */
            rc = sqlite3PagerGet(pPager, i, &pPage);
            if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
            rc = sqlite3PagerWrite(pPage);
            sqlite3PagerUnref(pPage);
            if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
          }
        }
        pPager->dbSize = dbSize;
      }


You can test it with a really simple application that I included in 
attachment...

You just have to put a breakpoint at sqlite3.c:56747 and step over it and then 
restart the app right there.

The next integrity_check will fail completely :)

Thanks

Danny Couture
Technical Architect
Ubisoft Montreal


#include "stdafx.h"

//DON'T FORGET TO COMPILE SQLITE WITH #define SQLITE_OMIT_AUTOVACUUM
#include "sqlite3.h"

int callback(void *, int argc, char ** argv, char ** x)
{
    for (int i = 0; i < argc; ++i)
        printf("%s, ", argv[i]);

    printf("\n");

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    sqlite3 * db;
    sqlite3_open("test.db", &db);

    char * errorMsg;

    sqlite3_exec(db, "PRAGMA integrity_check", callback, 0, &errorMsg);

    sqlite3_exec(db, "CREATE TABLE test (Key INT, Test DOUBLE, Text 
VARCHAR(1024))", callback, 0, &errorMsg);

    sqlite3_exec(db, "BEGIN", callback, 0, &errorMsg);
    
    //add some stuff
    char query[1024];
    for (int i = 0; i < 100000; ++i)
    {
        sprintf_s(query, "INSERT INTO test VALUES(%d, %d.5, \"%d\")", i, i, i);
        sqlite3_exec(db, query, callback, 0, &errorMsg);
    }

    sqlite3_exec(db, "COMMIT", callback, 0, &errorMsg);

    //remove some of the stuff so the vacuum shrinks the DB
    sqlite3_exec(db, "DELETE FROM test WHERE Key > 5000", callback, 0, 
&errorMsg);

    //add a breakpoint at sqlite3.c:58390 and restart the application right 
there.
    //the next integrity_check will fail completely...
    sqlite3_exec(db, "VACUUM", callback, 0, &errorMsg);

        return 0;
}

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

Reply via email to