/ Check-in [6217b607]
Login

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

Overview
Comment:Fix a bug to do with deleting the journal file when exiting exclusive-locking mode.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1:6217b607f0cd60383c6cb4ab0fe9da008f611244
User & Date: dan 2010-08-06 06:54:48
Context
2010-08-06
09:43
Modify test_journal.c to work with pre-allocated databases. check-in: 4894a5d2 user: dan tags: experimental
06:54
Fix a bug to do with deleting the journal file when exiting exclusive-locking mode. check-in: 6217b607 user: dan tags: experimental
2010-08-05
18:53
Add comments describing UNKNOWN_LOCK to pager.c. Improve some other comments in the same file. check-in: 54eff6de user: dan tags: experimental
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

143
144
145
146
147
148
149
















150
151
152
153
154
155
156
...
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
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
...
798
799
800
801
802
803
804

805
806
807
808
809
810

811
812
813
814
815
816
817
....
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828

1829
1830
1831
1832
1833
1834
1835
**               |              |                |
**               |              V                |
**               |<-------WRITER_DBMOD---------->|
**               |              |                |
**               |              V                |
**               +<------WRITER_FINISHED-------->+
**
















**  NONE:
**
**    The pager starts up in this state. Nothing is guaranteed in this
**    state - the file may or may not be locked and the database size is
**    unknown. The database may not be read or written.
**
**    * No read or write transaction is active.
................................................................................
**    A connection running with locking_mode=normal enters this state when
**    it opens a read-transaction on the database and returns to state
**    NONE after the read-transaction is completed. However a connection
**    running in locking_mode=exclusive (including temp databases) remains in
**    this state even after the read-transaction is closed. The only way
**    a locking_mode=exclusive connection can transition from READER to NONE
**    is via the ERROR state (see below).




** 
**    * A read transaction may be active (but a write-transaction cannot).
**    * A SHARED or greater lock is held on the database file.
**    * The dbSize variable may be trusted (even if a user-level read 
**      transaction is not active). The dbOrigSize and dbFileSize variables
**      may not be trusted at this point.
**    * If the database is a WAL database, then the WAL connection is open.
**    * Even if a read-transaction is not open, it is guaranteed that 
**      there is no hot-journal in the file-system.
**
**  WRITER_INITIAL:
**



**    * A write transaction is active.

**    * A RESERVED or greater lock is held on the database file.



**    * The dbSize, dbOrigSize and dbFileSize variables are all valid.
**    * The contents of the pager cache have not been modified.
**    * The journal file may or may not be open.
**    * Nothing (not even the first header) has been written to the journal.
**
**  WRITER_CACHEMOD:
**
................................................................................
**    statement executed within a transaction. In this case, if the error
**    code were simply returned to the user, the b-tree layer would not
**    automatically attempt a rollback, as it assumes that an error in a
**    read-only statement cannot leave the pager in an internally inconsistent 
**    state.
**    
**
** State transitions and the [function] that performs each:
** 
**   NONE              -> READER              [PagerSharedLock]
**   READER            -> WRITER_INITIAL      [PagerBegin]
**   WRITER_INITIAL    -> WRITER_CACHEMOD     [pager_open_journal]
**   WRITER_CACHEMOD   -> WRITER_DBMOD        [syncJournal]
**   WRITER_DBMOD      -> WRITER_FINISHED     [PagerCommitPhaseOne]
** 
**   WRITER_***        -> READER              [pager_end_transaction]
**   WRITER_***        -> ERROR               [pager_error]
** 
**   READER            -> NONE                [pager_unlock]
**   ERROR             -> NONE                [pager_unlock]
**
** Notes:
**
**   * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the
**     connection is open in WAL mode. A WAL connection is always in one
**     of the first four states.
**
**   * Normally, a connection open in exclusive mode is never in PAGER_NONE
................................................................................
/*
** (gdb) printf "%s", print_pager_state(pPager)
*/
static char *print_pager_state(Pager *p){
  static char zRet[1024];

  sqlite3_snprintf(1024, zRet,

      "State:         %s errCode=%d\n"
      "Lock:          %s\n"
      "Locking mode:  locking_mode=%s\n"
      "Journal mode:  journal_mode=%s\n"
      "Backing store: tempFile=%d memDb=%d useJournal=%d\n"
      "Journal:       journalOff=%lld journalHdr=%lld\n"

      , p->eState==PAGER_NONE            ? "NONE" :
        p->eState==PAGER_READER          ? "READER" :
        p->eState==PAGER_WRITER_INITIAL  ? "WRITER_INITIAL" :
        p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" :
        p->eState==PAGER_WRITER_DBMOD    ? "WRITER_DBMOD" :
        p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" :
        p->eState==PAGER_ERROR           ? "ERROR" : "?error?"
................................................................................
**
** If the pager has not already entered the error state, but an IO or
** malloc error occurs during a rollback, then this will itself cause 
** the pager to enter the error state. Which will be cleared by the
** call to pager_unlock(), as described above.
*/
static void pagerUnlockAndRollback(Pager *pPager){
  if( pPager->eState!=PAGER_ERROR ){
    assert( assert_pager_state(pPager) );
    if( pPager->eState>=PAGER_WRITER_INITIAL ){
      sqlite3BeginBenignMalloc();
      sqlite3PagerRollback(pPager);
      sqlite3EndBenignMalloc();
    }else if( pPager->eLock>=RESERVED_LOCK && !pPager->exclusiveMode ){

      pager_end_transaction(pPager, 0);
    }
  }
  pager_unlock(pPager);
}

/*







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







 







>
>
>
>












>
>
>

>
|
>
>
>







 







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







 







>






>







 







|






>







143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
...
289
290
291
292
293
294
295














296
297
298
299
300
301
302
...
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
....
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
**               |              |                |
**               |              V                |
**               |<-------WRITER_DBMOD---------->|
**               |              |                |
**               |              V                |
**               +<------WRITER_FINISHED-------->+
**
**
** List of state transitions and the C [function] that performs each:
** 
**   NONE              -> READER              [sqlite3PagerSharedLock]
**   READER            -> NONE                [pager_unlock]
**
**   READER            -> WRITER_INITIAL      [sqlite3PagerBegin]
**   WRITER_INITIAL    -> WRITER_CACHEMOD     [pager_open_journal]
**   WRITER_CACHEMOD   -> WRITER_DBMOD        [syncJournal]
**   WRITER_DBMOD      -> WRITER_FINISHED     [sqlite3PagerCommitPhaseOne]
**   WRITER_***        -> READER              [pager_end_transaction]
**
**   WRITER_***        -> ERROR               [pager_error]
**   ERROR             -> NONE                [pager_unlock]
** 
**
**  NONE:
**
**    The pager starts up in this state. Nothing is guaranteed in this
**    state - the file may or may not be locked and the database size is
**    unknown. The database may not be read or written.
**
**    * No read or write transaction is active.
................................................................................
**    A connection running with locking_mode=normal enters this state when
**    it opens a read-transaction on the database and returns to state
**    NONE after the read-transaction is completed. However a connection
**    running in locking_mode=exclusive (including temp databases) remains in
**    this state even after the read-transaction is closed. The only way
**    a locking_mode=exclusive connection can transition from READER to NONE
**    is via the ERROR state (see below).
**
**    TODO: Maybe WAL connections should behave like locking_mode=exclusive
**          connections and remain in READER state even when there is no
**          active read transaction.
** 
**    * A read transaction may be active (but a write-transaction cannot).
**    * A SHARED or greater lock is held on the database file.
**    * The dbSize variable may be trusted (even if a user-level read 
**      transaction is not active). The dbOrigSize and dbFileSize variables
**      may not be trusted at this point.
**    * If the database is a WAL database, then the WAL connection is open.
**    * Even if a read-transaction is not open, it is guaranteed that 
**      there is no hot-journal in the file-system.
**
**  WRITER_INITIAL:
**
**    The pager moves to this state from READER when a write-transaction
**    is first opened on the database.
**
**    * A write transaction is active.
**    * If the connection is open in rollback-mode, a RESERVED or greater 
**      lock is held on the database file.
**    * If the connection is open in WAL-mode, a WAL write transaction
**      is open (i.e. sqlite3WalBeginWriteTransaction() has been successfully
**      called).
**    * The dbSize, dbOrigSize and dbFileSize variables are all valid.
**    * The contents of the pager cache have not been modified.
**    * The journal file may or may not be open.
**    * Nothing (not even the first header) has been written to the journal.
**
**  WRITER_CACHEMOD:
**
................................................................................
**    statement executed within a transaction. In this case, if the error
**    code were simply returned to the user, the b-tree layer would not
**    automatically attempt a rollback, as it assumes that an error in a
**    read-only statement cannot leave the pager in an internally inconsistent 
**    state.
**    
**














** Notes:
**
**   * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the
**     connection is open in WAL mode. A WAL connection is always in one
**     of the first four states.
**
**   * Normally, a connection open in exclusive mode is never in PAGER_NONE
................................................................................
/*
** (gdb) printf "%s", print_pager_state(pPager)
*/
static char *print_pager_state(Pager *p){
  static char zRet[1024];

  sqlite3_snprintf(1024, zRet,
      "Filename:      %s\n"
      "State:         %s errCode=%d\n"
      "Lock:          %s\n"
      "Locking mode:  locking_mode=%s\n"
      "Journal mode:  journal_mode=%s\n"
      "Backing store: tempFile=%d memDb=%d useJournal=%d\n"
      "Journal:       journalOff=%lld journalHdr=%lld\n"
      , p->zFilename
      , p->eState==PAGER_NONE            ? "NONE" :
        p->eState==PAGER_READER          ? "READER" :
        p->eState==PAGER_WRITER_INITIAL  ? "WRITER_INITIAL" :
        p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" :
        p->eState==PAGER_WRITER_DBMOD    ? "WRITER_DBMOD" :
        p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" :
        p->eState==PAGER_ERROR           ? "ERROR" : "?error?"
................................................................................
**
** If the pager has not already entered the error state, but an IO or
** malloc error occurs during a rollback, then this will itself cause 
** the pager to enter the error state. Which will be cleared by the
** call to pager_unlock(), as described above.
*/
static void pagerUnlockAndRollback(Pager *pPager){
  if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_NONE ){
    assert( assert_pager_state(pPager) );
    if( pPager->eState>=PAGER_WRITER_INITIAL ){
      sqlite3BeginBenignMalloc();
      sqlite3PagerRollback(pPager);
      sqlite3EndBenignMalloc();
    }else if( pPager->eLock>=RESERVED_LOCK && !pPager->exclusiveMode ){
      assert( pPager->eState==PAGER_READER );
      pager_end_transaction(pPager, 0);
    }
  }
  pager_unlock(pPager);
}

/*

Added test/pager3.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
# 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
source $testdir/wal_common.tcl


foreach {tn sql res j} {
  1 "PRAGMA journal_mode = DELETE"  delete        0
  2 "CREATE TABLE t1(a, b)"         {}            0
  3 "PRAGMA locking_mode=EXCLUSIVE" {exclusive}   0
  4 "INSERT INTO t1 VALUES(1, 2)"   {}            1
  5 "PRAGMA locking_mode=NORMAL"    {normal}      1
  6 "SELECT * FROM t1"              {1 2}         0
} {
  do_execsql_test pager3-1.$tn.1 $sql $res
  do_test         pager3-1.$tn.2 { file exists test.db-journal } $j
}


finish_test