SQLite

Check-in [eba8a564e6]
Login

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

Overview
Comment:Handle a few obscure problems that could manifest if a database corrupted in a certain way was written by a connection in the middle of a SELECT statement on the same db.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: eba8a564e62f84a9620008beead80081fe90a1b7
User & Date: dan 2014-01-20 18:25:44.841
Context
2014-01-20
19:55
In where.c, do not allocate space in sqlite3_index_info structures for the internal WHERE clause "terms" generated to record column equivalencies. Fix for ticket [1a1a194d1e5f8]. (check-in: 7d9e22187d user: dan tags: trunk)
18:25
Handle a few obscure problems that could manifest if a database corrupted in a certain way was written by a connection in the middle of a SELECT statement on the same db. (check-in: eba8a564e6 user: dan tags: trunk)
14:58
Remove an unused #define and add an assert(), both associated with WITH logic. (check-in: a06235e0f6 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
*/
#ifndef NDEBUG
  static void assertCellInfo(BtCursor *pCur){
    CellInfo info;
    int iPage = pCur->iPage;
    memset(&info, 0, sizeof(info));
    btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
    assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
  }
#else
  #define assertCellInfo(x)
#endif
#ifdef _MSC_VER
  /* Use a real function in MSVC to work around bugs in that compiler. */
  static void getCellInfo(BtCursor *pCur){







|







3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
*/
#ifndef NDEBUG
  static void assertCellInfo(BtCursor *pCur){
    CellInfo info;
    int iPage = pCur->iPage;
    memset(&info, 0, sizeof(info));
    btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
    assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 );
  }
#else
  #define assertCellInfo(x)
#endif
#ifdef _MSC_VER
  /* Use a real function in MSVC to work around bugs in that compiler. */
  static void getCellInfo(BtCursor *pCur){
4386
4387
4388
4389
4390
4391
4392
4393



4394
4395
4396
4397






4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
    rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
                        pCur->wrFlag==0 ? PAGER_GET_READONLY : 0);
    if( rc!=SQLITE_OK ){
      pCur->eState = CURSOR_INVALID;
      return rc;
    }
    pCur->iPage = 0;




    /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
    ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
    ** NULL, the caller expects a table b-tree. If this is not the case,
    ** return an SQLITE_CORRUPT error.  */






    assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 );
    if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){
      return SQLITE_CORRUPT_BKPT;
    }
  }

  /* Assert that the root page is of the correct type. This must be the
  ** case as the call to this function that loaded the root-page (either
  ** this call or a previous invocation) would have detected corruption 
  ** if the assumption were not true, and it is not possible for the flags 
  ** byte to have been modified while this cursor is holding a reference
  ** to the page.  */
  pRoot = pCur->apPage[0];
  assert( pRoot->pgno==pCur->pgnoRoot );
  assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey );

  pCur->aiIdx[0] = 0;
  pCur->info.nSize = 0;
  pCur->atLast = 0;
  pCur->validNKey = 0;

  if( pRoot->nCell>0 ){







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







4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410











4411
4412
4413
4414
4415
4416
4417
    rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
                        pCur->wrFlag==0 ? PAGER_GET_READONLY : 0);
    if( rc!=SQLITE_OK ){
      pCur->eState = CURSOR_INVALID;
      return rc;
    }
    pCur->iPage = 0;
  }
  pRoot = pCur->apPage[0];
  assert( pRoot->pgno==pCur->pgnoRoot );

  /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
  ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
  ** NULL, the caller expects a table b-tree. If this is not the case,
  ** return an SQLITE_CORRUPT error. 
  **
  ** Earlier versions of SQLite assumed that this test could not fail
  ** if the root page was already loaded when this function was called (i.e.
  ** if pCur->iPage>=0). But this is not so if the database is corrupted 
  ** in such a way that page pRoot is linked into a second b-tree table 
  ** (or the freelist).  */
  assert( pRoot->intKey==1 || pRoot->intKey==0 );
  if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
    return SQLITE_CORRUPT_BKPT;
  }












  pCur->aiIdx[0] = 0;
  pCur->info.nSize = 0;
  pCur->atLast = 0;
  pCur->validNKey = 0;

  if( pRoot->nCell>0 ){
5247
5248
5249
5250
5251
5252
5253

5254
5255
5256
5257
5258
5259
5260

end_allocate_page:
  releasePage(pTrunk);
  releasePage(pPrevTrunk);
  if( rc==SQLITE_OK ){
    if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
      releasePage(*ppPage);

      return SQLITE_CORRUPT_BKPT;
    }
    (*ppPage)->isInit = 0;
  }else{
    *ppPage = 0;
  }
  assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) );







>







5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259

end_allocate_page:
  releasePage(pTrunk);
  releasePage(pPrevTrunk);
  if( rc==SQLITE_OK ){
    if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
      releasePage(*ppPage);
      *ppPage = 0;
      return SQLITE_CORRUPT_BKPT;
    }
    (*ppPage)->isInit = 0;
  }else{
    *ppPage = 0;
  }
  assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) );
Added test/corruptH.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
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
# 2014-01-20
#
# 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
set testprefix corruptH

# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec
database_may_be_corrupt

# Initialize the database.
#
do_execsql_test 1.1 {
  PRAGMA page_size=1024;

  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(1, 'one');
  INSERT INTO t1 VALUES(2, 'two');

  CREATE TABLE t2(x);
  INSERT INTO t2 VALUES(randomblob(200));
  INSERT INTO t2 SELECT randomblob(200) FROM t2;
  INSERT INTO t2 SELECT randomblob(200) FROM t2;
  INSERT INTO t2 SELECT randomblob(200) FROM t2;
  INSERT INTO t2 SELECT randomblob(200) FROM t2;
  INSERT INTO t2 SELECT randomblob(200) FROM t2;
  INSERT INTO t2 SELECT randomblob(200) FROM t2;
} {}

# Corrupt the file so that the root page of t1 is also linked into t2 as
# a leaf page.
#
do_test 1.2 {
  db eval { SELECT name, rootpage FROM sqlite_master } { 
    set r($name) $rootpage 
  }
  db close
  hexio_write test.db [expr {($r(t2)-1)*1024 + 11}] [format %.2X $r(t1)]
  sqlite3 db test.db
} {}

do_test 1.3 {
  db eval { PRAGMA secure_delete=1 }
  list [catch {
    db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
      db eval { DELETE FROM t2 }
    }
  } msg] $msg
} {1 {database disk image is malformed}}

#-------------------------------------------------------------------------
reset_db

# Initialize the database.
#
do_execsql_test 2.1 {
  PRAGMA page_size=1024;

  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(1, 'one');
  INSERT INTO t1 VALUES(2, 'two');

  CREATE TABLE t3(x);

  CREATE TABLE t2(x PRIMARY KEY) WITHOUT ROWID;
  INSERT INTO t2 VALUES(randomblob(100));

  DROP TABLE t3;
} {}

do_test 2.2 {
  db eval { SELECT name, rootpage FROM sqlite_master } { 
    set r($name) $rootpage 
  }
  db close
  set fl [hexio_get_int [hexio_read test.db 32 4]]

  hexio_write test.db [expr {($fl-1) * 1024 + 0}] 00000000 
  hexio_write test.db [expr {($fl-1) * 1024 + 4}] 00000001 
  hexio_write test.db [expr {($fl-1) * 1024 + 8}] [format %.8X $r(t1)]
  hexio_write test.db 36 00000002

  sqlite3 db test.db
} {}

do_test 2.3 {
  list [catch {
  db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
    db eval { 
      INSERT INTO t2 SELECT randomblob(100) FROM t2;
      INSERT INTO t2 SELECT randomblob(100) FROM t2;
      INSERT INTO t2 SELECT randomblob(100) FROM t2;
      INSERT INTO t2 SELECT randomblob(100) FROM t2;
      INSERT INTO t2 SELECT randomblob(100) FROM t2;
    }
  }
  } msg] $msg
} {1 {database disk image is malformed}}

#-------------------------------------------------------------------------
reset_db

# Initialize the database.
#
do_execsql_test 3.1 {
  PRAGMA page_size=1024;

  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(1, 'one');
  INSERT INTO t1 VALUES(2, 'two');

  CREATE TABLE t2(c INTEGER PRAGMA KEY, d);
  INSERT INTO t2 VALUES(1, randomblob(1100));
} {}

do_test 3.2 {
  db eval { SELECT name, rootpage FROM sqlite_master } { 
    set r($name) $rootpage 
  }
  db close

  hexio_write test.db [expr {($r(t2)-1) * 1024 + 1020}] 00000002

  sqlite3 db test.db
} {}

do_test 3.3 {
  list [catch {
  db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
    db eval { 
      DELETE FROM t2 WHERE c=1;
    }
  }
  } msg] $msg
} {1 {database disk image is malformed}}

finish_test