/ Check-in [4eb06e84]
Login
Overview
Comment:Fixes for OOM and IO error handling with temp file databases.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tempfiles-lazy-open
Files: files | file ages | folders
SHA1:4eb06e843af60d5e533793618c6e0e9b7ef7a1a6
User & Date: dan 2016-04-11 18:07:47
Context
2016-04-11
18:49
Add extra tests to temptable2.test. check-in: 7dd9d4c1 user: dan tags: tempfiles-lazy-open
18:07
Fixes for OOM and IO error handling with temp file databases. check-in: 4eb06e84 user: dan tags: tempfiles-lazy-open
09:39
Update this branch with the latest changes from the trunk. check-in: 982b753d user: dan tags: tempfiles-lazy-open
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
....
1815
1816
1817
1818
1819
1820
1821
1822



1823
1824

1825
1826
1827
1828
1829
1830
1831
1832
1833
....
5030
5031
5032
5033
5034
5035
5036

5037
5038
5039
5040
5041
5042
5043
5044
5045

    case PAGER_ERROR:
      /* There must be at least one outstanding reference to the pager if
      ** in ERROR state. Otherwise the pager should have already dropped
      ** back to OPEN state.
      */
      assert( pPager->errCode!=SQLITE_OK );
      assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
      break;
  }

  return 1;
}
#endif /* ifndef NDEBUG */

................................................................................
    pPager->eState = PAGER_OPEN;
  }

  /* If Pager.errCode is set, the contents of the pager cache cannot be
  ** trusted. Now that there are no outstanding references to the pager,
  ** it can safely move back to PAGER_OPEN state. This happens in both
  ** normal and exclusive-locking mode.
  */



  if( pPager->errCode ){
    assert( !MEMDB );

    pager_reset(pPager);
    pPager->changeCountDone = pPager->tempFile;
    pPager->eState = PAGER_OPEN;
    pPager->errCode = SQLITE_OK;
    if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
  }

  pPager->journalOff = 0;
  pPager->journalHdr = 0;
................................................................................
  /* This routine is only called from b-tree and only when there are no
  ** outstanding pages. This implies that the pager state should either
  ** be OPEN or READER. READER is only possible if the pager is or was in 
  ** exclusive access mode.
  */
  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
  assert( assert_pager_state(pPager) );

  assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
  if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }

  if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
    int bHotJournal = 1;          /* True if there exists a hot journal-file */

    assert( !MEMDB );

    rc = pager_wait_on_lock(pPager, SHARED_LOCK);







|







 







|
>
>
>
|
<
>

|







 







>

<







954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
....
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826

1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
....
5033
5034
5035
5036
5037
5038
5039
5040
5041

5042
5043
5044
5045
5046
5047
5048

    case PAGER_ERROR:
      /* There must be at least one outstanding reference to the pager if
      ** in ERROR state. Otherwise the pager should have already dropped
      ** back to OPEN state.
      */
      assert( pPager->errCode!=SQLITE_OK );
      assert( sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile );
      break;
  }

  return 1;
}
#endif /* ifndef NDEBUG */

................................................................................
    pPager->eState = PAGER_OPEN;
  }

  /* If Pager.errCode is set, the contents of the pager cache cannot be
  ** trusted. Now that there are no outstanding references to the pager,
  ** it can safely move back to PAGER_OPEN state. This happens in both
  ** normal and exclusive-locking mode.
  **
  ** Exception: There is no way out of the error state for temp files.
  ** This is because it is not possible to call pager_reset() on a temp
  ** file pager (as this may discard the only copy of some data).  */
  assert( pPager->errCode==SQLITE_OK || !MEMDB );

  if( pPager->tempFile==0 && pPager->errCode ){
    pager_reset(pPager);
    pPager->changeCountDone = 0;
    pPager->eState = PAGER_OPEN;
    pPager->errCode = SQLITE_OK;
    if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
  }

  pPager->journalOff = 0;
  pPager->journalHdr = 0;
................................................................................
  /* This routine is only called from b-tree and only when there are no
  ** outstanding pages. This implies that the pager state should either
  ** be OPEN or READER. READER is only possible if the pager is or was in 
  ** exclusive access mode.
  */
  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
  assert( assert_pager_state(pPager) );
  if( pPager->tempFile && pPager->errCode ) { return pPager->errCode; }
  assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );


  if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
    int bHotJournal = 1;          /* True if there exists a hot journal-file */

    assert( !MEMDB );

    rc = pager_wait_on_lock(pPager, SHARED_LOCK);

Changes to test/cffault.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#
# This file contains fault-injection test cases for the 
# sqlite3_db_cacheflush API.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix cacheflush
source $testdir/malloc_common.tcl

# Run the supplied SQL on a copy of the database currently stored on 
# disk in file $dbfile.
proc diskquery {dbfile sql} {
  forcecopy $dbfile dq.db
  sqlite3 dq dq.db







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#
# This file contains fault-injection test cases for the 
# sqlite3_db_cacheflush API.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix cffault
source $testdir/malloc_common.tcl

# Run the supplied SQL on a copy of the database currently stored on 
# disk in file $dbfile.
proc diskquery {dbfile sql} {
  forcecopy $dbfile dq.db
  sqlite3 dq dq.db

Added test/tempfault.test.





































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# 2016 April 11
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests for fault-injection when SQLite is used with
# a temp file database.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix tempfault

sqlite3_memdebug_vfs_oom_test 0

do_faultsim_test 1 -faults oom* -prep {
  sqlite3 db ""
  db eval {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
  }
} -body {
  execsql { INSERT INTO t1 VALUES(5, 6) }
} -test {
  faultsim_test_result {0 {}}
  set rc [catch { execsql { SELECT * FROM t1 } } msg]
  if {$rc==0 && $msg != "1 2 3 4 5 6" && $msg != "1 2 3 4"} {
    error "data mismatch 1: $msg"
  }
  if {$testrc==0 && $msg != "1 2 3 4 5 6"} {
    error "data mismatch 2: $msg"
  }
  faultsim_integrity_check
}

do_faultsim_test 2 -faults oom* -prep {
  sqlite3 db ""
  db eval {
    PRAGMA page_size = 1024;
    PRAGMA cache_size = 10;
    CREATE TABLE t1(a, b);
    CREATE INDEX i1 ON t1(b, a);
    WITH x(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<50)
    INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x;
  }
} -body {
  execsql { UPDATE t1 SET a = randomblob(99) }
} -test {
  faultsim_test_result {0 {}}
  faultsim_integrity_check db
}

do_faultsim_test 3 -faults oom* -prep {
  sqlite3 db ""
  db eval {
    PRAGMA page_size = 1024;
    PRAGMA cache_size = 10;
    CREATE TABLE t1(a, b);
    CREATE INDEX i1 ON t1(b, a);
    WITH x(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<50)
    INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x;
  }
} -body {
  execsql { 
    BEGIN;
      UPDATE t1 SET a = randomblob(99);
      SAVEPOINT abc;
        UPDATE t1 SET a = randomblob(98) WHERE (rowid%10)==0;
      ROLLBACK TO abc;
        UPDATE t1 SET a = randomblob(97) WHERE (rowid%5)==0;
      ROLLBACK TO abc;
    COMMIT;
  }
} -test {
  faultsim_test_result {0 {}}
  faultsim_integrity_check db
}

do_faultsim_test 4 -faults * -prep {
  sqlite3 db ""
  db eval {
    PRAGMA page_size = 1024;
    PRAGMA cache_size = 10;
    CREATE TABLE t1(a, b);
    CREATE INDEX i1 ON t1(b, a);
    WITH x(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<50)
    INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x;
  }
} -body {
  execsql { 
    BEGIN;
      UPDATE t1 SET a = randomblob(99);
      SAVEPOINT abc;
        UPDATE t1 SET a = randomblob(98) WHERE (rowid%10)==0;
      ROLLBACK TO abc;
        UPDATE t1 SET a = randomblob(97) WHERE (rowid%5)==0;
      ROLLBACK TO abc;
    COMMIT;
  }
} -test {
  faultsim_test_result {0 {}}
}

sqlite3_memdebug_vfs_oom_test 1
finish_test