/ Check-in [228c5b16]
Login

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

Overview
Comment:Add coverage tests for pager.c.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:228c5b16af80c22e97d9d4deb351e0d3f4523f89
User & Date: dan 2010-08-12 11:25:48
Context
2010-08-12
16:36
If an error occurs in PagerSetPagesize(), set the output variable to the unmodified page-size before returning. check-in: 02def8f9 user: dan tags: trunk
11:25
Add coverage tests for pager.c. check-in: 228c5b16 user: dan tags: trunk
02:41
Increase the maximum page size from 32k to 64k. check-in: 45362437 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
....
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
....
6421
6422
6423
6424
6425
6426
6427

6428
6429
6430
6431
6432
6433
6434

  /* Mark the page as clean. */
  if( rc==SQLITE_OK ){
    PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno));
    sqlite3PcacheMakeClean(pPg);
  }

  return pager_error(pPager, rc);
}


/*
** Allocate and initialize a new Pager object and put a pointer to it
** in *ppPager. The pager should eventually be freed by passing it
** to sqlite3PagerClose().
................................................................................
      ** 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.
      **
      ** When journal_mode==OFF the dbOrigSize is always zero, so this
      ** block never runs if journal_mode=OFF.
      */
  #ifndef SQLITE_OMIT_AUTOVACUUM
      if( pPager->dbSize<pPager->dbOrigSize 
       && pPager->journalMode!=PAGER_JOURNALMODE_OFF
      ){
        Pgno i;                                   /* Iterator variable */
        const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
................................................................................
      */
      sqlite3OsClose(pPager->jfd);
      if( pPager->eLock>=RESERVED_LOCK ){
        sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }else{
        int rc = SQLITE_OK;
        int state = pPager->eState;

        if( state==PAGER_OPEN ){
          rc = sqlite3PagerSharedLock(pPager);
        }
        if( pPager->eState==PAGER_READER ){
          assert( rc==SQLITE_OK );
          rc = pagerLockDb(pPager, RESERVED_LOCK);
        }







|







 







<
<
<







 







>







4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
....
5697
5698
5699
5700
5701
5702
5703



5704
5705
5706
5707
5708
5709
5710
....
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432

  /* Mark the page as clean. */
  if( rc==SQLITE_OK ){
    PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno));
    sqlite3PcacheMakeClean(pPg);
  }

  return pager_error(pPager, rc); 
}


/*
** Allocate and initialize a new Pager object and put a pointer to it
** in *ppPager. The pager should eventually be freed by passing it
** to sqlite3PagerClose().
................................................................................
      ** 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.



      */
  #ifndef SQLITE_OMIT_AUTOVACUUM
      if( pPager->dbSize<pPager->dbOrigSize 
       && pPager->journalMode!=PAGER_JOURNALMODE_OFF
      ){
        Pgno i;                                   /* Iterator variable */
        const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
................................................................................
      */
      sqlite3OsClose(pPager->jfd);
      if( pPager->eLock>=RESERVED_LOCK ){
        sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }else{
        int rc = SQLITE_OK;
        int state = pPager->eState;
        assert( state==PAGER_OPEN || state==PAGER_READER );
        if( state==PAGER_OPEN ){
          rc = sqlite3PagerSharedLock(pPager);
        }
        if( pPager->eState==PAGER_READER ){
          assert( rc==SQLITE_OK );
          rc = pagerLockDb(pPager, RESERVED_LOCK);
        }

Changes to src/vdbeaux.c.

2057
2058
2059
2060
2061
2062
2063
2064
2065









2066
2067
2068
2069
2070
2071
2072

    /* Check for one of the special errors */
    mrc = p->rc & 0xff;
    assert( p->rc!=SQLITE_IOERR_BLOCKED );  /* This error no longer exists */
    isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
    if( isSpecialError ){
      /* If the query was read-only, we need do no rollback at all. Otherwise,
      ** proceed with the special handling.









      */
      if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
        if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
          eStatementOp = SAVEPOINT_ROLLBACK;
        }else{
          /* We are forced to roll back the active transaction. Before doing
          ** so, abort any other statements this handle currently has active.







|
|
>
>
>
>
>
>
>
>
>







2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081

    /* Check for one of the special errors */
    mrc = p->rc & 0xff;
    assert( p->rc!=SQLITE_IOERR_BLOCKED );  /* This error no longer exists */
    isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
    if( isSpecialError ){
      /* If the query was read-only and the error code is SQLITE_INTERRUPT, 
      ** no rollback is necessary. Otherwise, at least a savepoint 
      ** transaction must be rolled back to restore the database to a 
      ** consistent state.
      **
      ** Even if the statement is read-only, it is important to perform
      ** a statement or transaction rollback operation. If the error 
      ** occured while writing to the journal, sub-journal or database
      ** file as part of an effort to free up cache space (see function
      ** pagerStress() in pager.c), the rollback is required to restore 
      ** the pager to a consistent state.
      */
      if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
        if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
          eStatementOp = SAVEPOINT_ROLLBACK;
        }else{
          /* We are forced to roll back the active transaction. Before doing
          ** so, abort any other statements this handle currently has active.

Changes to test/lock_common.tcl.

23
24
25
26
27
28
29


30
31
32
33
34
35
36
    set tn 1
  } {
    proc code2 {tcl} { uplevel #0 $tcl }
    proc code3 {tcl} { uplevel #0 $tcl }
    set tn 2
  }] {
    faultsim_delete_and_reopen


  
    # Open connections [db2] and [db3]. Depending on which iteration this
    # is, the connections may be created in this interpreter, or in 
    # interpreters running in other OS processes. As such, the [db2] and [db3]
    # commands should only be accessed within [code2] and [code3] blocks,
    # respectively.
    #







>
>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    set tn 1
  } {
    proc code2 {tcl} { uplevel #0 $tcl }
    proc code3 {tcl} { uplevel #0 $tcl }
    set tn 2
  }] {
    faultsim_delete_and_reopen

    proc code1 {tcl} { uplevel #0 $tcl }
  
    # Open connections [db2] and [db3]. Depending on which iteration this
    # is, the connections may be created in this interpreter, or in 
    # interpreters running in other OS processes. As such, the [db2] and [db3]
    # commands should only be accessed within [code2] and [code3] blocks,
    # respectively.
    #

Changes to test/pager1.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
....
2269
2270
2271
2272
2273
2274
2275
























































2276
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
 
# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec

#
# pager1-1.*: Test inter-process locking (clients in multiple processes).
................................................................................
    csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); }
  } {1 {database is locked}}
  code2 { db2 close ; sqlite3 db2 test.db }
  do_test pager1-28.$tn.4 { 
    sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT }
  } {}
}
























































finish_test







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
....
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl

# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec

#
# pager1-1.*: Test inter-process locking (clients in multiple processes).
................................................................................
    csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); }
  } {1 {database is locked}}
  code2 { db2 close ; sqlite3 db2 test.db }
  do_test pager1-28.$tn.4 { 
    sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT }
  } {}
}

#-------------------------------------------------------------------------
# Normally, when changing from journal_mode=PERSIST to DELETE the pager
# attempts to delete the journal file. However, if it cannot obtain a
# RESERVED lock on the database file, this step is skipped.
#
do_multiclient_test tn {
  do_test pager1-28.$tn.1 {
    sql1 { 
      PRAGMA journal_mode = PERSIST;
      CREATE TABLE t1(a, b);
      INSERT INTO t1 VALUES('a', 'b');
    }
  } {persist}
  do_test pager1-28.$tn.2 { file exists test.db-journal } 1
  do_test pager1-28.$tn.3 { sql1 { PRAGMA journal_mode = DELETE } } delete
  do_test pager1-28.$tn.4 { file exists test.db-journal } 0

  do_test pager1-28.$tn.5 {
    sql1 { 
      PRAGMA journal_mode = PERSIST;
      INSERT INTO t1 VALUES('c', 'd');
    }
  } {persist}
  do_test pager1-28.$tn.6 { file exists test.db-journal } 1
  do_test pager1-28.$tn.7 {
    sql2 { BEGIN; INSERT INTO t1 VALUES('e', 'f'); }
  } {}
  do_test pager1-28.$tn.8  { file exists test.db-journal } 1
  do_test pager1-28.$tn.9  { sql1 { PRAGMA journal_mode = DELETE } } delete
  do_test pager1-28.$tn.10 { file exists test.db-journal } 1

  do_test pager1-28.$tn.11 { sql2 COMMIT } {}
  do_test pager1-28.$tn.12 { file exists test.db-journal } 0

  do_test pager1-28-$tn.13 {
    code1 { set channel [db incrblob -readonly t1 a 2] }
    sql1 {
      PRAGMA journal_mode = PERSIST;
      INSERT INTO t1 VALUES('g', 'h');
    }
  } {persist}
  do_test pager1-28.$tn.14 { file exists test.db-journal } 1
  do_test pager1-28.$tn.15 {
    sql2 { BEGIN; INSERT INTO t1 VALUES('e', 'f'); }
  } {}
  do_test pager1-28.$tn.16 { sql1 { PRAGMA journal_mode = DELETE } } delete
  do_test pager1-28.$tn.17 { file exists test.db-journal } 1

  do_test pager1-28.$tn.17 { csql2 { COMMIT } } {1 {database is locked}}
  do_test pager1-28-$tn.18 { code1 { read $channel } } c
  do_test pager1-28-$tn.19 { code1 { close $channel } } {}
  do_test pager1-28.$tn.20 { sql2 { COMMIT } } {}
}


finish_test

Changes to test/pager2.test.

114
115
116
117
118
119
120

121


122
123
124
125
126
127
128
129
130
131
132
133
134










135



136
  }
}
db close
tv delete


#-------------------------------------------------------------------------

# Test a ROLLBACK with journal_mode=off.


#
breakpoint
do_test pager2-2.1 {
  faultsim_delete_and_reopen
  execsql {
    CREATE TABLE t1(a, b);
    PRAGMA journal_mode = off;
    BEGIN;
      INSERT INTO t1 VALUES(1, 2);
    ROLLBACK;
    SELECT * FROM t1;
  }
} {off 1 2}














finish_test







>
|
>
>

<











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

114
115
116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  }
}
db close
tv delete


#-------------------------------------------------------------------------
#
# pager2-2.1: Test a ROLLBACK with journal_mode=off.
# pager2-2.2: Test shrinking the database (auto-vacuum) with 
#             journal_mode=off
#

do_test pager2-2.1 {
  faultsim_delete_and_reopen
  execsql {
    CREATE TABLE t1(a, b);
    PRAGMA journal_mode = off;
    BEGIN;
      INSERT INTO t1 VALUES(1, 2);
    ROLLBACK;
    SELECT * FROM t1;
  }
} {off 1 2}
do_test pager2-2.2 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA auto_vacuum = incremental;
    PRAGMA page_size = 1024;
    PRAGMA journal_mode = off;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(zeroblob(5000), zeroblob(5000));
    DELETE FROM t1;
    PRAGMA incremental_vacuum;
  }
  file size test.db
} {3072}

finish_test

Changes to test/pagerfault.test.

23
24
25
26
27
28
29


30
31
32
33
34
35
36
....
1113
1114
1115
1116
1117
1118
1119

1120
1121
1122
1123
1124
1125
1126










































1127
set a_string_counter 1
proc a_string {n} {
  global a_string_counter
  incr a_string_counter
  string range [string repeat "${a_string_counter}." $n] 1 $n
}
db func a_string a_string



#-------------------------------------------------------------------------
# Test fault-injection while rolling back a hot-journal file.
#
do_test pagerfault-1-pre1 {
  execsql {
    PRAGMA journal_mode = DELETE;
................................................................................
do_faultsim_test pagerfault-24 -prep {
  faultsim_delete_and_reopen
  db eval { PRAGMA temp_store = file }
  execsql { CREATE TABLE x(a, b) }
} -body {
  execsql { CREATE TEMP TABLE t1(a, b) }
} -test {

  faultsim_test_result {0 {}} {1 {unable to open a temporary database file for storing temporary tables}}
  set ic [db eval { PRAGMA temp.integrity_check }]
  if {$ic != "ok"} { error "Integrity check: $ic" }
}













































finish_test







>
>







 







>
|




|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
....
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
set a_string_counter 1
proc a_string {n} {
  global a_string_counter
  incr a_string_counter
  string range [string repeat "${a_string_counter}." $n] 1 $n
}
db func a_string a_string

if 1 {

#-------------------------------------------------------------------------
# Test fault-injection while rolling back a hot-journal file.
#
do_test pagerfault-1-pre1 {
  execsql {
    PRAGMA journal_mode = DELETE;
................................................................................
do_faultsim_test pagerfault-24 -prep {
  faultsim_delete_and_reopen
  db eval { PRAGMA temp_store = file }
  execsql { CREATE TABLE x(a, b) }
} -body {
  execsql { CREATE TEMP TABLE t1(a, b) }
} -test {
  faultsim_test_result {0 {}} \
    {1 {unable to open a temporary database file for storing temporary tables}}
  set ic [db eval { PRAGMA temp.integrity_check }]
  if {$ic != "ok"} { error "Integrity check: $ic" }
}

}

proc lockrows {n} {
  if {$n==0} { return "" }
  db eval { SELECT * FROM t1 WHERE oid = $n } { 
    return [lockrows [expr {$n-1}]]
  }
}

do_test pagerfault-25-pre1 {
  faultsim_delete_and_reopen
  db func a_string a_string
  execsql {
    PRAGMA page_size = 1024;
    PRAGMA auto_vacuum = 0;
    CREATE TABLE t1(a);
    INSERT INTO t1 VALUES(a_string(500));
    INSERT INTO t1 SELECT a_string(500) FROM t1;
    INSERT INTO t1 SELECT a_string(500) FROM t1;
    INSERT INTO t1 SELECT a_string(500) FROM t1;
    INSERT INTO t1 SELECT a_string(500) FROM t1;
    INSERT INTO t1 SELECT a_string(500) FROM t1;
  }
  faultsim_save_and_close
} {}
do_faultsim_test pagerfault-25 -faults full -prep {
  faultsim_restore_and_reopen
  db func a_string a_string
  set ::channel [db incrblob -readonly t1 a 1]
  execsql { 
    PRAGMA cache_size = 10;
    BEGIN;
      INSERT INTO t1 VALUES(a_string(3000));
      INSERT INTO t1 VALUES(a_string(3000));
  }
} -body {
  lockrows 30
} -test {
  catch { lockrows 30 }
  close $::channel
  faultsim_test_result {0 {}} 
}


finish_test