/ Check-in [e07923128b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:If a call to sqlite3_snapshot_open() fails because the requested snapshot no longer exists, return SQLITE_ERROR_SNAPSHOT instead of SQLITE_BUSY_SNAPSHOT.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e07923128bb164efbafde29d49175b61f2ef44b2dfac5ae4ed61937945dfcf4c
User & Date: dan 2018-08-31 19:00:16
Context
2018-08-31
23:22
New hyperlink on the README.md file. check-in: c663961e34 user: drh tags: trunk
19:00
If a call to sqlite3_snapshot_open() fails because the requested snapshot no longer exists, return SQLITE_ERROR_SNAPSHOT instead of SQLITE_BUSY_SNAPSHOT. check-in: e07923128b user: dan tags: trunk
2018-08-30
20:28
Try to identify the places in WAL code where thread-safety depends on the underlying architecture supporting atomic load and store of aligned 32-bit values. check-in: 47d44be4a6 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

1325
1326
1327
1328
1329
1330
1331

1332
1333
1334
1335
1336
1337
1338
const char *sqlite3ErrName(int rc){
  const char *zName = 0;
  int i, origRc = rc;
  for(i=0; i<2 && zName==0; i++, rc &= 0xff){
    switch( rc ){
      case SQLITE_OK:                 zName = "SQLITE_OK";                break;
      case SQLITE_ERROR:              zName = "SQLITE_ERROR";             break;

      case SQLITE_INTERNAL:           zName = "SQLITE_INTERNAL";          break;
      case SQLITE_PERM:               zName = "SQLITE_PERM";              break;
      case SQLITE_ABORT:              zName = "SQLITE_ABORT";             break;
      case SQLITE_ABORT_ROLLBACK:     zName = "SQLITE_ABORT_ROLLBACK";    break;
      case SQLITE_BUSY:               zName = "SQLITE_BUSY";              break;
      case SQLITE_BUSY_RECOVERY:      zName = "SQLITE_BUSY_RECOVERY";     break;
      case SQLITE_BUSY_SNAPSHOT:      zName = "SQLITE_BUSY_SNAPSHOT";     break;







>







1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
const char *sqlite3ErrName(int rc){
  const char *zName = 0;
  int i, origRc = rc;
  for(i=0; i<2 && zName==0; i++, rc &= 0xff){
    switch( rc ){
      case SQLITE_OK:                 zName = "SQLITE_OK";                break;
      case SQLITE_ERROR:              zName = "SQLITE_ERROR";             break;
      case SQLITE_ERROR_SNAPSHOT:     zName = "SQLITE_ERROR_SNAPSHOT";    break;
      case SQLITE_INTERNAL:           zName = "SQLITE_INTERNAL";          break;
      case SQLITE_PERM:               zName = "SQLITE_PERM";              break;
      case SQLITE_ABORT:              zName = "SQLITE_ABORT";             break;
      case SQLITE_ABORT_ROLLBACK:     zName = "SQLITE_ABORT_ROLLBACK";    break;
      case SQLITE_BUSY:               zName = "SQLITE_BUSY";              break;
      case SQLITE_BUSY_RECOVERY:      zName = "SQLITE_BUSY_RECOVERY";     break;
      case SQLITE_BUSY_SNAPSHOT:      zName = "SQLITE_BUSY_SNAPSHOT";     break;

Changes to src/sqlite.h.in.

468
469
470
471
472
473
474

475
476
477
478
479
480
481
....
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
** on a per database connection basis using the
** [sqlite3_extended_result_codes()] API.  Or, the extended code for
** the most recent error can be obtained using
** [sqlite3_extended_errcode()].
*/
#define SQLITE_ERROR_MISSING_COLLSEQ   (SQLITE_ERROR | (1<<8))
#define SQLITE_ERROR_RETRY             (SQLITE_ERROR | (2<<8))

#define SQLITE_IOERR_READ              (SQLITE_IOERR | (1<<8))
#define SQLITE_IOERR_SHORT_READ        (SQLITE_IOERR | (2<<8))
#define SQLITE_IOERR_WRITE             (SQLITE_IOERR | (3<<8))
#define SQLITE_IOERR_FSYNC             (SQLITE_IOERR | (4<<8))
#define SQLITE_IOERR_DIR_FSYNC         (SQLITE_IOERR | (5<<8))
#define SQLITE_IOERR_TRUNCATE          (SQLITE_IOERR | (6<<8))
#define SQLITE_IOERR_FSTAT             (SQLITE_IOERR | (7<<8))
................................................................................
** must have no active statements (SELECT statements that have been passed
** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). 
** SQLITE_ERROR is returned if either of these conditions is violated, or
** if schema S does not exist, or if the snapshot object is invalid.
**
** ^A call to sqlite3_snapshot_open() will fail to open if the specified
** snapshot has been overwritten by a [checkpoint]. In this case 
** SQLITE_BUSY_SNAPSHOT is returned.
**
** If there is already a read transaction open when this function is 
** invoked, then the same read transaction remains open (on the same
** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT
** is returned. If another error code - for example SQLITE_PROTOCOL or an
** SQLITE_IOERR error code - is returned, then the final state of the
** read transaction is undefined. If SQLITE_OK is returned, then the 
** read transaction is now open on database snapshot P.
**
** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
** database connection D does not know that the database file for







>







 







|



|







468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
....
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
** on a per database connection basis using the
** [sqlite3_extended_result_codes()] API.  Or, the extended code for
** the most recent error can be obtained using
** [sqlite3_extended_errcode()].
*/
#define SQLITE_ERROR_MISSING_COLLSEQ   (SQLITE_ERROR | (1<<8))
#define SQLITE_ERROR_RETRY             (SQLITE_ERROR | (2<<8))
#define SQLITE_ERROR_SNAPSHOT          (SQLITE_ERROR | (3<<8))
#define SQLITE_IOERR_READ              (SQLITE_IOERR | (1<<8))
#define SQLITE_IOERR_SHORT_READ        (SQLITE_IOERR | (2<<8))
#define SQLITE_IOERR_WRITE             (SQLITE_IOERR | (3<<8))
#define SQLITE_IOERR_FSYNC             (SQLITE_IOERR | (4<<8))
#define SQLITE_IOERR_DIR_FSYNC         (SQLITE_IOERR | (5<<8))
#define SQLITE_IOERR_TRUNCATE          (SQLITE_IOERR | (6<<8))
#define SQLITE_IOERR_FSTAT             (SQLITE_IOERR | (7<<8))
................................................................................
** must have no active statements (SELECT statements that have been passed
** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). 
** SQLITE_ERROR is returned if either of these conditions is violated, or
** if schema S does not exist, or if the snapshot object is invalid.
**
** ^A call to sqlite3_snapshot_open() will fail to open if the specified
** snapshot has been overwritten by a [checkpoint]. In this case 
** SQLITE_ERROR_SNAPSHOT is returned.
**
** If there is already a read transaction open when this function is 
** invoked, then the same read transaction remains open (on the same
** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT
** is returned. If another error code - for example SQLITE_PROTOCOL or an
** SQLITE_IOERR error code - is returned, then the final state of the
** read transaction is undefined. If SQLITE_OK is returned, then the 
** read transaction is now open on database snapshot P.
**
** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
** database connection D does not know that the database file for

Changes to src/wal.c.

2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
....
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
      */
      rc = walLockShared(pWal, WAL_CKPT_LOCK);

      if( rc==SQLITE_OK ){
        /* Check that the wal file has not been wrapped. Assuming that it has
        ** not, also check that no checkpointer has attempted to checkpoint any
        ** frames beyond pSnapshot->mxFrame. If either of these conditions are
        ** true, return SQLITE_BUSY_SNAPSHOT. Otherwise, overwrite pWal->hdr
        ** with *pSnapshot and set *pChanged as appropriate for opening the
        ** snapshot.  */
        if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
         && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
        ){
          assert( pWal->readLock>0 );
          memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
          *pChanged = bChanged;
        }else{
          rc = SQLITE_BUSY_SNAPSHOT;
        }

        /* Release the shared CKPT lock obtained above. */
        walUnlockShared(pWal, WAL_CKPT_LOCK);
        pWal->minFrame = 1;
      }

................................................................................
  int rc;
  rc = walLockShared(pWal, WAL_CKPT_LOCK);
  if( rc==SQLITE_OK ){
    WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
    if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
     || pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
    ){
      rc = SQLITE_BUSY_SNAPSHOT;
      walUnlockShared(pWal, WAL_CKPT_LOCK);
    }
  }
  return rc;
}

/*







|









|







 







|







2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
....
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
      */
      rc = walLockShared(pWal, WAL_CKPT_LOCK);

      if( rc==SQLITE_OK ){
        /* Check that the wal file has not been wrapped. Assuming that it has
        ** not, also check that no checkpointer has attempted to checkpoint any
        ** frames beyond pSnapshot->mxFrame. If either of these conditions are
        ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
        ** with *pSnapshot and set *pChanged as appropriate for opening the
        ** snapshot.  */
        if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
         && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
        ){
          assert( pWal->readLock>0 );
          memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
          *pChanged = bChanged;
        }else{
          rc = SQLITE_ERROR_SNAPSHOT;
        }

        /* Release the shared CKPT lock obtained above. */
        walUnlockShared(pWal, WAL_CKPT_LOCK);
        pWal->minFrame = 1;
      }

................................................................................
  int rc;
  rc = walLockShared(pWal, WAL_CKPT_LOCK);
  if( rc==SQLITE_OK ){
    WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
    if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
     || pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
    ){
      rc = SQLITE_ERROR_SNAPSHOT;
      walUnlockShared(pWal, WAL_CKPT_LOCK);
    }
  }
  return rc;
}

/*

Changes to test/snapshot.test.

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

  do_test $tn.$tn.3.3.2 {
    snapshot_free $snapshot
    execsql COMMIT
  } {}

  #-------------------------------------------------------------------------
  # Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
  # no longer exists because the wal file has been checkpointed.
  #
  #   1. Reading a snapshot from the middle of a wal file is not possible
  #      after the wal file has been checkpointed.
  #
  #   2. That a snapshot from the end of a wal file can not be read once
  #      the wal file has been wrapped.
................................................................................
  do_test $tn.4.1.3 {
    execsql { 
      COMMIT;
      PRAGMA wal_checkpoint;
      BEGIN;
    }
    list [catch {snapshot_open db main $snapshot} msg] $msg
  } {1 SQLITE_BUSY_SNAPSHOT}
  do_test $tn.4.1.4 {
    snapshot_free $snapshot
    execsql COMMIT
  } {}

  do_test $tn.4.2.1 {
    execsql {
................................................................................
  do_test $tn.4.2.3 {
    execsql {
      COMMIT;
      INSERT INTO t3 VALUES('e', 't');
      BEGIN;
    }
    list [catch {snapshot_open db main $snapshot} msg] $msg
  } {1 SQLITE_BUSY_SNAPSHOT}
  do_test $tn.4.2.4 {
    snapshot_free $snapshot
  } {}

  #-------------------------------------------------------------------------
  # Check that SQLITE_BUSY is returned if a checkpoint is running when
  # sqlite3_snapshot_open() is called.







|







 







|







 







|







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

  do_test $tn.$tn.3.3.2 {
    snapshot_free $snapshot
    execsql COMMIT
  } {}

  #-------------------------------------------------------------------------
  # Check that SQLITE_ERROR_SNAPSHOT is returned if the specified snapshot
  # no longer exists because the wal file has been checkpointed.
  #
  #   1. Reading a snapshot from the middle of a wal file is not possible
  #      after the wal file has been checkpointed.
  #
  #   2. That a snapshot from the end of a wal file can not be read once
  #      the wal file has been wrapped.
................................................................................
  do_test $tn.4.1.3 {
    execsql { 
      COMMIT;
      PRAGMA wal_checkpoint;
      BEGIN;
    }
    list [catch {snapshot_open db main $snapshot} msg] $msg
  } {1 SQLITE_ERROR_SNAPSHOT}
  do_test $tn.4.1.4 {
    snapshot_free $snapshot
    execsql COMMIT
  } {}

  do_test $tn.4.2.1 {
    execsql {
................................................................................
  do_test $tn.4.2.3 {
    execsql {
      COMMIT;
      INSERT INTO t3 VALUES('e', 't');
      BEGIN;
    }
    list [catch {snapshot_open db main $snapshot} msg] $msg
  } {1 SQLITE_ERROR_SNAPSHOT}
  do_test $tn.4.2.4 {
    snapshot_free $snapshot
  } {}

  #-------------------------------------------------------------------------
  # Check that SQLITE_BUSY is returned if a checkpoint is running when
  # sqlite3_snapshot_open() is called.

Changes to test/snapshot2.test.

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
230
231
232
233
234
235
236
237
238
239
240
241
  sqlite3_db_config db NO_CKPT_ON_CLOSE 1
  db close
  sqlite3 db test.db

  execsql {SELECT * FROM sqlite_master}
  execsql BEGIN
  list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}

do_test 2.3 {
  execsql COMMIT
  sqlite3_snapshot_recover db main
  execsql BEGIN
  sqlite3_snapshot_open_blob db main $snap
  execsql { SELECT * FROM t1 }
................................................................................
  sqlite3_db_config db NO_CKPT_ON_CLOSE 1
  db close
  sqlite3 db test.db

  sqlite3_snapshot_recover db main
  execsql BEGIN
  list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}

#-------------------------------------------------------------------------
# Check that calling sqlite3_snapshot_recover() does not confuse the
# pager cache.
reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
................................................................................
  db2 eval { SELECT * FROM t2 ; END }
} {abc def ghi}

do_test 5.4 {
  execsql { INSERT INTO t2 VALUES('jkl') } 
  execsql BEGIN db2
  list [catch { sqlite3_snapshot_open_blob db2 main $snap } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}


finish_test








|







 







|







 







|




106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
230
231
232
233
234
235
236
237
238
239
240
241
  sqlite3_db_config db NO_CKPT_ON_CLOSE 1
  db close
  sqlite3 db test.db

  execsql {SELECT * FROM sqlite_master}
  execsql BEGIN
  list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}

do_test 2.3 {
  execsql COMMIT
  sqlite3_snapshot_recover db main
  execsql BEGIN
  sqlite3_snapshot_open_blob db main $snap
  execsql { SELECT * FROM t1 }
................................................................................
  sqlite3_db_config db NO_CKPT_ON_CLOSE 1
  db close
  sqlite3 db test.db

  sqlite3_snapshot_recover db main
  execsql BEGIN
  list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}

#-------------------------------------------------------------------------
# Check that calling sqlite3_snapshot_recover() does not confuse the
# pager cache.
reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
................................................................................
  db2 eval { SELECT * FROM t2 ; END }
} {abc def ghi}

do_test 5.4 {
  execsql { INSERT INTO t2 VALUES('jkl') } 
  execsql BEGIN db2
  list [catch { sqlite3_snapshot_open_blob db2 main $snap } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}


finish_test

Changes to test/snapshot3.test.

90
91
92
93
94
95
96
97
98
99
100
  execsql { PRAGMA wal_checkpoint = truncate }
  file size test.db-wal
} 0

do_test 1.8 {
  execsql BEGIN db3
  list [catch { sqlite3_snapshot_open_blob db3 main $snap } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}

finish_test








|



90
91
92
93
94
95
96
97
98
99
100
  execsql { PRAGMA wal_checkpoint = truncate }
  file size test.db-wal
} 0

do_test 1.8 {
  execsql BEGIN db3
  list [catch { sqlite3_snapshot_open_blob db3 main $snap } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}

finish_test

Changes to test/snapshot_up.test.

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

do_execsql_test 1.6 {
  SELECT * FROM t1
} {1 2 3 4 5 6 7 8 9 10 11 12}

do_test 1.7 {
  list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}

do_execsql_test 1.8 {
  SELECT * FROM t1
} {1 2 3 4 5 6 7 8 9 10 11 12}

do_test 1.9 {
  execsql { COMMIT ; BEGIN }
  list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}

do_test 1.10 {
  execsql { COMMIT }
  execsql {
    PRAGMA wal_checkpoint;
    DELETE FROM t1 WHERE a = 1;
  } db2
................................................................................
do_test 1.12 {
  sqlite3_snapshot_open db main $::snap4
  execsql { SELECT * FROM t1 }
} {4 5 6 7 8 9 10 11 12 13 14 15}

do_test 1.13 {
  list [catch { sqlite3_snapshot_open db main $::snap3 } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}
do_test 1.14 {
  execsql { SELECT * FROM t1 }
} {4 5 6 7 8 9 10 11 12 13 14 15}

db close
db2 close
sqlite3 db test.db
do_execsql_test 1.15 {
  BEGIN;
    SELECT * FROM t1
} {7 8 9 10 11 12 13 14 15}
do_test 1.16 {
  list [catch { sqlite3_snapshot_open db main $::snap4 } msg] $msg
} {1 SQLITE_BUSY_SNAPSHOT}
do_execsql_test 1.17 { COMMIT }

sqlite3_snapshot_free $::snap1
sqlite3_snapshot_free $::snap2
sqlite3_snapshot_free $::snap3
sqlite3_snapshot_free $::snap4








|








|







 







|













|







71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

do_execsql_test 1.6 {
  SELECT * FROM t1
} {1 2 3 4 5 6 7 8 9 10 11 12}

do_test 1.7 {
  list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}

do_execsql_test 1.8 {
  SELECT * FROM t1
} {1 2 3 4 5 6 7 8 9 10 11 12}

do_test 1.9 {
  execsql { COMMIT ; BEGIN }
  list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}

do_test 1.10 {
  execsql { COMMIT }
  execsql {
    PRAGMA wal_checkpoint;
    DELETE FROM t1 WHERE a = 1;
  } db2
................................................................................
do_test 1.12 {
  sqlite3_snapshot_open db main $::snap4
  execsql { SELECT * FROM t1 }
} {4 5 6 7 8 9 10 11 12 13 14 15}

do_test 1.13 {
  list [catch { sqlite3_snapshot_open db main $::snap3 } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}
do_test 1.14 {
  execsql { SELECT * FROM t1 }
} {4 5 6 7 8 9 10 11 12 13 14 15}

db close
db2 close
sqlite3 db test.db
do_execsql_test 1.15 {
  BEGIN;
    SELECT * FROM t1
} {7 8 9 10 11 12 13 14 15}
do_test 1.16 {
  list [catch { sqlite3_snapshot_open db main $::snap4 } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}
do_execsql_test 1.17 { COMMIT }

sqlite3_snapshot_free $::snap1
sqlite3_snapshot_free $::snap2
sqlite3_snapshot_free $::snap3
sqlite3_snapshot_free $::snap4