SQLite

Check-in [6c5c04eea1]
Login

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

Overview
Comment:Fix a memory leak that can occur in os_unix.c if an IO error occurs within the xUnlock method.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 6c5c04eea1f0e8d61883ee8675c249fbf895dc01
User & Date: dan 2010-06-16 10:55:43.000
Context
2010-06-16
12:30
Add extra test cases to pager1.test. (check-in: ad3209572d user: dan tags: trunk)
10:55
Fix a memory leak that can occur in os_unix.c if an IO error occurs within the xUnlock method. (check-in: 6c5c04eea1 user: dan tags: trunk)
2010-06-15
19:07
Rationalize a common pattern in tcl test cases into proc do_multiclient_test. (check-in: efe4456498 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/os_unix.c.
730
731
732
733
734
735
736






























737
738
739
740
741
742
743
744

745
746
747
748
749

750
751
752
753
754
755
756
#endif
};

/*
** A lists of all unixInodeInfo objects.
*/
static unixInodeInfo *inodeList = 0;































/*
** Release a unixInodeInfo structure previously allocated by findInodeInfo().
**
** The mutex entered using the unixEnterMutex() function must be held
** when this function is called.
*/
static void releaseInodeInfo(unixInodeInfo *pInode){

  assert( unixMutexHeld() );
  if( pInode ){
    pInode->nRef--;
    if( pInode->nRef==0 ){
      assert( pInode->pShmNode==0 );

      if( pInode->pPrev ){
        assert( pInode->pPrev->pNext==pInode );
        pInode->pPrev->pNext = pInode->pNext;
      }else{
        assert( inodeList==pInode );
        inodeList = pInode->pNext;
      }







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







|
>





>







730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
#endif
};

/*
** A lists of all unixInodeInfo objects.
*/
static unixInodeInfo *inodeList = 0;

/*
** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
** If all such file descriptors are closed without error, the list is
** cleared and SQLITE_OK returned.
**
** Otherwise, if an error occurs, then successfully closed file descriptor
** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. 
** not deleted and SQLITE_IOERR_CLOSE returned.
*/ 
static int closePendingFds(unixFile *pFile){
  int rc = SQLITE_OK;
  unixInodeInfo *pInode = pFile->pInode;
  UnixUnusedFd *pError = 0;
  UnixUnusedFd *p;
  UnixUnusedFd *pNext;
  for(p=pInode->pUnused; p; p=pNext){
    pNext = p->pNext;
    if( close(p->fd) ){
      pFile->lastErrno = errno;
      rc = SQLITE_IOERR_CLOSE;
      p->pNext = pError;
      pError = p;
    }else{
      sqlite3_free(p);
    }
  }
  pInode->pUnused = pError;
  return rc;
}

/*
** Release a unixInodeInfo structure previously allocated by findInodeInfo().
**
** The mutex entered using the unixEnterMutex() function must be held
** when this function is called.
*/
static void releaseInodeInfo(unixFile *pFile){
  unixInodeInfo *pInode = pFile->pInode;
  assert( unixMutexHeld() );
  if( pInode ){
    pInode->nRef--;
    if( pInode->nRef==0 ){
      assert( pInode->pShmNode==0 );
      closePendingFds(pFile);
      if( pInode->pPrev ){
        assert( pInode->pPrev->pNext==pInode );
        pInode->pPrev->pNext = pInode->pNext;
      }else{
        assert( inodeList==pInode );
        inodeList = pInode->pNext;
      }
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
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
end_lock:
  unixLeaveMutex();
  OSTRACE(("LOCK    %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), 
      rc==SQLITE_OK ? "ok" : "failed"));
  return rc;
}

/*
** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
** If all such file descriptors are closed without error, the list is
** cleared and SQLITE_OK returned.
**
** Otherwise, if an error occurs, then successfully closed file descriptor
** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. 
** not deleted and SQLITE_IOERR_CLOSE returned.
*/ 
static int closePendingFds(unixFile *pFile){
  int rc = SQLITE_OK;
  unixInodeInfo *pInode = pFile->pInode;
  UnixUnusedFd *pError = 0;
  UnixUnusedFd *p;
  UnixUnusedFd *pNext;
  for(p=pInode->pUnused; p; p=pNext){
    pNext = p->pNext;
    if( close(p->fd) ){
      pFile->lastErrno = errno;
      rc = SQLITE_IOERR_CLOSE;
      p->pNext = pError;
      pError = p;
    }else{
      sqlite3_free(p);
    }
  }
  pInode->pUnused = pError;
  return rc;
}

/*
** Add the file descriptor used by file handle pFile to the corresponding
** pUnused list.
*/
static void setPendingFd(unixFile *pFile){
  unixInodeInfo *pInode = pFile->pInode;
  UnixUnusedFd *p = pFile->pUnused;







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1180
1181
1182
1183
1184
1185
1186






























1187
1188
1189
1190
1191
1192
1193
end_lock:
  unixLeaveMutex();
  OSTRACE(("LOCK    %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), 
      rc==SQLITE_OK ? "ok" : "failed"));
  return rc;
}































/*
** Add the file descriptor used by file handle pFile to the corresponding
** pUnused list.
*/
static void setPendingFd(unixFile *pFile){
  unixInodeInfo *pInode = pFile->pInode;
  UnixUnusedFd *p = pFile->pUnused;
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
      /* If there are outstanding locks, do not actually close the file just
      ** yet because that would clear those locks.  Instead, add the file
      ** descriptor to pInode->pUnused list.  It will be automatically closed 
      ** when the last lock is cleared.
      */
      setPendingFd(pFile);
    }
    releaseInodeInfo(pFile->pInode);
    rc = closeUnixFile(id);
    unixLeaveMutex();
  }
  return rc;
}

/************** End of the posix advisory lock implementation *****************







|







1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
      /* If there are outstanding locks, do not actually close the file just
      ** yet because that would clear those locks.  Instead, add the file
      ** descriptor to pInode->pUnused list.  It will be automatically closed 
      ** when the last lock is cleared.
      */
      setPendingFd(pFile);
    }
    releaseInodeInfo(pFile);
    rc = closeUnixFile(id);
    unixLeaveMutex();
  }
  return rc;
}

/************** End of the posix advisory lock implementation *****************
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
 */
static int semClose(sqlite3_file *id) {
  if( id ){
    unixFile *pFile = (unixFile*)id;
    semUnlock(id, NO_LOCK);
    assert( pFile );
    unixEnterMutex();
    releaseInodeInfo(pFile->pInode);
    unixLeaveMutex();
    closeUnixFile(id);
  }
  return SQLITE_OK;
}

#endif /* OS_VXWORKS */







|







2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
 */
static int semClose(sqlite3_file *id) {
  if( id ){
    unixFile *pFile = (unixFile*)id;
    semUnlock(id, NO_LOCK);
    assert( pFile );
    unixEnterMutex();
    releaseInodeInfo(pFile);
    unixLeaveMutex();
    closeUnixFile(id);
  }
  return SQLITE_OK;
}

#endif /* OS_VXWORKS */
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
      /* If there are outstanding locks, do not actually close the file just
      ** yet because that would clear those locks.  Instead, add the file
      ** descriptor to pInode->aPending.  It will be automatically closed when
      ** the last lock is cleared.
      */
      setPendingFd(pFile);
    }
    releaseInodeInfo(pFile->pInode);
    sqlite3_free(pFile->lockingContext);
    rc = closeUnixFile(id);
    unixLeaveMutex();
  }
  return rc;
}








|







2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
      /* If there are outstanding locks, do not actually close the file just
      ** yet because that would clear those locks.  Instead, add the file
      ** descriptor to pInode->aPending.  It will be automatically closed when
      ** the last lock is cleared.
      */
      setPendingFd(pFile);
    }
    releaseInodeInfo(pFile);
    sqlite3_free(pFile->lockingContext);
    rc = closeUnixFile(id);
    unixLeaveMutex();
  }
  return rc;
}

Changes to src/test_vfs.c.
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
#define TESTVFS_SHMLOCK_MASK    0x00000010
#define TESTVFS_SHMMAP_MASK     0x00000020
#define TESTVFS_SHMBARRIER_MASK 0x00000040
#define TESTVFS_SHMCLOSE_MASK   0x00000080

#define TESTVFS_OPEN_MASK       0x00000100
#define TESTVFS_SYNC_MASK       0x00000200

#define TESTVFS_ALL_MASK        0x000003FF


#define TESTVFS_MAX_PAGES 256

/*
** A shared-memory buffer. There is one of these objects for each shared
** memory region opened by clients. If two clients open the same file,







>
|







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#define TESTVFS_SHMLOCK_MASK    0x00000010
#define TESTVFS_SHMMAP_MASK     0x00000020
#define TESTVFS_SHMBARRIER_MASK 0x00000040
#define TESTVFS_SHMCLOSE_MASK   0x00000080

#define TESTVFS_OPEN_MASK       0x00000100
#define TESTVFS_SYNC_MASK       0x00000200
#define TESTVFS_DELETE_MASK     0x00000400
#define TESTVFS_ALL_MASK        0x000007FF


#define TESTVFS_MAX_PAGES 256

/*
** A shared-memory buffer. There is one of these objects for each shared
** memory region opened by clients. If two clients open the same file,
453
454
455
456
457
458
459










460


461
462
463
464
465
466
467

/*
** Delete the file located at zPath. If the dirSync argument is true,
** ensure the file-system modifications are synced to disk before
** returning.
*/
static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){










  return sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);


}

/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int tvfsAccess(







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







454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

/*
** Delete the file located at zPath. If the dirSync argument is true,
** ensure the file-system modifications are synced to disk before
** returning.
*/
static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
  int rc = SQLITE_OK;
  Testvfs *p = (Testvfs *)pVfs->pAppData;

  if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){
    tvfsExecTcl(p, "xDelete", 
        Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0
    );
    tvfsResultCode(p, &rc);
  }
  if( rc==SQLITE_OK ){
    rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
  }
  return rc;
}

/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int tvfsAccess(
839
840
841
842
843
844
845

846
847
848
849
850
851
852
      } vfsmethod [] = {
        { "xShmOpen",    TESTVFS_SHMOPEN_MASK },
        { "xShmLock",    TESTVFS_SHMLOCK_MASK },
        { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
        { "xShmClose",   TESTVFS_SHMCLOSE_MASK },
        { "xShmMap",     TESTVFS_SHMMAP_MASK },
        { "xSync",       TESTVFS_SYNC_MASK },

        { "xOpen",       TESTVFS_OPEN_MASK },
      };
      Tcl_Obj **apElem = 0;
      int nElem = 0;
      int i;
      int mask = 0;
      if( objc!=3 ){







>







852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
      } vfsmethod [] = {
        { "xShmOpen",    TESTVFS_SHMOPEN_MASK },
        { "xShmLock",    TESTVFS_SHMLOCK_MASK },
        { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
        { "xShmClose",   TESTVFS_SHMCLOSE_MASK },
        { "xShmMap",     TESTVFS_SHMMAP_MASK },
        { "xSync",       TESTVFS_SYNC_MASK },
        { "xDelete",     TESTVFS_DELETE_MASK },
        { "xOpen",       TESTVFS_OPEN_MASK },
      };
      Tcl_Obj **apElem = 0;
      int nElem = 0;
      int i;
      int mask = 0;
      if( objc!=3 ){
Changes to test/malloc_common.tcl.
105
106
107
108
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
    eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec
  }
}

#-------------------------------------------------------------------------
# Procedures to save and restore the current file-system state:
#

#   faultsim_save_and_close
#   faultsim_restore_and_reopen

#
proc faultsim_save_and_close {} {
  foreach {a => b} {
      test.db          =>  testX.db 
      test.db-wal      =>  testX.db-wal 
      test.db-journal  =>  testX.db-journal
  } {
    if {[file exists $a]} {
      file copy -force $a $b
    } else {
      file delete -force $b
    }
  }


  catch { db close }
  return ""
}
proc faultsim_restore_and_reopen {} {
  catch { db close }
  foreach {a => b} {
      testX.db          =>  test.db 
      testX.db-wal      =>  test.db-wal 
      testX.db-journal  =>  test.db-journal
  } {
    if {[file exists $a]} {
      file copy -force $a $b
    } else {
      file delete -force $b
    }
  }
  sqlite3 db test.db
  sqlite3_extended_result_codes db 1
  sqlite3_db_config_lookaside db 0 0 0
}

proc faultsim_integrity_check {{db db}} {
  set ic [$db eval { PRAGMA integrity_check }]
  if {$ic != "ok"} { error "Integrity check: $ic" }
}

proc faultsim_delete_and_reopen {{file test.db}} {
  catch { db close }
  file delete -force test.db test.db-wal test.db-journal
  sqlite3 db test.db
}


# The following procs are used as [do_one_faultsim_test] callbacks when 
# injecting OOM faults into test cases.
#







>


>

|
|
|
<
<
|
<
|
<
<
|
|
>
>





|
|
|
<
<
<
|
<
<
<













|







105
106
107
108
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec
  }
}

#-------------------------------------------------------------------------
# Procedures to save and restore the current file-system state:
#
#   faultsim_save
#   faultsim_save_and_close
#   faultsim_restore_and_reopen
#   faultsim_delete_and_reopen
#
proc faultsim_save {} {
  foreach f [glob -nocomplain sv_test.db*] { file delete -force $f }
  foreach f [glob -nocomplain test.db*] {


    set f2 "sv_$f"

    file copy -force $f $f2


  }
}
proc faultsim_save_and_close {} {
  faultsim_save
  catch { db close }
  return ""
}
proc faultsim_restore_and_reopen {} {
  catch { db close }
  foreach f [glob -nocomplain test.db*] { file delete -force $f }
  foreach f2 [glob -nocomplain sv_test.db*] {
    set f [string range $f2 3 end]



    file copy -force $f2 $f



  }
  sqlite3 db test.db
  sqlite3_extended_result_codes db 1
  sqlite3_db_config_lookaside db 0 0 0
}

proc faultsim_integrity_check {{db db}} {
  set ic [$db eval { PRAGMA integrity_check }]
  if {$ic != "ok"} { error "Integrity check: $ic" }
}

proc faultsim_delete_and_reopen {{file test.db}} {
  catch { db close }
  foreach f [glob -nocomplain test.db*] { file delete -force $f }
  sqlite3 db test.db
}


# The following procs are used as [do_one_faultsim_test] callbacks when 
# injecting OOM faults into test cases.
#
Changes to test/pager1.test.
10
11
12
13
14
15
16















17
18
19
20
21
22
23
#***********************************************************************
#

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
















do_multiclient_test tn {

  # Create and populate a database table using connection [db]. Check 
  # that connections [db2] and [db3] can see the schema and content.
  #
  do_test pager1-$tn.1 {







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







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
#***********************************************************************
#

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

#
# pager1-1.*: Test inter-process locking (clients in multiple processes).
#
# pager1-2.*: Test intra-process locking (multiple clients in this process).
#
# pager1-3.*: Savepoint related tests.
#

proc do_execsql_test {testname sql result} {
  uplevel do_test $testname [list "execsql {$sql}"] [list $result]
}
proc do_catchsql_test {testname sql result} {
  uplevel do_test $testname [list "catchsql {$sql}"] [list $result]
}

do_multiclient_test tn {

  # Create and populate a database table using connection [db]. Check 
  # that connections [db2] and [db3] can see the schema and content.
  #
  do_test pager1-$tn.1 {
147
148
149
150
151
152
153

































154
155
  do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
  do_test pager1-$tn.26 { sql1 { COMMIT } } {}
  do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
  do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}
}


































finish_test








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


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
  do_test pager1-$tn.26 { sql1 { COMMIT } } {}
  do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
  do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}
}

do_test pager1-3.1 {
  faultsim_delete_and_reopen
  execsql {
    CREATE TABLE t1(a PRIMARY KEY, b);
    CREATE TABLE counter(
      i CHECK (i<5), 
      u CHECK (u<10)
    );
    INSERT INTO counter VALUES(0, 0);
    CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
      UPDATE counter SET i = i+1;
    END;
    CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN
      UPDATE counter SET u = u+1;
    END;
  }
  execsql { SELECT * FROM counter }
} {0 0}

do_execsql_test pager1-3.2 {
  BEGIN;
    INSERT INTO t1 VALUES(1, randomblob(1500));
    INSERT INTO t1 VALUES(2, randomblob(1500));
    INSERT INTO t1 VALUES(3, randomblob(1500));
    SELECT * FROM counter;
} {3 0}
do_catchsql_test pager1-3.3 {
    INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
} {1 {constraint failed}}
do_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
do_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
do_execsql_test pager1-3.6 { COMMIT } {}

finish_test

Added test/pagerfault.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
115
116
117
118
119
# 2010 June 15
#
# 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.
#
#***********************************************************************
#

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

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;
    PRAGMA cache_size = 10;
    CREATE TABLE t1(a UNIQUE, b UNIQUE);
    INSERT INTO t1 VALUES(a_string(200), a_string(300));
    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
    BEGIN;
      INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
      INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
      INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
      INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
  }
  faultsim_save_and_close
} {}
do_faultsim_test pagerfault-1 -prep {
  faultsim_restore_and_reopen
} -body {
  execsql { SELECT count(*) FROM t1 }
} -test {
  faultsim_test_result {0 4}
  faultsim_integrity_check
  if {[db one { SELECT count(*) FROM t1 }] != 4} {
    error "Database content appears incorrect"
  }
}

#-------------------------------------------------------------------------
# Test fault-injection while rolling back hot-journals that were created
# as part of a multi-file transaction.
#
do_test pagerfault-2-pre1 {
  testvfs tstvfs -default 1
  tstvfs filter xDelete
  tstvfs script xDeleteCallback

  proc xDeleteCallback {method file args} {
    set file [file tail $file]
    if { [string match *mj* $file] } { faultsim_save }
  }

  faultsim_delete_and_reopen
  db func a_string a_string

  execsql {
    ATTACH 'test.db2' AS aux;
    PRAGMA journal_mode = DELETE;
    PRAGMA main.cache_size = 10;
    PRAGMA aux.cache_size = 10;

    CREATE TABLE t1(a UNIQUE, b UNIQUE);
    CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
    INSERT INTO t1 VALUES(a_string(200), a_string(300));
    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
    INSERT INTO t2 SELECT * FROM t1;

    BEGIN;
      INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
      INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
      INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
      INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
      REPLACE INTO t2 SELECT * FROM t1;
    COMMIT;
  }

  db close
  tstvfs delete
} {}
do_faultsim_test pagerfault-2 -faults ioerr-persistent -prep {
  faultsim_restore_and_reopen
} -body {
  execsql { 
    ATTACH 'test.db2' AS aux;
    SELECT count(*) FROM t2;
    SELECT count(*) FROM t1;
  }
} -test {
  faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}}
  faultsim_integrity_check

  catchsql { ATTACH 'test.db2' AS aux }
  if {[db one { SELECT count(*) FROM t1 }] != 4
   || [db one { SELECT count(*) FROM t2 }] != 4
  } {
    error "Database content appears incorrect"
  }
}

finish_test
Changes to test/permutations.test.
165
166
167
168
169
170
171

172
173
174
175
176
177
178
  walfault.test
} 

test_suite "coverage-pager" -description {
  Coverage tests for file pager.c.
} -files {
  pager1.test

} 


lappend ::testsuitelist xxx
#-------------------------------------------------------------------------
# Define the permutation test suites:
#







>







165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  walfault.test
} 

test_suite "coverage-pager" -description {
  Coverage tests for file pager.c.
} -files {
  pager1.test
  pagerfault.test
} 


lappend ::testsuitelist xxx
#-------------------------------------------------------------------------
# Define the permutation test suites:
#