/ Check-in [8dbe89d0]
Login

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

Overview
Comment:Fix a case where a checkpoint operation could write to an invalid part of a memory mapped region.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental-mmap
Files: files | file ages | folders
SHA1: 8dbe89d05ce91428c69003f0da79d883fa23e2b5
User & Date: dan 2013-03-23 14:20:42
Context
2013-03-23
17:29
Improve a comment in wal.c. No code changes. check-in: 60b9f5e4 user: dan tags: experimental-mmap
14:20
Fix a case where a checkpoint operation could write to an invalid part of a memory mapped region. check-in: 8dbe89d0 user: dan tags: experimental-mmap
12:15
In the winMremap VFS function, unmap the region prior to attempting to truncate the file. check-in: 8870c4cc user: mistachkin tags: experimental-mmap
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/pager.c.

4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246

4247
4248
4249
4250
4251
4252
4253
4254

4255
4256
4257
4258
4259
4260
4261
**   * If mmap is being used, then the mapping is extended to szReq
**     bytes in size.
**
** SQLITE_OK is returned if successful, or an error code if an error occurs.
*/
int sqlite3PagerSetFilesize(Pager *pPager, i64 szReq){
  int rc;
  sqlite3_int64 sz;

  assert( pPager->eState==PAGER_OPEN );
  assert( pPager->nMmapOut==0 );

  rc = sqlite3OsFileSize(pPager->fd, &sz);
  if( rc==SQLITE_OK ){
    if( sz>szReq ){
      sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &sz);
    }
  }


  if( rc==SQLITE_OK 
   && pPager->nMapLimit>0 
   && pPager->nMapValid<szReq 
   && pPager->nMapValid<pPager->nMapLimit 
  ){
    pPager->dbFileSize = (szReq / pPager->pageSize);
    rc = pagerMap(pPager, 1);
  }


  return rc;
}

/*
** The argument is the first in a linked list of dirty pages connected
** by the PgHdr.pDirty pointer. This function writes each one of the







|











>
|
|
|
<
<



>







4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250


4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
**   * If mmap is being used, then the mapping is extended to szReq
**     bytes in size.
**
** SQLITE_OK is returned if successful, or an error code if an error occurs.
*/
int sqlite3PagerSetFilesize(Pager *pPager, i64 szReq){
  int rc;
  i64 sz;                         /* Size of file on disk in bytes */

  assert( pPager->eState==PAGER_OPEN );
  assert( pPager->nMmapOut==0 );

  rc = sqlite3OsFileSize(pPager->fd, &sz);
  if( rc==SQLITE_OK ){
    if( sz>szReq ){
      sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &sz);
    }
  }


  if( rc==SQLITE_OK ){
    i64 szMap = (szReq > pPager->nMapLimit) ? pPager->nMapLimit : szReq;
    if( pPager->nMapValid!=pPager->nMap || szMap!=pPager->nMap ){


      pPager->dbFileSize = (szReq / pPager->pageSize);
      rc = pagerMap(pPager, 1);
    }
  }

  return rc;
}

/*
** The argument is the first in a linked list of dirty pages connected
** by the PgHdr.pDirty pointer. This function writes each one of the

Changes to test/mmap1.test.

19
20
21
22
23
24
25
26
27
28
29



30
31
32

33
34
35
36
37
38
39
..
75
76
77
78
79
80
81





82








































83
84
  set bt [btree_from_db $db]
  db_enter $db
  array set stats [btree_pager_stats $bt]
  db_leave $db
  return $stats(read)
}

foreach {t mmap_size nRead} {
  1 { PRAGMA mmap_size = -65536 }   4
  2 { PRAGMA mmap_size = -50    } 156
  3 { PRAGMA mmap_size = 0      } 344



} {
  do_multiclient_test tn {
    sql1 $mmap_size


    code2 {
      set ::rcnt 0
      proc rblob {n} {
        set ::rcnt [expr (($::rcnt << 3) + $::rcnt + 456) & 0xFFFFFFFF]
        set str [format %.8x [expr $::rcnt ^ 0xbdf20da3]] 
        string range [string repeat $str [expr $n/4]] 1 $n
................................................................................

    # Check that the number of pages read by connection 1 indicates that the
    # "PRAGMA mmap_size" command worked.
    do_test $t.$tn.5 { nRead db } $nRead
  }
}















































finish_test








|
|
|
|
>
>
>



>







 







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


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
..
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  set bt [btree_from_db $db]
  db_enter $db
  array set stats [btree_pager_stats $bt]
  db_leave $db
  return $stats(read)
}

foreach {t mmap_size nRead c2init} {
  1.1 { PRAGMA mmap_size = -65536 }   4 {}
  1.2 { PRAGMA mmap_size = -50    } 156 {}
  1.3 { PRAGMA mmap_size = 0      } 344 {}
  1.4 { PRAGMA mmap_size = -65536 }   4 {PRAGMA mmap_size = -65536}
  1.5 { PRAGMA mmap_size = -50    } 156 {PRAGMA mmap_size = -65536}
  1.6 { PRAGMA mmap_size = 0      } 344 {PRAGMA mmap_size = -65536}
} {
  do_multiclient_test tn {
    sql1 $mmap_size
    sql2 $c2init

    code2 {
      set ::rcnt 0
      proc rblob {n} {
        set ::rcnt [expr (($::rcnt << 3) + $::rcnt + 456) & 0xFFFFFFFF]
        set str [format %.8x [expr $::rcnt ^ 0xbdf20da3]] 
        string range [string repeat $str [expr $n/4]] 1 $n
................................................................................

    # Check that the number of pages read by connection 1 indicates that the
    # "PRAGMA mmap_size" command worked.
    do_test $t.$tn.5 { nRead db } $nRead
  }
}

set ::rcnt 0
proc rblob {n} {
  set ::rcnt [expr (($::rcnt << 3) + $::rcnt + 456) & 0xFFFFFFFF]
  set str [format %.8x [expr $::rcnt ^ 0xbdf20da3]] 
  string range [string repeat $str [expr $n/4]] 1 $n
}

reset_db
db func rblob rblob

do_execsql_test 2.1 {
  PRAGMA auto_vacuum = 1;
  PRAGMA mmap_size = -65536;
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(a, b, UNIQUE(a, b));
  INSERT INTO t1 VALUES(rblob(500), rblob(500));
  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    2
  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    4
  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    8
  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --   16
  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --   32
  PRAGMA wal_checkpoint;
} {wal 0 103 103}

do_execsql_test 2.2 {
  PRAGMA auto_vacuum;
  SELECT count(*) FROM t1;
} {1 32}

do_test 2.3 {
  sqlite3 db2 test.db
  db2 func rblob rblob
  db2 eval { 
    DELETE FROM t1 WHERE (rowid%4);
    PRAGMA wal_checkpoint;
  }
  db2 eval { 
    INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    16
    SELECT count(*) FROM t1;
  }
} {16}

do_execsql_test 2.4 {
  PRAGMA wal_checkpoint;
} {0 24 24}

finish_test

Changes to test/speed1p.test.

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  return $txt
}

# Create a database schema.
#
do_test speed1p-1.0 {
  execsql {
    PRAGMA mmap_size=65536;
    PRAGMA journal_mode=wal;

    PRAGMA page_size=1024;
    PRAGMA cache_size=500;
    PRAGMA locking_mode=EXCLUSIVE;
    CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
    CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
    CREATE INDEX i2a ON t2(a);
    CREATE INDEX i2b ON t2(b);







<
<
<







61
62
63
64
65
66
67



68
69
70
71
72
73
74
  return $txt
}

# Create a database schema.
#
do_test speed1p-1.0 {
  execsql {



    PRAGMA page_size=1024;
    PRAGMA cache_size=500;
    PRAGMA locking_mode=EXCLUSIVE;
    CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
    CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
    CREATE INDEX i2a ON t2(a);
    CREATE INDEX i2b ON t2(b);