/ Check-in [4761db83]
Login

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

Overview
Comment:When the final connection disconnects from a wal mode database, check that the database file has not been moved or unlinked before deleting the wal and shm files.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 4761db83b6d3d57f281370899403c102e39ad0021d315dd6a6912d250436782a
User & Date: dan 2018-02-07 16:14:41
References
2018-02-07
18:45
Fix typo in comment. Skip tests added by check-in [4761db83b6] when running on Windows. check-in: 468a389c user: mistachkin tags: trunk
Context
2018-02-07
18:02
In extensions rtree, fts3 and fts5, ensure that when dynamic buffers are bound to persistent SQL statements using SQLITE_STATIC, the binding is replaced with an SQL NULL before the buffer is freed. Otherwise, a user may obtain a pointer to the persistent statement using sqlite3_next_stmt() and attempt to access the freed buffer using sqlite3_expanded_sql() or similar. check-in: 2a5f813b user: dan tags: trunk
16:14
When the final connection disconnects from a wal mode database, check that the database file has not been moved or unlinked before deleting the wal and shm files. check-in: 4761db83 user: dan tags: trunk
2018-02-05
21:02
Adjust the previous check-in, which modified the Win32 VFS, so that it works with SQLITE_OMIT_WAL. check-in: 36c2e67e user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

  4098   4098     PgHdr *pNext;
  4099   4099     for(p=pPager->pMmapFreelist; p; p=pNext){
  4100   4100       pNext = p->pDirty;
  4101   4101       sqlite3_free(p);
  4102   4102     }
  4103   4103   }
  4104   4104   
         4105  +/* Verify that the database file has not be deleted or renamed out from
         4106  +** under the pager.  Return SQLITE_OK if the database is still were it ought
         4107  +** to be on disk.  Return non-zero (SQLITE_READONLY_DBMOVED or some other error
         4108  +** code from sqlite3OsAccess()) if the database has gone missing.
         4109  +*/
         4110  +static int databaseIsUnmoved(Pager *pPager){
         4111  +  int bHasMoved = 0;
         4112  +  int rc;
         4113  +
         4114  +  if( pPager->tempFile ) return SQLITE_OK;
         4115  +  if( pPager->dbSize==0 ) return SQLITE_OK;
         4116  +  assert( pPager->zFilename && pPager->zFilename[0] );
         4117  +  rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
         4118  +  if( rc==SQLITE_NOTFOUND ){
         4119  +    /* If the HAS_MOVED file-control is unimplemented, assume that the file
         4120  +    ** has not been moved.  That is the historical behavior of SQLite: prior to
         4121  +    ** version 3.8.3, it never checked */
         4122  +    rc = SQLITE_OK;
         4123  +  }else if( rc==SQLITE_OK && bHasMoved ){
         4124  +    rc = SQLITE_READONLY_DBMOVED;
         4125  +  }
         4126  +  return rc;
         4127  +}
         4128  +
  4105   4129   
  4106   4130   /*
  4107   4131   ** Shutdown the page cache.  Free all memory and close all files.
  4108   4132   **
  4109   4133   ** If a transaction was in progress when this routine is called, that
  4110   4134   ** transaction is rolled back.  All outstanding pages are invalidated
  4111   4135   ** and their memory is freed.  Any attempt to use a page associated
................................................................................
  4114   4138   **
  4115   4139   ** This function always succeeds. If a transaction is active an attempt
  4116   4140   ** is made to roll it back. If an error occurs during the rollback 
  4117   4141   ** a hot journal may be left in the filesystem but no error is returned
  4118   4142   ** to the caller.
  4119   4143   */
  4120   4144   int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
  4121         -  u8 *pTmp = (u8 *)pPager->pTmpSpace;
  4122         -
         4145  +  u8 *pTmp = (u8*)pPager->pTmpSpace;
  4123   4146     assert( db || pagerUseWal(pPager)==0 );
  4124   4147     assert( assert_pager_state(pPager) );
  4125   4148     disable_simulated_io_errors();
  4126   4149     sqlite3BeginBenignMalloc();
  4127   4150     pagerFreeMapHdrs(pPager);
  4128   4151     /* pPager->errCode = 0; */
  4129   4152     pPager->exclusiveMode = 0;
  4130   4153   #ifndef SQLITE_OMIT_WAL
  4131         -  assert( db || pPager->pWal==0 );
  4132         -  sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,
  4133         -      (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp)
  4134         -  );
  4135         -  pPager->pWal = 0;
         4154  +  {
         4155  +    u8 *a = 0;
         4156  +    assert( db || pPager->pWal==0 );
         4157  +    if( db && 0==(db->flags & SQLITE_NoCkptOnClose) 
         4158  +     && SQLITE_OK==databaseIsUnmoved(pPager)
         4159  +    ){
         4160  +      a = pTmp;
         4161  +    }
         4162  +    sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a);
         4163  +    pPager->pWal = 0;
         4164  +  }
  4136   4165   #endif
  4137   4166     pager_reset(pPager);
  4138   4167     if( MEMDB ){
  4139   4168       pager_unlock(pPager);
  4140   4169     }else{
  4141   4170       /* If it is open, sync the journal file before calling UnlockAndRollback.
  4142   4171       ** If this is not done, then an unsynced portion of the open journal 
................................................................................
  4963   4992     /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */
  4964   4993   
  4965   4994     *ppPager = pPager;
  4966   4995     return SQLITE_OK;
  4967   4996   }
  4968   4997   
  4969   4998   
  4970         -/* Verify that the database file has not be deleted or renamed out from
  4971         -** under the pager.  Return SQLITE_OK if the database is still were it ought
  4972         -** to be on disk.  Return non-zero (SQLITE_READONLY_DBMOVED or some other error
  4973         -** code from sqlite3OsAccess()) if the database has gone missing.
  4974         -*/
  4975         -static int databaseIsUnmoved(Pager *pPager){
  4976         -  int bHasMoved = 0;
  4977         -  int rc;
  4978         -
  4979         -  if( pPager->tempFile ) return SQLITE_OK;
  4980         -  if( pPager->dbSize==0 ) return SQLITE_OK;
  4981         -  assert( pPager->zFilename && pPager->zFilename[0] );
  4982         -  rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
  4983         -  if( rc==SQLITE_NOTFOUND ){
  4984         -    /* If the HAS_MOVED file-control is unimplemented, assume that the file
  4985         -    ** has not been moved.  That is the historical behavior of SQLite: prior to
  4986         -    ** version 3.8.3, it never checked */
  4987         -    rc = SQLITE_OK;
  4988         -  }else if( rc==SQLITE_OK && bHasMoved ){
  4989         -    rc = SQLITE_READONLY_DBMOVED;
  4990         -  }
  4991         -  return rc;
  4992         -}
  4993         -
  4994   4999   
  4995   5000   /*
  4996   5001   ** This function is called after transitioning from PAGER_UNLOCK to
  4997   5002   ** PAGER_SHARED state. It tests if there is a hot journal present in
  4998   5003   ** the file-system for the given pager. A hot journal is one that 
  4999   5004   ** needs to be played back. According to this function, a hot-journal
  5000   5005   ** file exists if the following criteria are met:

Changes to test/nockpt.test.

    57     57   } {1 2 3 4 5 6 7 8 9}
    58     58   
    59     59   do_execsql_test 1.13 { PRAGMA main.journal_mode } {wal}
    60     60   do_test 1.14 { sqlite3_db_config db NO_CKPT_ON_CLOSE 1 } {1}
    61     61   do_execsql_test 1.14 { PRAGMA main.journal_mode = delete } {delete}
    62     62   do_test 1.15 { file exists test.db-wal } {0}
    63     63   
           64  +#-------------------------------------------------------------------------
           65  +# Test an unusual scenario:
           66  +#
           67  +#   1. A wal mode db is opened and written. Then sqlite3_close_v2() used
           68  +#      to close the db handle while there is still an unfinalized
           69  +#      statement (so the db handle stays open).
           70  +#
           71  +#   2. The db, wal and *-shm files are deleted from the file system.
           72  +#
           73  +#   3. Another connection creates a new wal mode db at the same file-system
           74  +#      location as the previous one.
           75  +#
           76  +#   4. The statement left unfinalized in (1) is finalized.
           77  +#
           78  +# The test is to ensure that the connection left open in step (1) does
           79  +# not try to delete the wal file from the file-system as part of step
           80  +# 4.
           81  +#
           82  +reset_db
           83  +db close
           84  +
           85  +# Open a connection on a wal database. Write to it a bit. Then prepare
           86  +# a statement and call sqlite3_close_v2() (so that the statement handle
           87  +# holds the db connection open).
           88  +#
           89  +set ::db1 [sqlite3_open_v2 test.db SQLITE_OPEN_READWRITE ""]
           90  +do_test 2.0 {
           91  +  lindex [
           92  +    sqlite3_exec $::db1 {
           93  +      PRAGMA journal_mode = wal;
           94  +      CREATE TABLE t1(x PRIMARY KEY, y UNIQUE, z);
           95  +      INSERT INTO t1 VALUES(1, 2, 3);
           96  +      PRAGMA wal_checkpoint;
           97  +    }] 0
           98  +} {0}
           99  +set ::stmt [sqlite3_prepare $::db1 "SELECT * FROM t1" -1 dummy]
          100  +sqlite3_close_v2 $::db1
          101  +
          102  +# Delete the database, wal and shm files.
          103  +#
          104  +forcedelete test.db test.db-wal test.db-shm
          105  +
          106  +# Open and populate a new database file at the same file-system location
          107  +# as the one just deleted. Contrive a partial checkpoint on it.
          108  +#
          109  +sqlite3 db  test.db
          110  +sqlite3 db2 test.db
          111  +do_execsql_test 2.1 {
          112  +  PRAGMA journal_mode = wal;
          113  +  CREATE TABLE y1(a PRIMARY KEY, b UNIQUE, c);
          114  +  INSERT INTO y1 VALUES('a', 'b', 'c');
          115  +  INSERT INTO y1 VALUES('d', 'e', 'f');
          116  +} {wal}
          117  +do_execsql_test -db db2 2.2 {
          118  +  BEGIN;
          119  +    SELECT * FROM y1;
          120  +} {a b c d e f}
          121  +do_execsql_test 2.3 {
          122  +  UPDATE y1 SET c='g' WHERE a='d';
          123  +  PRAGMA wal_checkpoint;
          124  +} {0 11 10}
          125  +do_execsql_test -db db2 2.4 {
          126  +  COMMIT
          127  +}
          128  +
          129  +# Finalize the statement handle, causing the first connection to be
          130  +# closed. Test that this has not corrupted the database file by 
          131  +# deleting the new wal file from the file-system. If it has, this
          132  +# test should fail with an IO or corruption error.
          133  +#
          134  +do_test 2.5 {
          135  +  sqlite3_finalize $::stmt
          136  +  sqlite3 db3 test.db
          137  +  execsql { 
          138  +    PRAGMA integrity_check; 
          139  +    SELECT * FROM y1;
          140  +  } db3
          141  +} {ok a b c d e g}
    64    142   
    65    143   
    66    144   finish_test