SQLite

Check-in [a49713db39]
Login

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

Overview
Comment:Add further test cases for the logic in sqlite3WalBeginReadTransaction().
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a49713db39d0d6940b368206d4e669aa69aa1fe5
User & Date: dan 2010-06-05 19:18:59.000
Context
2010-06-07
06:11
Add a couple of extra coverage tests for wal.c. (check-in: cfe60254df user: dan tags: trunk)
2010-06-05
19:18
Add further test cases for the logic in sqlite3WalBeginReadTransaction(). (check-in: a49713db39 user: dan tags: trunk)
18:34
Clarify the purpose of a test for a race-condition in sqlite3WalBeginReadTransaction(). (check-in: c041c6a978 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/wal.c.
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
  if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){
    /* The WAL has been completely backfilled (or it is empty).
    ** and can be safely ignored.
    */
    rc = walLockShared(pWal, WAL_READ_LOCK(0));
    sqlite3OsShmBarrier(pWal->pDbFd);
    if( rc==SQLITE_OK ){
      if( memcmp(pHdr, &pWal->hdr, sizeof(WalIndexHdr)) ){
        /* It is not safe to allow the reader to continue here if frames
        ** may have been appended to the log before READ_LOCK(0) was obtained.
        ** When holding READ_LOCK(0), the reader ignores the entire log file,
        ** which implies that the database file contains a trustworthy
        ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from
        ** happening, this is usually correct.
        **







|







1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
  if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){
    /* The WAL has been completely backfilled (or it is empty).
    ** and can be safely ignored.
    */
    rc = walLockShared(pWal, WAL_READ_LOCK(0));
    sqlite3OsShmBarrier(pWal->pDbFd);
    if( rc==SQLITE_OK ){
      if( memcmp((void *)pHdr, &pWal->hdr, sizeof(WalIndexHdr)) ){
        /* It is not safe to allow the reader to continue here if frames
        ** may have been appended to the log before READ_LOCK(0) was obtained.
        ** When holding READ_LOCK(0), the reader ignores the entire log file,
        ** which implies that the database file contains a trustworthy
        ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from
        ** happening, this is usually correct.
        **
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
    ** that the log file may have been wrapped by a writer, or that frames
    ** that occur later in the log than pWal->hdr.mxFrame may have been
    ** copied into the database by a checkpointer. If either of these things
    ** happened, then reading the database with the current value of
    ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
    ** instead.
    **
    ** This does not guarantee that the copy wal-index header is up to
    ** date before proceeding. This would not be possible without somehow
    ** blocking writers. It only guarantees that a damaging checkpoint or 
    ** log-wrap (either of which would require an exclusive lock on
    ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
    */
    sqlite3OsShmBarrier(pWal->pDbFd);
    if( pInfo->aReadMark[mxI]!=mxReadMark
     || memcmp(pHdr, &pWal->hdr, sizeof(WalIndexHdr))
    ){
      walUnlockShared(pWal, WAL_READ_LOCK(mxI));
      return WAL_RETRY;
    }else{
      pWal->readLock = mxI;
    }
  }







|
|
|





|







1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
    ** that the log file may have been wrapped by a writer, or that frames
    ** that occur later in the log than pWal->hdr.mxFrame may have been
    ** copied into the database by a checkpointer. If either of these things
    ** happened, then reading the database with the current value of
    ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
    ** instead.
    **
    ** This does not guarantee that the copy of the wal-index header is up to
    ** date before proceeding. That would not be possible without somehow
    ** blocking writers. It only guarantees that a dangerous checkpoint or 
    ** log-wrap (either of which would require an exclusive lock on
    ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
    */
    sqlite3OsShmBarrier(pWal->pDbFd);
    if( pInfo->aReadMark[mxI]!=mxReadMark
     || memcmp((void *)pHdr, &pWal->hdr, sizeof(WalIndexHdr))
    ){
      walUnlockShared(pWal, WAL_READ_LOCK(mxI));
      return WAL_RETRY;
    }else{
      pWal->readLock = mxI;
    }
  }
Changes to test/wal3.test.
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431

db close
T delete

#-------------------------------------------------------------------------
# When opening a read-transaction on a database, if the entire log has
# already been copied to the database file, the reader grabs a special
# kind of read lock (on aReadMark[0]). This test case tests the outcome
# of the following:
#
#   + The reader discovering that between the time when it determined 
#     that the log had been completely backfilled and the lock is obtained
#     that a writer has written to the log. In this case the reader should
#     acquire a different read-lock (not aReadMark[0]) and read the new
#     snapshot.
#







|
|







416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431

db close
T delete

#-------------------------------------------------------------------------
# When opening a read-transaction on a database, if the entire log has
# already been copied to the database file, the reader grabs a special
# kind of read lock (on aReadMark[0]). This set of test cases tests the 
# outcome of the following:
#
#   + The reader discovering that between the time when it determined 
#     that the log had been completely backfilled and the lock is obtained
#     that a writer has written to the log. In this case the reader should
#     acquire a different read-lock (not aReadMark[0]) and read the new
#     snapshot.
#
559
560
561
562
563
564
565





566


































































567
568
  expr {$sz2 == $sz1}
} {1}
 
db2 close
db close
T delete









































































finish_test








>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
  expr {$sz2 == $sz1}
} {1}
 
db2 close
db close
T delete

#-------------------------------------------------------------------------
# When opening a read-transaction on a database, if the entire log has
# not yet been copied to the database file, the reader grabs a read
# lock on aReadMark[x], where x>0. The following test cases experiment
# with the outcome of the following:
#
#   + The reader discovering that between the time when it read the
#     wal-index header and the lock was obtained that a writer has 
#     written to the log. In this case the reader should re-read the 
#     wal-index header and lock a snapshot corresponding to the new 
#     header.
#
#   + The value in the aReadMark[x] slot has been modified since it was
#     read.
#
catch {db close}
testvfs T -default 1
do_test wal3-7.1.1 {
  file delete -force test.db test.db-journal test.db wal
  sqlite3 db test.db
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE blue(red PRIMARY KEY, green);
  }
} {wal}

T script method_callback
T filter xOpen
proc method_callback {method args} {
  if {$method == "xOpen"} { return "reader" }
}
do_test wal3-7.1.2 {
  sqlite3 db2 test.db
  execsql { SELECT * FROM blue } db2
} {}

T filter xShmLock
set ::locks [list]
proc method_callback {method file handle spec} {
  if {$handle != "reader" } { return }
  if {$method == "xShmLock"} {
    catch { execsql { INSERT INTO blue VALUES(1, 2) } }
    catch { execsql { INSERT INTO blue VALUES(3, 4) } }
  }
  lappend ::locks $spec
}
do_test wal3-7.1.3 {
  execsql { SELECT * FROM blue } db2
} {1 2 3 4}
do_test wal3-7.1.4 {
  set ::locks
} {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}}

set ::locks [list]
proc method_callback {method file handle spec} {
  if {$handle != "reader" } { return }
  if {$method == "xShmLock"} {
    catch { execsql { INSERT INTO blue VALUES(5, 6) } }
  }
  lappend ::locks $spec
}
do_test wal3-7.2.1 {
  execsql { SELECT * FROM blue } db2
} {1 2 3 4 5 6}
do_test wal3-7.2.2 {
  set ::locks
} {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}}

db close
db2 close
T delete

finish_test