SQLite

Check-in [7c36147846]
Login

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

Overview
Comment:Fix a problem causing corruption when an UNLOCKED transaction is rolled back.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | begin-concurrent
Files: files | file ages | folders
SHA1: 7c36147846484c96023939864417b5624f3bc5f8
User & Date: dan 2015-08-20 20:25:56.029
Context
2015-08-21
14:21
Add extra tests and a fix for rollbacks of UNLOCKED transactions. (check-in: 82cd837e72 user: dan tags: begin-concurrent)
2015-08-20
20:25
Fix a problem causing corruption when an UNLOCKED transaction is rolled back. (check-in: 7c36147846 user: dan tags: begin-concurrent)
2015-08-19
20:27
When committing an unlocked transaction, relocate newly allocated database pages within the file to avoid conflicting with committed transactions. There are lots of things still to fix in this code. (check-in: 3bbc31d515 user: dan tags: begin-concurrent)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/pager.c.
3017
3018
3019
3020
3021
3022
3023

3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036

3037
3038
3039







3040
3041
3042
3043
3044
3045
3046

/*
** This function is called to rollback a transaction on a WAL database.
*/
static int pagerRollbackWal(Pager *pPager){
  int rc;                         /* Return Code */
  PgHdr *pList;                   /* List of dirty pages to revert */


  /* For all pages in the cache that are currently dirty or have already
  ** been written (but not committed) to the log file, do one of the 
  ** following:
  **
  **   + Discard the cached page (if refcount==0), or
  **   + Reload page content from the database (if refcount>0).
  */
  pPager->dbSize = pPager->dbOrigSize;
  rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
  pList = sqlite3PcacheDirtyList(pPager->pPCache);
  while( pList && rc==SQLITE_OK ){
    PgHdr *pNext = pList->pDirty;

    rc = pagerUndoCallback((void *)pPager, pList->pgno);
    pList = pNext;
  }








  return rc;
}

/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),







>













>



>
>
>
>
>
>
>







3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055

/*
** This function is called to rollback a transaction on a WAL database.
*/
static int pagerRollbackWal(Pager *pPager){
  int rc;                         /* Return Code */
  PgHdr *pList;                   /* List of dirty pages to revert */
  int bPage1 = 0;                 /* True if page 1 has been undone */

  /* For all pages in the cache that are currently dirty or have already
  ** been written (but not committed) to the log file, do one of the 
  ** following:
  **
  **   + Discard the cached page (if refcount==0), or
  **   + Reload page content from the database (if refcount>0).
  */
  pPager->dbSize = pPager->dbOrigSize;
  rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
  pList = sqlite3PcacheDirtyList(pPager->pPCache);
  while( pList && rc==SQLITE_OK ){
    PgHdr *pNext = pList->pDirty;
    if( pList->pgno==1 ) bPage1 = 1;
    rc = pagerUndoCallback((void *)pPager, pList->pgno);
    pList = pNext;
  }

  /* If this is an UNLOCKED transaction, then page 1 must be reread from the
  ** db file, even if it is not dirty. This is because the b-tree layer may
  ** have already zeroed the nFree and iTrunk header fields.  */
  if( rc==SQLITE_OK && bPage1==0 && pPager->pAllRead ){
    rc = pagerUndoCallback((void*)pPager, 1);
  }

  return rc;
}

/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
Changes to test/unlocked2.test.
86
87
88
89
90
91
92






93






94

95




















96
97
98
99
100


























101

    sql1 { 
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(randomblob(1500));
    }
    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
    }






    sql1 {






      COMMIT;

    }




















  } {}

  do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok}
}



























finish_test








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


|


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

>
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
151
152
153
154
155
156
157
158
159
160
161
    sql1 { 
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(randomblob(1500));
    }
    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
    }
    sql1 COMMIT
  } {}

  do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok}

  do_test 2.$tn.4  {
    sql1 { 
      BEGIN UNLOCKED;
        DELETE FROM t1;
    }
    sql2 {
      DELETE FROM t2;
    }
    sql1 COMMIT
  } {}

  do_test 2.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}

  do_test 2.$tn.6 {
    sql1 {
      INSERT INTO t1 VALUES(randomblob(1500));
      INSERT INTO t1 VALUES(randomblob(1500));
      INSERT INTO t2 VALUES(randomblob(1500));
      DELETE FROM t1 WHERE rowid=1;
    }

    sql1 {
      BEGIN UNLOCKED;
        DELETE FROM t1 WHERE rowid=2;
    }

    sql2 {
      DELETE FROM t2;
    }

    sql1 COMMIT
  } {}

  do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok}
}

reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500));
  DELETE FROM t1;
} {wal}

db close
sqlite3 db test.db
breakpoint

do_execsql_test 3.1 {
  BEGIN UNLOCKED;
    INSERT INTO t1 VALUES(1, 2);
  ROLLBACK;
}

do_execsql_test 3.2 {
  PRAGMA integrity_check;
} {ok}

do_execsql_test 3.3 {
  PRAGMA freelist_count;
} {2}

finish_test