/ Check-in [ffce4aac]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Make the winTruncate() method of the windows VFS be a no-op if there are outstanding references to the memory-mapped pages. Otherwise, memory might be deleted out from under those references when the file is remapped during the truncate operation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ffce4aac18dacbf2a3112ae2ab56c7db20cb164f179683d90a66ef38f4a98f2b
User & Date: drh 2018-11-24 17:46:07
Context
2018-11-24
20:44
Fix a bug in the geopoly_ccw() function. Test cases in TH3. check-in: 773c5c1d user: drh tags: trunk
17:46
Make the winTruncate() method of the windows VFS be a no-op if there are outstanding references to the memory-mapped pages. Otherwise, memory might be deleted out from under those references when the file is remapped during the truncate operation. check-in: ffce4aac user: drh tags: trunk
16:07
Remove the unused mmapSizeActual field from the Windows sqlite3_file implementation. check-in: 0e7aa622 user: drh tags: trunk
2018-11-23
13:21
Make the winTruncate() method of the windows VFS be a no-op if there are outstanding references to the memory-mapped pages. Otherwise, memory might be deleted out from under those references when the file is remapped during the truncate operation. Leaf check-in: 8576ccb4 user: drh tags: branch-3.25
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/os_win.c.

2901
2902
2903
2904
2905
2906
2907




















2908
2909
2910
2911
2912
2913
2914
*/
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
  winFile *pFile = (winFile*)id;  /* File handle object */
  int rc = SQLITE_OK;             /* Return code for this function */
  DWORD lastErrno;
#if SQLITE_MAX_MMAP_SIZE>0
  sqlite3_int64 oldMmapSize;




















#endif

  assert( pFile );
  SimulateIOError(return SQLITE_IOERR_TRUNCATE);
  OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n",
           osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype));








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







2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
*/
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
  winFile *pFile = (winFile*)id;  /* File handle object */
  int rc = SQLITE_OK;             /* Return code for this function */
  DWORD lastErrno;
#if SQLITE_MAX_MMAP_SIZE>0
  sqlite3_int64 oldMmapSize;
  if( pFile->nFetchOut>0 ){
    /* File truncation is a no-op if there are outstanding memory mapped
    ** pages.  This is because truncating the file means temporarily unmapping
    ** the file, and that might delete memory out from under existing cursors.
    **
    ** This can result in incremental vacuum not truncating the file,
    ** if there is an active read cursor when the incremental vacuum occurs.
    ** No real harm comes of this - the database file is not corrupted,
    ** though some folks might complain that the file is bigger than it
    ** needs to be.
    **
    ** The only feasible work-around is to defer the truncation until after
    ** all references to memory-mapped content are closed.  That is doable,
    ** but involves adding a few branches in the common write code path which
    ** could slow down normal operations slightly.  Hence, we have decided for
    ** now to simply make trancations a no-op if there are pending reads.  We
    ** can maybe revisit this decision in the future.
    */
    return SQLITE_OK;
  }
#endif

  assert( pFile );
  SimulateIOError(return SQLITE_IOERR_TRUNCATE);
  OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n",
           osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype));

Changes to test/incrvacuum.test.

778
779
780
781
782
783
784
785
















































786
  catchsql {INSERT INTO t2 SELECT * FROM t1}

  execsql { 
    COMMIT;
    PRAGMA integrity_check;
  }
} {ok}

















































finish_test








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

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
  catchsql {INSERT INTO t2 SELECT * FROM t1}

  execsql { 
    COMMIT;
    PRAGMA integrity_check;
  }
} {ok}

#-------------------------------------------------------------------------
# At one point it was unsafe to truncate a db file on windows while there
# were outstanding xFetch() references. This test case attempts to hit
# that case.
#
reset_db
do_execsql_test incrvacuum-16.0 {
  PRAGMA auto_vacuum = 2;
  CREATE TABLE t3(a);
  INSERT INTO t3 VALUES(1), (2), (3), (4);

  CREATE TABLE t2(x);
  INSERT INTO t2 VALUES( randomblob(1000) );
  INSERT INTO t2 VALUES( randomblob(1000) );
  INSERT INTO t2 VALUES( randomblob(1000) );
  INSERT INTO t2 VALUES( randomblob(1000) );
  INSERT INTO t2 VALUES( randomblob(1000) );
  INSERT INTO t2 VALUES( randomblob(1000) );
} {}

# Reopen db to ensure the page-cache is empty.
#
db close
sqlite3 db test.db

# Open db in mmap-mode. Open a transaction, delete some data, then run
# incremental-vacuum. Do not commit the transaction. 
#
do_execsql_test incrvacuum-16.1 {
  PRAGMA mmap_size = 1000000;
  BEGIN;
  DELETE FROM t2;
  PRAGMA incremental_vacuum = 1000;
} {1000000}

# Scan through table t3 (which is all clean pages - so mmap is used). Then,
# midway through, commit the transaction. This causes the db to be truncated
# while there are outstanding xFetch pages.
#
do_test incrvacuum-16.2 {
  set res [list]
  db eval { SELECT a FROM t3 } {
    if {$a==3} { db eval COMMIT }
    lappend res $a
  }
  set res
} {1 2 3 4}

finish_test