SQLite

Check-in [2fb3fdcdf0]
Login

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

Overview
Comment:Fix for ticket #9 (again). The rollback journal files should now also be byte-order independent. (CVS 705)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2fb3fdcdf06c1206bf14da640c2f9e599455f0eb
User & Date: drh 2002-08-12 12:29:57.000
Context
2002-08-13
00:01
Updates prior to release 2.6.3. (CVS 706) (check-in: 34c4149eea user: drh tags: trunk)
2002-08-12
12:29
Fix for ticket #9 (again). The rollback journal files should now also be byte-order independent. (CVS 705) (check-in: 2fb3fdcdf0 user: drh tags: trunk)
2002-08-11
20:10
Fix for ticket #9: Add the ability to read little-endian database files from a big-endian machine and vice versa. (CVS 704) (check-in: ce4b943bf6 user: drh tags: trunk)
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to src/pager.c.
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28







-
+







** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.49 2002/07/07 16:52:47 drh Exp $
** @(#) $Id: pager.c,v 1.50 2002/08/12 12:29:57 drh Exp $
*/
#include "sqliteInt.h"
#include "pager.h"
#include "os.h"
#include <assert.h>
#include <string.h>

120
121
122
123
124
125
126

127
128
129
130
131
132
133
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134







+







  u8 state;                   /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
  u8 errMask;                 /* One of several kinds of errors */
  u8 tempFile;                /* zFilename is a temporary file */
  u8 readOnly;                /* True for a read-only database */
  u8 needSync;                /* True if an fsync() is needed on the journal */
  u8 dirtyFile;               /* True if database file has changed in any way */
  u8 alwaysRollback;          /* Disable dont_rollback() for all pages */
  u8 journalFormat;           /* Version number of the journal file */
  u8 *aInJournal;             /* One bit for each page in the database file */
  u8 *aInCkpt;                /* One bit for each page in the database */
  PgHdr *pFirst, *pLast;      /* List of free pages */
  PgHdr *pAll;                /* List of all pages */
  PgHdr *aHash[N_PG_HASH];    /* Hash table to map page number of PgHdr */
};

149
150
151
152
153
154
155






156



157
158

159











160
161
162
163
164
165
166
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187







+
+
+
+
+
+

+
+
+

-
+

+
+
+
+
+
+
+
+
+
+
+







  Pgno pgno;                     /* The page number */
  char aData[SQLITE_PAGE_SIZE];  /* Original data for page pgno */
};

/*
** Journal files begin with the following magic string.  The data
** was obtained from /dev/random.  It is used only as a sanity check.
**
** There are two journal formats.  The older journal format writes
** 32-bit integers in the byte-order of the host machine.  The new
** format writes integers as big-endian.  All new journals use the
** new format, but we have to be able to read an older journal in order
** to roll it back.
*/
static const unsigned char aOldJournalMagic[] = {
  0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd4,
};
static const unsigned char aJournalMagic[] = {
  0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd4,
  0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd5,
};
#define SQLITE_NEW_JOURNAL_FORMAT 1
#define SQLITE_OLD_JOURNAL_FORMAT 0

/*
** The following integer, if set, causes journals to be written in the
** old format.  This is used for testing purposes only - to make sure
** the code is able to rollback an old journal.
*/
#ifdef SQLITE_TEST
int pager_old_format = 0;
#endif

/*
** Hash a page number
*/
#define pager_hash(PN)  ((PN)%N_PG_HASH)

/*
177
178
179
180
181
182
183



































184
185
186
187
188
189
190
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    );
    cnt++;   /* Something to set a breakpoint on */
  }
# define REFINFO(X)  pager_refinfo(X)
#else
# define REFINFO(X)
#endif

/*
** Read a 32-bit integer from the given file descriptor
*/
static int read32bits(Pager *pPager, OsFile *fd, u32 *pRes){
  u32 res;
  int rc;
  rc = sqliteOsRead(fd, &res, sizeof(res));
  if( rc==SQLITE_OK && pPager->journalFormat==SQLITE_NEW_JOURNAL_FORMAT ){
    unsigned char ac[4];
    memcpy(ac, &res, 4);
    res = (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3];
  }
  *pRes = res;
  return rc;
}

/*
** Write a 32-bit integer into the given file descriptor.  Writing
** is always done using the new journal format.
*/
static int write32bits(OsFile *fd, u32 val){
  unsigned char ac[4];
#ifdef SQLITE_TEST
  if( pager_old_format ){
    return sqliteOsWrite(fd, &val, 4);
  }
#endif
  ac[0] = (val>>24) & 0xff;
  ac[1] = (val>>16) & 0xff;
  ac[2] = (val>>8) & 0xff;
  ac[3] = val & 0xff;
  return sqliteOsWrite(fd, ac, 4);
}


/*
** Convert the bits in the pPager->errMask into an approprate
** return code.
*/
static int pager_errcode(Pager *pPager){
  int rc = SQLITE_OK;
277
278
279
280
281
282
283


284

285
286
287
288
289
290
291
333
334
335
336
337
338
339
340
341

342
343
344
345
346
347
348
349







+
+
-
+







** jfd.  Playback this one page.
*/
static int pager_playback_one_page(Pager *pPager, OsFile *jfd){
  int rc;
  PgHdr *pPg;              /* An existing page in the cache */
  PageRecord pgRec;

  rc = read32bits(pPager, jfd, &pgRec.pgno);
  if( rc!=SQLITE_OK ) return rc;
  rc = sqliteOsRead(jfd, &pgRec, sizeof(pgRec));
  rc = sqliteOsRead(jfd, &pgRec.aData, sizeof(pgRec.aData));
  if( rc!=SQLITE_OK ) return rc;

  /* Sanity checking on the page */
  if( pgRec.pgno>pPager->dbSize || pgRec.pgno==0 ) return SQLITE_CORRUPT;

  /* Playback the page.  Update the in-memory copy of the page
  ** at the same time, if there is one.
343
344
345
346
347
348
349
350









351
352
353
354

355
356
357
358
359
360
361
401
402
403
404
405
406
407

408
409
410
411
412
413
414
415
416
417
418
419

420
421
422
423
424
425
426
427







-
+
+
+
+
+
+
+
+
+



-
+







    goto end_playback;
  }

  /* Read the beginning of the journal and truncate the
  ** database file back to its original size.
  */
  rc = sqliteOsRead(&pPager->jfd, aMagic, sizeof(aMagic));
  if( rc!=SQLITE_OK || memcmp(aMagic,aJournalMagic,sizeof(aMagic))!=0 ){
  if( rc!=SQLITE_OK ){
    rc = SQLITE_PROTOCOL;
    goto end_playback;
  }
  if( memcmp(aMagic, aOldJournalMagic, sizeof(aMagic))==0 ){
    pPager->journalFormat = SQLITE_OLD_JOURNAL_FORMAT;
  }else if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))==0 ){
    pPager->journalFormat = SQLITE_NEW_JOURNAL_FORMAT;
  }else{
    rc = SQLITE_PROTOCOL;
    goto end_playback;
  }
  rc = sqliteOsRead(&pPager->jfd, &mxPg, sizeof(mxPg));
  rc = read32bits(pPager, &pPager->jfd, &mxPg);
  if( rc!=SQLITE_OK ){
    goto end_playback;
  }
  rc = sqliteOsTruncate(&pPager->fd, mxPg*SQLITE_PAGE_SIZE);
  if( rc!=SQLITE_OK ){
    goto end_playback;
  }
412
413
414
415
416
417
418

419
420
421
422
423
424
425
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492







+







    goto end_ckpt_playback;
  }
  nRec /= sizeof(PageRecord);
  
  /* Copy original pages out of the checkpoint journal and back into the
  ** database file.
  */
  pPager->journalFormat = SQLITE_NEW_JOURNAL_FORMAT;
  for(i=nRec-1; i>=0; i--){
    rc = pager_playback_one_page(pPager, &pPager->cpfd);
    if( rc!=SQLITE_OK ) goto end_ckpt_playback;
  }

  /* Figure out how many pages need to be copied out of the transaction
  ** journal.
843
844
845
846
847
848
849
850

851
852
853
854
855
856
857
910
911
912
913
914
915
916

917
918
919
920
921
922
923
924







-
+







      ** dirty free pages into the database file, thus making them
      ** clean pages and available for recycling.
      **
      ** We have to sync the journal before writing a page to the main
      ** database.  But syncing is a very slow operation.  So after a
      ** sync, it is best to write everything we can back to the main
      ** database to minimize the risk of having to sync again in the
      ** near future.  That is way we write all dirty pages after a
      ** near future.  That is why we write all dirty pages after a
      ** sync.
      */
      if( pPg==0 ){
        int rc = syncAllPages(pPager);
        if( rc!=0 ){
          sqlitepager_rollback(pPager);
          *ppPage = 0;
1069
1070
1071
1072
1073
1074
1075





1076





1077
1078

1079
1080
1081
1082
1083
1084
1085
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147

1148
1149
1150
1151
1152
1153

1154
1155
1156
1157
1158
1159
1160
1161







+
+
+
+
+
-
+
+
+
+
+

-
+







    pPager->journalOpen = 1;
    pPager->needSync = 0;
    pPager->dirtyFile = 0;
    pPager->alwaysRollback = 0;
    pPager->state = SQLITE_WRITELOCK;
    sqlitepager_pagecount(pPager);
    pPager->origDbSize = pPager->dbSize;
#ifdef SQLITE_TEST
    if( pager_old_format ){
      rc = sqliteOsWrite(&pPager->jfd, aOldJournalMagic,
                         sizeof(aOldJournalMagic));
    }else{
    rc = sqliteOsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
      rc = sqliteOsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
    }
#else
    rc = sqliteOsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
#endif
    if( rc==SQLITE_OK ){
      rc = sqliteOsWrite(&pPager->jfd, &pPager->dbSize, sizeof(Pgno));
      rc = write32bits(&pPager->jfd, pPager->dbSize);
    }
    if( rc!=SQLITE_OK ){
      rc = pager_unwritelock(pPager);
      if( rc==SQLITE_OK ) rc = SQLITE_FULL;
    }
  }
  return rc;
1140
1141
1142
1143
1144
1145
1146
1147

1148
1149
1150
1151
1152
1153
1154
1216
1217
1218
1219
1220
1221
1222

1223
1224
1225
1226
1227
1228
1229
1230







-
+







  assert( pPager->journalOpen );

  /* The transaction journal now exists and we have a write lock on the
  ** main database file.  Write the current page to the transaction 
  ** journal if it is not there already.
  */
  if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
    rc = sqliteOsWrite(&pPager->jfd, &pPg->pgno, sizeof(Pgno));
    rc = write32bits(&pPager->jfd, pPg->pgno);
    if( rc==SQLITE_OK ){
      rc = sqliteOsWrite(&pPager->jfd, pData, SQLITE_PAGE_SIZE);
    }
    if( rc!=SQLITE_OK ){
      sqlitepager_rollback(pPager);
      pPager->errMask |= PAGER_ERR_FULL;
      return rc;
1164
1165
1166
1167
1168
1169
1170
1171

1172
1173
1174
1175
1176
1177
1178
1240
1241
1242
1243
1244
1245
1246

1247
1248
1249
1250
1251
1252
1253
1254







-
+







  }

  /* If the checkpoint journal is open and the page is not in it,
  ** then write the current page to the checkpoint journal.
  */
  if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
    assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
    rc = sqliteOsWrite(&pPager->cpfd, &pPg->pgno, sizeof(Pgno));
    rc = write32bits(&pPager->cpfd, pPg->pgno);
    if( rc==SQLITE_OK ){
      rc = sqliteOsWrite(&pPager->cpfd, pData, SQLITE_PAGE_SIZE);
    }
    if( rc!=SQLITE_OK ){
      sqlitepager_rollback(pPager);
      pPager->errMask |= PAGER_ERR_FULL;
      return rc;
Changes to src/pager.h.
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23







-
+







**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.16 2002/03/05 12:41:20 drh Exp $
** @(#) $Id: pager.h,v 1.17 2002/08/12 12:29:57 drh Exp $
*/

/*
** The size of one page
**
** You can change this value to another (reasonable) power of two
** such as 512, 2048, 4096, or 8192 and things will still work.  But
69
70
71
72
73
74
75

76
69
70
71
72
73
74
75
76
77







+

void sqlitepager_dont_rollback(void*);
void sqlitepager_dont_write(Pager*, Pgno);
int *sqlitepager_stats(Pager*);

#ifdef SQLITE_TEST
void sqlitepager_refdump(Pager*);
int pager_refinfo_enable;
int pager_old_format;
#endif
Changes to src/test2.c.
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23







-
+







**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the pager.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test2.c,v 1.8 2002/05/10 05:44:56 drh Exp $
** $Id: test2.c,v 1.9 2002/08/12 12:29:57 drh Exp $
*/
#include "sqliteInt.h"
#include "pager.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

489
490
491
492
493
494
495




496
497
489
490
491
492
493
494
495
496
497
498
499
500
501







+
+
+
+


  Tcl_CreateCommand(interp, "page_lookup", page_lookup, 0, 0);
  Tcl_CreateCommand(interp, "page_unref", page_unref, 0, 0);
  Tcl_CreateCommand(interp, "page_read", page_read, 0, 0);
  Tcl_CreateCommand(interp, "page_write", page_write, 0, 0);
  Tcl_CreateCommand(interp, "page_number", page_number, 0, 0);
  Tcl_LinkVar(interp, "sqlite_io_error_pending",
     (char*)&sqlite_io_error_pending, TCL_LINK_INT);
#ifdef SQLITE_TEST
  Tcl_LinkVar(interp, "pager_old_format",
     (char*)&pager_old_format, TCL_LINK_INT);
#endif
  return TCL_OK;
}
Changes to test/trans.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21













-
+







# 2001 September 15
#
# 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is database locks.
#
# $Id: trans.test,v 1.13 2002/07/07 16:52:47 drh Exp $
# $Id: trans.test,v 1.14 2002/08/12 12:29:57 drh Exp $


set testdir [file dirname $argv0]
source $testdir/tester.tcl


# Create several tables to work with.
853
854
855
856
857
858
859

860
861
862
863
864
865
866
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867







+







}

# Do rollbacks.  Make sure the signature does not change.
#
for {set i 2} {$i<=$limit} {incr i} {
  set ::sig [signature]
  set cnt [lindex $::sig 0]
  set ::pager_old_format [expr {($i%4)==0}]
  do_test trans-9.$i.1-$cnt {
     execsql {
       BEGIN;
       DELETE FROM t3 WHERE random()%10!=0;
       INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
       INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
       ROLLBACK;
Changes to www/index.tcl.
1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







#
# Run this TCL script to generate HTML for the index.html file.
#
set rcsid {$Id: index.tcl,v 1.61 2002/08/11 20:10:49 drh Exp $}
set rcsid {$Id: index.tcl,v 1.62 2002/08/12 12:29:58 drh Exp $}

puts {<html>
<head><title>SQLite: An SQL Database Engine In A C Library</title></head>
<body bgcolor=white>
<h1 align=center>SQLite: An SQL Database Engine In A C Library</h1>
<p align=center>}
puts "This page was last modified on [lrange $rcsid 3 4] UTC<br>"
209
210
211
212
213
214
215
216
217
218
219
220
221
222
209
210
211
212
213
214
215






216







-
-
-
-
-
-


<li><p>A PHP module for SQLite can be found at
       <a href="http://freshmeat.net/projects/sqlite-php">
       http://freshmeat.net/projects/sqlite-php</a></li>
</ul>}

puts {
<p><hr /></p>
<p>
<a href="../index.html"><img src="/goback.jpg" border=0 />
More Open Source Software</a> from Hwaci.
</p>

</body></html>}