SQLite Forum

Database WAL snapshot
Login
The error is because the WAL file does not contain a transaction and you cannot get a snapshot marker when there are no transactions in the WAL file.  So for your process to work, you need to actually commit that transaction.

`sqlite3_snapshot_get` records the location of the "commit marker" in the WAL file, so in order for it to work, there must be at least one "commit marker" which means you must have committed at least one transaction.

`sqlite3_snapshot_open` sets the "location" to the "commit marker" previously recorded by a call to `sqlite3_snapshot_get`.  If you "checkpoint" that commit marker out of the WAL, you can no longer open that recorded "location".

In other words, think of the WAL log as a queue of dinner plates waiting to get loaded into the dishwasher, each one with a number (1, 2, 3, 4, 5 ... and so on).  As you write (COMMIT) each transaction, a new "plate" (transaction) is added to the queue (this is a FIFO queue) with ever increasing plate numbers.

`sqlite3_snapshot_get` returns the number of the last added plate in the queue at the particular moment it is called.  If there are no plates in the queue (no transactions) then you will get an error that there is no plate number to remember.

When a WAL Checkpoint occurs, plates are removed from the other end of the stack and loaded into the dishwasher (the main database file) up until an "in use" plate (with an open transaction referring to it) -- it is a FIFO queue.

`sqlite3_snapshot_open` sets the current read location to the "plate" number previously saved with `sqlite3_snapshot_get`.  If that plate has been removed (put in the dishwasher) you can no longer go to that plate number because it is no longer in the queue, so you get an error.

This is how it worked last time someone asked and I looked at it.  Perhaps it has been updated to permit `sqlite3_snapshot_get` to work when the WAL is empty, but I don't know the answer to that.  Based on what you are reporting, it would look like there must be a transaction in the WAL file.

The following C code will ensure that you can snapshot against a database connection (you may want to fix/change some of the error handling):

```
int sqlite3_snapshot_ensure(sqlite3* db, char* zDbName)
{
    sqlite3_snapshot *sn = NULL;
    sqlite3_stmt* stmt;
    int rc = 0;
    int wal = 0;
    int persist = 1;
    char buf[128];

    // Prerequisite 1:  Make sure database is in journal_mode=wal
    //                  Disable AutoCheckpointing
    //                  Persist the WAL files

    if ((zDbName == NULL) || (strlen(zDbName) < 1) || (strlen(zDbName) > 64))
        return SQLITE_ERROR;
    snprintf(buf, sizeof(buf), "pragma %s.journal_mode", zDbName);
    if (sqlite3_prepare_v2(db, buf, -1, &stmt, NULL) != SQLITE_OK)
        return SQLITE_ERROR;
    if (sqlite3_step(stmt) != SQLITE_ROW)
    {
        sqlite3+finalize(stmt);
        return SQLITE_ERROR;
    }
    wal = !(strnicmp("wal", sqlite3_column_text(stmt, 0), 3));
    sqlite3_finalize(stmt);
    if (!wal)
    {
        snprintf(buf, sizeof(buf), "pragma %s.journal_mode=wal", zDbName);
        if (sqlite3_exec(db, buf, NULL, NULL, NULL) != SQLITE_OK)
            return SQLITE_ERROR;
    }
    snprintf(buf, sizeof(buf), "pragma %s.wal_autocheckpoint=0", zDbName);
    if (sqlite3_exec(db, buf, NULL, NULL, NULL) != SQLITE_OK)
        return SQLITE_ERROR;
    snprintf(buf, sizeof(buf), "pragma %s.synchronous=full", zDbName);
    if (sqlite3_exec(db, buf, NULL, NULL, NULL) != SQLITE_OK)
        return SQLITE_ERROR;
//    sqlite3_file_control(db, zDbName, SQLITE_FCNTL_PERSIST_WAL, &persist);

    // Test retrieving a snapshot structure

    sqlite3_exec(db, "BEGIN", NULL, NULL, NULL);
    rc = sqlite3_snapshot_get(db, zDbName, &sn);
    sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL);

    // Prerequisite 2:  Must have a transaction in the wal file

    if (!sn)
    {

        sqlite3_exec(db, "BEGIN IMMEDIATE", NULL, NULL, NULL);
        snprintf(buf, sizeof(buf), "pragma %s.user_version", zDbName);
        sqlite3_prepare_v2(db, buf, -1, &stmt, NULL);
        sqlite3_step(stmt);
        snprintf(buf, sizeof(buf), "pragma %s.user_version=%d", zDbName, sqlite3_column_int(stmt, 1));
        sqlite3_finalize(stmt);
        sqlite3_exec(db, buf, NULL, NULL, NULL);
        sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);

        // Test retrieving a snapshot structure

        sqlite3_exec(db, "BEGIN", NULL, NULL, NULL);
        rc = sqlite3_snapshot_get(db, zDbName, &sn);
        sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL);
    }

    // Free the snapshop structure if we have one
    // and return the result code from the attempt

    if (sn)
        sqlite3_snapshot_free(sn);

    return rc;
}
```