/ Check-in [72cb2e1a]
Login

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

Overview
Comment:Add some tests and fixes surrounding exclusive-access mode and the pager change-counter. (CVS 3716)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:72cb2e1a73cd09d32900bb473377f66ff55058fb
User & Date: danielk1977 2007-03-26 10:27:19
Context
2007-03-26
12:26
Run some malloc() tests with exclusive-access mode. (CVS 3717) check-in: 12745490 user: danielk1977 tags: trunk
10:27
Add some tests and fixes surrounding exclusive-access mode and the pager change-counter. (CVS 3716) check-in: 72cb2e1a user: danielk1977 tags: trunk
08:41
Add some documentation for pragma locking_mode. (CVS 3715) check-in: 394b174e user: danielk1977 tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.295 2007/03/26 08:05:12 danielk1977 Exp $
           21  +** @(#) $Id: pager.c,v 1.296 2007/03/26 10:27:19 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   #include "os.h"
    26     26   #include "pager.h"
    27     27   #include <assert.h>
    28     28   #include <string.h>
................................................................................
   923    923     int rc = SQLITE_OK;
   924    924     assert( !MEMDB );
   925    925     if( pPager->state<PAGER_RESERVED ){
   926    926       return SQLITE_OK;
   927    927     }
   928    928     sqlite3PagerStmtCommit(pPager);
   929    929     if( pPager->stmtOpen && !pPager->exclusiveMode ){
   930         -    if( pPager->exclusiveMode ){
          930  +    if( !pPager->exclusiveMode ){
   931    931         sqlite3OsClose(&pPager->stfd);
   932    932         pPager->stmtOpen = 0;
   933    933       }else{
   934    934         sqlite3OsTruncate(pPager->stfd, 0);
   935    935       }
   936    936     }
   937    937     if( pPager->journalOpen ){
................................................................................
  3893   3893   ** stored at byte 24 of the pager file.
  3894   3894   */
  3895   3895   static int pager_incr_changecounter(Pager *pPager){
  3896   3896     PgHdr *pPgHdr;
  3897   3897     u32 change_counter;
  3898   3898     int rc;
  3899   3899   
  3900         -  /* Open page 1 of the file for writing. */
  3901         -  rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
  3902         -  if( rc!=SQLITE_OK ) return rc;
  3903         -  rc = sqlite3PagerWrite(pPgHdr);
  3904         -  if( rc!=SQLITE_OK ) return rc;
  3905         -
  3906         -  /* Read the current value at byte 24. */
  3907         -  change_counter = retrieve32bits(pPgHdr, 24);
  3908         -
  3909         -  /* Increment the value just read and write it back to byte 24. */
  3910         -  change_counter++;
  3911         -  put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
  3912         -  pPager->iChangeCount = change_counter;
  3913         -
  3914         -  /* Release the page reference. */
  3915         -  sqlite3PagerUnref(pPgHdr);
  3916         -  pPager->changeCountDone = 1;
         3900  +  if( !pPager->changeCountDone ){
         3901  +    /* Open page 1 of the file for writing. */
         3902  +    rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
         3903  +    if( rc!=SQLITE_OK ) return rc;
         3904  +    rc = sqlite3PagerWrite(pPgHdr);
         3905  +    if( rc!=SQLITE_OK ) return rc;
         3906  +  
         3907  +    /* Read the current value at byte 24. */
         3908  +    change_counter = retrieve32bits(pPgHdr, 24);
         3909  +  
         3910  +    /* Increment the value just read and write it back to byte 24. */
         3911  +    change_counter++;
         3912  +    put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
         3913  +    pPager->iChangeCount = change_counter;
         3914  +  
         3915  +    /* Release the page reference. */
         3916  +    sqlite3PagerUnref(pPgHdr);
         3917  +    pPager->changeCountDone = 1;
         3918  +  }
  3917   3919     return SQLITE_OK;
  3918   3920   }
  3919   3921   
  3920   3922   /*
  3921   3923   ** Sync the database file for the pager pPager. zMaster points to the name
  3922   3924   ** of a master journal file that should be written into the individual
  3923   3925   ** journal file. zMaster may be NULL, which is interpreted as no master

Changes to test/exclusive.test.

     4      4   # a legal notice, here is a blessing:
     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11         -# This file implements regression tests for SQLite library.
           11  +# This file implements regression tests for SQLite library. The focus
           12  +# of these tests is exclusive access mode (i.e. the thing activated by 
           13  +# "PRAGMA locking_mode = EXCLUSIVE").
    12     14   #
    13         -# $Id: exclusive.test,v 1.2 2007/03/26 08:05:12 danielk1977 Exp $
           15  +# $Id: exclusive.test,v 1.3 2007/03/26 10:27:19 danielk1977 Exp $
    14     16   
    15     17   set testdir [file dirname $argv0]
    16     18   source $testdir/tester.tcl
    17     19   
    18     20   ifcapable {!pager_pragmas} {
    19     21     finish_test
    20     22     return

Added test/exclusive2.test.

            1  +# 2007 March 24
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.
           12  +#
           13  +# $Id: exclusive2.test,v 1.1 2007/03/26 10:27:19 danielk1977 Exp $
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +
           18  +ifcapable {!pager_pragmas} {
           19  +  finish_test
           20  +  return
           21  +}
           22  +
           23  +proc pagerChangeCounter {filename {new ""}} {
           24  +  set fd [open $filename a+]
           25  +  fconfigure $fd -translation binary -encoding binary
           26  +  if {$new ne ""} {
           27  +    seek $fd 24
           28  +    set a [expr {($new&0xFF000000)>>24}]
           29  +    set b [expr {($new&0x00FF0000)>>16}]
           30  +    set c [expr {($new&0x0000FF00)>>8}]
           31  +    set d [expr {($new&0x000000FF)}]
           32  +    puts $fd [binary format cccc $a $b $c $d]
           33  +  }
           34  +
           35  +  seek $fd 24
           36  +  foreach {a b c d} [list 0 0 0 0] {}
           37  +  binary scan [read $fd 4] cccc a b c d
           38  +  set  ret [expr ($a&0x000000FF)<<24]
           39  +  incr ret [expr ($b&0x000000FF)<<16]
           40  +  incr ret [expr ($c&0x000000FF)<<8]
           41  +  incr ret [expr ($d&0x000000FF)<<0]
           42  +
           43  +  close $fd
           44  +  return $ret
           45  +}
           46  +
           47  +proc t1sig {{db db}} {
           48  +  execsql {SELECT count(*), md5sum(a) FROM t1} $db
           49  +}
           50  +do_test exclusive2-1.0 {
           51  +  pagerChangeCounter test.db
           52  +} {0}
           53  +
           54  +#-----------------------------------------------------------------------
           55  +# The following tests - exclusive2-1.X - check that:
           56  +#
           57  +# 1-3:   Build a database with connection 1, calculate a signature.
           58  +# 4-9:   Modify the database using a second connection, then reset
           59  +#        the pager change-counter to the value it had before the modifications.
           60  +# 8:     Check that using the first connection, the database signature
           61  +#        is still the same. This is because it uses the in-memory cache.
           62  +#        It can't tell the db has changed because we reset the change-counter.
           63  +# 9:     Increment the change-counter.
           64  +# 10:    Ensure that the first connection now sees the updated database. It
           65  +#        sees the change-counter has been incremented and discards the 
           66  +#        invalid in-memory cache.
           67  +#
           68  +do_test exclusive2-1.1 {
           69  +  execsql {
           70  +    BEGIN;
           71  +    CREATE TABLE t1(a UNIQUE);
           72  +    INSERT INTO t1 VALUES(randstr(10, 400));
           73  +    INSERT INTO t1 VALUES(randstr(10, 400));
           74  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
           75  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
           76  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
           77  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
           78  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
           79  +    COMMIT;
           80  +    SELECT count(*) FROM t1;
           81  +  }
           82  +} {64}
           83  +do_test exclusive2-1.2 {
           84  +  set ::sig [t1sig]
           85  +  pagerChangeCounter test.db
           86  +} {1}
           87  +do_test exclusive2-1.3 {
           88  +  t1sig
           89  +} $::sig
           90  +do_test exclusive2-1.4 {
           91  +  sqlite3 db2 test.db
           92  +  t1sig db2
           93  +} $::sig
           94  +do_test exclusive2-1.5 {
           95  +  execsql {
           96  +    DELETE FROM t1;
           97  +  } db2
           98  +  expr {[t1sig db2] eq $::sig}
           99  +} 0
          100  +do_test exclusive2-1.6 {
          101  +  pagerChangeCounter test.db
          102  +} {2}
          103  +do_test exclusive2-1.7 {
          104  +  pagerChangeCounter test.db 1
          105  +} {1}
          106  +do_test exclusive2-1.9 {
          107  +  t1sig
          108  +  expr {[t1sig] eq $::sig}
          109  +} {1}
          110  +do_test exclusive2-1.10 {
          111  +  pagerChangeCounter test.db 2
          112  +} {2}
          113  +do_test exclusive2-1.11 {
          114  +  expr {[t1sig] eq $::sig}
          115  +} {0}
          116  +
          117  +#--------------------------------------------------------------------
          118  +# These tests - exclusive2-2.X - are similar to exclusive2-1.X, 
          119  +# except that they are run with locking_mode=EXCLUSIVE.
          120  +#
          121  +# 1-3:   Build a database with exclusive-access connection 1, 
          122  +#        calculate a signature.
          123  +# 4:     Corrupt the database by writing 10000 bytes of garbage
          124  +#        starting at the beginning of page 2. Check that connection 1
          125  +#        still works. It should be accessing the in-memory cache.
          126  +# 5-6:   Modify the dataase change-counter. Connection 1 still works
          127  +#        entirely from in-memory cache, because it doesn't check the
          128  +#        change-counter.
          129  +# 7-8    Set the locking-mode back to normal. After the db is unlocked,
          130  +#        SQLite detects the modified change-counter and discards the
          131  +#        in-memory cache. Then it finds the corruption caused in step 4....
          132  +#
          133  +do_test exclusive2-2.1 {
          134  +  execsql {PRAGMA locking_mode = exclusive;}
          135  +  execsql {
          136  +    BEGIN;
          137  +    INSERT INTO t1 VALUES(randstr(10, 400));
          138  +    INSERT INTO t1 VALUES(randstr(10, 400));
          139  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
          140  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
          141  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
          142  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
          143  +    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
          144  +    COMMIT;
          145  +    SELECT count(*) FROM t1;
          146  +  }
          147  +} {64}
          148  +do_test exclusive2-2.2 {
          149  +  set ::sig [t1sig]
          150  +  pagerChangeCounter test.db
          151  +} {3}
          152  +do_test exclusive2-2.3 {
          153  +  t1sig
          154  +} $::sig
          155  +
          156  +do_test exclusive2-2.4 {
          157  +  set fd [open test.db a]
          158  +  seek $fd 1024
          159  +  puts -nonewline $fd [string repeat [binary format c 0] 10000]
          160  +  t1sig
          161  +} $::sig
          162  +
          163  +do_test exclusive2-2.5 {
          164  +  pagerChangeCounter test.db 5
          165  +} {5}
          166  +do_test exclusive2-2.6 {
          167  +  t1sig
          168  +} $::sig
          169  +do_test exclusive2-2.7 {
          170  +  execsql {PRAGMA locking_mode = normal}
          171  +  t1sig
          172  +} $::sig
          173  +
          174  +do_test exclusive2-2.8 {
          175  +  set rc [catch {t1sig} msg]
          176  +  list $rc $msg
          177  +} {1 {database disk image is malformed}}
          178  +
          179  +#--------------------------------------------------------------------
          180  +# These tests - exclusive2-3.X - verify that the pager change-counter
          181  +# is only incremented by the first change when in exclusive access
          182  +# mode. In normal mode, the change-counter is incremented once
          183  +# per write-transaction.
          184  +#
          185  +
          186  +db close
          187  +db2 close
          188  +file delete -force test.db
          189  +file delete -force test.db-journal
          190  +
          191  +do_test exclusive2-3.0 {
          192  +  sqlite3 db test.db
          193  +  execsql {
          194  +    BEGIN;
          195  +    CREATE TABLE t1(a UNIQUE);
          196  +    INSERT INTO t1 VALUES(randstr(10, 400));
          197  +    INSERT INTO t1 VALUES(randstr(10, 400));
          198  +    COMMIT;
          199  +  }
          200  +  pagerChangeCounter test.db
          201  +} {1}
          202  +do_test exclusive2-3.1 {
          203  +  execsql {
          204  +    INSERT INTO t1 VALUES(randstr(10, 400));
          205  +  }
          206  +  pagerChangeCounter test.db
          207  +} {2}
          208  +do_test exclusive2-3.2 {
          209  +  execsql {
          210  +    INSERT INTO t1 VALUES(randstr(10, 400));
          211  +  }
          212  +  pagerChangeCounter test.db
          213  +} {3}
          214  +do_test exclusive2-3.3 {
          215  +  execsql {
          216  +    PRAGMA locking_mode = exclusive;
          217  +    INSERT INTO t1 VALUES(randstr(10, 400));
          218  +  }
          219  +  pagerChangeCounter test.db
          220  +} {4}
          221  +do_test exclusive2-3.4 {
          222  +  execsql {
          223  +    INSERT INTO t1 VALUES(randstr(10, 400));
          224  +  }
          225  +  pagerChangeCounter test.db
          226  +} {4}
          227  +do_test exclusive2-3.5 {
          228  +  execsql {
          229  +    PRAGMA locking_mode = normal;
          230  +    INSERT INTO t1 VALUES(randstr(10, 400));
          231  +  }
          232  +  pagerChangeCounter test.db
          233  +} {4}
          234  +do_test exclusive2-3.6 {
          235  +  execsql {
          236  +    INSERT INTO t1 VALUES(randstr(10, 400));
          237  +  }
          238  +  pagerChangeCounter test.db
          239  +} {5}
          240  +
          241  +finish_test
          242  +