/ Check-in [717523d3]
Login

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

Overview
Comment:Improvements to the pager to help large updates against a large database run faster. Also improved the testing of the pager rollback algorithms. (CVS 835)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 717523d3750dce784fa767ed9a8267d1246798ef
User & Date: drh 2003-01-16 13:42:43
Context
2003-01-16
16:28
Add the sqlite_trace() hook for tracing the SQL that an application executes. The plan is to leave this API undocumented for the time being, in case we want to make changes to it later. (CVS 836) check-in: f67bff8f user: drh tags: trunk
13:42
Improvements to the pager to help large updates against a large database run faster. Also improved the testing of the pager rollback algorithms. (CVS 835) check-in: 717523d3 user: drh tags: trunk
2003-01-14
13:48
Finish out the test suite for the new sqlite_set_authorizer API. (CVS 834) check-in: 701a7391 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os.c.

1425
1426
1427
1428
1429
1430
1431
1432
    char zBuf[_MAX_PATH+1];
      sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, 0);
    }
  }
  return zFull;
#endif
}








<
1425
1426
1427
1428
1429
1430
1431

    char zBuf[_MAX_PATH+1];
      sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, 0);
    }
  }
  return zFull;
#endif
}

Changes to src/pager.c.

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



















28
29
30
31
32
33
34
..
74
75
76
77
78
79
80

81
82
83
84
85
86
87
...
110
111
112
113
114
115
116



117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
...
356
357
358
359
360
361
362

363
364
365
366
367
368
369
...
394
395
396
397
398
399
400





401
402
403
404

405
406
407
408
409
410
411
412
413
414
...
478
479
480
481
482
483
484
485

486
























487
488
489
490
491
492
493
...
655
656
657
658
659
660
661

662
663
664
665
666
667
668
...
757
758
759
760
761
762
763

764
765
766
767
768
769
770
...
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837



838
839





840
841
842
843
844
845
846
847
848
849
850
851
852

853
854
855
856
857
858
859
860
861
862
863
864
865
866
...
935
936
937
938
939
940
941

942
943
944
945
946
947
948
...
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008















1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
....
1047
1048
1049
1050
1051
1052
1053

1054

1055
1056

1057
1058
1059
1060
1061
1062
1063
....
1201
1202
1203
1204
1205
1206
1207

1208
1209
1210
1211
1212
1213
1214
....
1223
1224
1225
1226
1227
1228
1229



1230
1231
1232
1233
1234
1235
1236
....
1260
1261
1262
1263
1264
1265
1266

1267
1268
1269
1270
1271
1272
1273
....
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355








1356
1357
1358
1359
1360
1361
1362
....
1430
1431
1432
1433
1434
1435
1436

1437
1438
1439
1440
1441
1442
1443
....
1455
1456
1457
1458
1459
1460
1461

1462
1463
1464
1465
1466
1467
1468
....
1474
1475
1476
1477
1478
1479
1480

1481
1482
1483
1484
1485
1486
1487
....
1489
1490
1491
1492
1493
1494
1495

1496
1497
1498
1499
1500
1501
1502
1503


1504
1505
1506

1507
1508

1509
1510
1511
1512

1513
1514
1515
1516
1517
1518
1519
1520
1521
....
1538
1539
1540
1541
1542
1543
1544

1545
1546
1547
1548
1549
















1550
1551
1552
1553
1554
1555
1556
** 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.67 2003/01/12 18:02:18 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>




















/*
** The page cache as a whole is always in one of the following
** states:
**
**   SQLITE_UNLOCK       The page cache is not currently reading or 
**                       writing the database file.  There is no
................................................................................
  int nRef;                      /* Number of users of this page */
  PgHdr *pNextFree, *pPrevFree;  /* Freelist of pages where nRef==0 */
  PgHdr *pNextAll, *pPrevAll;    /* A list of all pages */
  PgHdr *pNextCkpt, *pPrevCkpt;  /* List of pages in the checkpoint journal */
  u8 inJournal;                  /* TRUE if has been written to journal */
  u8 inCkpt;                     /* TRUE if written to the checkpoint journal */
  u8 dirty;                      /* TRUE if we need to write back changes */

  u8 alwaysRollback;             /* Disable dont_rollback() for this page */
  /* SQLITE_PAGE_SIZE bytes of page data follow this header */
  /* Pager.nExtra bytes of local data follow the page data */
};

/*
** Convert a pointer to a PgHdr into a pointer to its data
................................................................................
  char *zJournal;             /* Name of the journal file */
  OsFile fd, jfd;             /* File descriptors for database and journal */
  OsFile cpfd;                /* File descriptor for the checkpoint journal */
  int dbSize;                 /* Number of pages in the file */
  int origDbSize;             /* dbSize before the current change */
  int ckptSize;               /* Size of database (in pages) at ckpt_begin() */
  off_t ckptJSize;            /* Size of journal at ckpt_begin() */



  int ckptNRec;               /* Number of records in the checkpoint journal */
  int nExtra;                 /* Add this many bytes to each in-memory page */
  void (*xDestructor)(void*); /* Call this routine when freeing pages */
  int nPage;                  /* Total number of in-memory pages */
  int nRef;                   /* Number of in-memory pages with PgHdr.nRef>0 */
  int mxPage;                 /* Maximum number of pages to hold in cache */
  int nHit, nMiss, nOvfl;     /* Cache hits, missing, and LRU overflows */
  u8 journalOpen;             /* True if journal file descriptors is valid */

  u8 useJournal;              /* Do not use a rollback journal on this file */
  u8 ckptOpen;                /* True if the checkpoint journal is open */
  u8 ckptInUse;               /* True we are in a checkpoint */
  u8 ckptAutoopen;            /* Open ckpt journal when main journal is opened*/
  u8 noSync;                  /* Do not sync the journal if true */
  u8 state;                   /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
  u8 errMask;                 /* One of several kinds of errors */
................................................................................
    pPager->journalOpen = 0;
    sqliteOsDelete(pPager->zJournal);
    sqliteFree( pPager->aInJournal );
    pPager->aInJournal = 0;
    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      pPg->inJournal = 0;
      pPg->dirty = 0;

    }
  }else{
    assert( pPager->dirtyFile==0 || pPager->useJournal==0 );
  }
  rc = sqliteOsReadLock(&pPager->fd);
  if( rc==SQLITE_OK ){
    pPager->state = SQLITE_READLOCK;
................................................................................
  /* 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.
  */
  pPg = pager_lookup(pPager, pgRec.pgno);





  if( pPg ){
    memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE);
    memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
  }

  rc = sqliteOsSeek(&pPager->fd, (pgRec.pgno-1)*(off_t)SQLITE_PAGE_SIZE);
  if( rc==SQLITE_OK ){
    rc = sqliteOsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
  }
  return rc;
}

/*
** Playback the journal and thus restore the database file to
** the state it was in before we started making changes.  
................................................................................
  
  /* Copy original pages out of the journal and back into the database file.
  */
  for(i=nRec-1; i>=0; i--){
    rc = pager_playback_one_page(pPager, &pPager->jfd);
    if( rc!=SQLITE_OK ) break;
  }


end_playback:
























  if( rc!=SQLITE_OK ){
    pager_unwritelock(pPager);
    pPager->errMask |= PAGER_ERR_CORRUPT;
    rc = SQLITE_CORRUPT;
  }else{
    rc = pager_unwritelock(pPager);
  }
................................................................................
  nameLen = strlen(zFullPathname);
  pPager = sqliteMalloc( sizeof(*pPager) + nameLen*2 + 30 );
  if( pPager==0 ){
    sqliteOsClose(&fd);
    sqliteFree(zFullPathname);
    return SQLITE_NOMEM;
  }

  pPager->zFilename = (char*)&pPager[1];
  pPager->zJournal = &pPager->zFilename[nameLen+1];
  strcpy(pPager->zFilename, zFullPathname);
  strcpy(pPager->zJournal, zFullPathname);
  sqliteFree(zFullPathname);
  strcpy(&pPager->zJournal[nameLen], "-journal");
  pPager->fd = fd;
................................................................................
  sqliteOsClose(&pPager->fd);
  assert( pPager->journalOpen==0 );
  /* Temp files are automatically deleted by the OS
  ** if( pPager->tempFile ){
  **   sqliteOsDelete(pPager->zFilename);
  ** }
  */

  sqliteFree(pPager);
  return SQLITE_OK;
}

/*
** Return the page number for the given page data.
*/
................................................................................
**
** If we are writing to temporary database, there is no need to preserve
** the integrity of the journal file, so we can save time and skip the
** fsync().
*/
static int syncAllPages(Pager *pPager){
  PgHdr *pPg;
  Pgno lastPgno = 0;
  int rc = SQLITE_OK;

  /* Sync the journal before modifying the main database
  ** (assuming there is a journal and it needs to be synced.)
  */
  if( pPager->needSync ){
    if( !pPager->tempFile ){



      rc = sqliteOsSync(&pPager->jfd);
      if( rc!=0 ) return rc;





    }
    pPager->needSync = 0;
  }

  /* Write all dirty free pages to the disk in the order that they
  ** appear on the disk.  We have experimented with sorting the pages
  ** by page numbers so that they are written in order, but that does
  ** not appear to improve performance.
  */
  for(pPg=pPager->pFirst; pPg; pPg=pPg->pNextFree){
    if( pPg->dirty ){
      if( lastPgno==0 || pPg->pgno!=lastPgno+1 ){
        sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*(off_t)SQLITE_PAGE_SIZE);

      }
      rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
      if( rc!=SQLITE_OK ) break;
      pPg->dirty = 0;
      lastPgno = pPg->pgno;
    }
  }
  return rc;
}

/*
** Acquire a page.
**
** A read lock on the disk file is obtained when the first page is acquired. 
................................................................................
       if( rc!=SQLITE_OK ){
         rc = sqliteOsUnlock(&pPager->fd);
         assert( rc==SQLITE_OK );
         *ppPage = 0;
         return SQLITE_BUSY;
       }
       pPager->journalOpen = 1;


       /* Playback and delete the journal.  Drop the database write
       ** lock and reacquire the read lock.
       */
       rc = pager_playback(pPager);
       if( rc!=SQLITE_OK ){
         return rc;
................................................................................
      if( pPager->pAll ){
        pPager->pAll->pPrevAll = pPg;
      }
      pPg->pPrevAll = 0;
      pPager->pAll = pPg;
      pPager->nPage++;
    }else{
      /* Recycle an older page.  First locate the page to be recycled.
      ** Try to find one that is not dirty and is near the head of
      ** of the free list */
      pPg = pPager->pFirst;
      while( pPg && pPg->dirty ){
        pPg = pPg->pNextFree;
      }

      /* If we could not find a page that has not been used recently
      ** and which is not dirty, then sync the journal and write all
      ** 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 why we write all dirty pages after a
      ** sync.
      */
      if( pPg==0 ){
        int rc = syncAllPages(pPager);
        if( rc!=0 ){
          sqlitepager_rollback(pPager);
          *ppPage = 0;
          return SQLITE_IOERR;
        }
        pPg = pPager->pFirst;
      }
      assert( pPg->nRef==0 );















      assert( pPg->dirty==0 );

      /* If the page we are recyclying is marked as alwaysRollback, then
      ** set the global alwaysRollback flag, thus disabling the
      ** sqlite_dont_rollback() optimization for the rest of this transaction.
      ** It is necessary to do this because the page marked alwaysRollback
      ** might be reloaded at a later time but at that point we won't remember
      ** that is was marked alwaysRollback.  This means that all pages must
      ** be marked as alwaysRollback from here on out.
      */
................................................................................
      }
      pPg->pNextHash = pPg->pPrevHash = 0;
      pPager->nOvfl++;
    }
    pPg->pgno = pgno;
    if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
      sqliteCheckMemory(pPager->aInJournal, pgno/8);

      pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;

    }else{
      pPg->inJournal = 0;

    }
    if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize
             && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){
      page_add_to_ckpt_list(pPg);
    }else{
      page_remove_from_ckpt_list(pPg);
    }
................................................................................
    sqliteFree(pPager->aInJournal);
    pPager->aInJournal = 0;
    sqliteOsReadLock(&pPager->fd);
    pPager->state = SQLITE_READLOCK;
    return SQLITE_CANTOPEN;
  }
  pPager->journalOpen = 1;

  pPager->needSync = 0;
  pPager->alwaysRollback = 0;
  sqlitepager_pagecount(pPager);
  pPager->origDbSize = pPager->dbSize;
  if( pager_old_format ){
    rc = sqliteOsWrite(&pPager->jfd, aOldJournalMagic,
                       sizeof(aOldJournalMagic));
................................................................................
  }
  if( rc!=SQLITE_OK ){
    rc = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){
      rc = SQLITE_FULL;
    }
  }



  return rc;  
}

/*
** Acquire a write-lock on the database.  The lock is removed when
** the any of the following happen:
**
................................................................................
    assert( pPager->aInJournal==0 );
    rc = sqliteOsWriteLock(&pPager->fd);
    if( rc!=SQLITE_OK ){
      return rc;
    }
    pPager->state = SQLITE_WRITELOCK;
    pPager->dirtyFile = 0;

    if( pPager->useJournal && !pPager->tempFile ){
      rc = pager_open_journal(pPager);
    }
  }
  return rc;
}

................................................................................
  assert( pPager->journalOpen || !pPager->useJournal );
  pPager->dirtyFile = 1;

  /* 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 && pPager->useJournal 
         && (int)pPg->pgno <= pPager->origDbSize ){
    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;
    }
    assert( pPager->aInJournal!=0 );
    pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
    pPager->needSync = !pPager->noSync;
    pPg->inJournal = 1;
    if( pPager->ckptInUse ){
      pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
      page_add_to_ckpt_list(pPg);








    }
  }

  /* 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 ){
................................................................................
      ** When the database file grows, we must make sure that the last page
      ** gets written at least once so that the disk file will be the correct
      ** size. If you do not write this page and the size of the file
      ** on the disk ends up being too small, that can lead to database
      ** corruption during the next transaction.
      */
    }else{

      pPg->dirty = 0;
    }
  }
}

/*
** A call to this routine tells the pager that if a rollback occurs,
................................................................................
    assert( pPager->aInJournal!=0 );
    pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
    pPg->inJournal = 1;
    if( pPager->ckptInUse ){
      pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
      page_add_to_ckpt_list(pPg);
    }

  }
  if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
    assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
    assert( pPager->aInCkpt!=0 );
    pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
    page_add_to_ckpt_list(pPg);
  }
................................................................................
** If the commit fails for any reason, a rollback attempt is made
** and an error code is returned.  If the commit worked, SQLITE_OK
** is returned.
*/
int sqlitepager_commit(Pager *pPager){
  int rc;
  PgHdr *pPg;


  if( pPager->errMask==PAGER_ERR_FULL ){
    rc = sqlitepager_rollback(pPager);
    if( rc==SQLITE_OK ){
      rc = SQLITE_FULL;
    }
    return rc;
................................................................................
  if( pPager->errMask!=0 ){
    rc = pager_errcode(pPager);
    return rc;
  }
  if( pPager->state!=SQLITE_WRITELOCK ){
    return SQLITE_ERROR;
  }

  if( pPager->dirtyFile==0 ){
    /* Exit early (without doing the time-consuming sqliteOsSync() calls)
    ** if there have been no changes to the database file. */
    rc = pager_unwritelock(pPager);
    pPager->dbSize = -1;
    return rc;
  }
  assert( pPager->journalOpen );


  if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){
    goto commit_abort;
  }

  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
    if( pPg->dirty==0 ) continue;

    rc = sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*(off_t)SQLITE_PAGE_SIZE);
    if( rc!=SQLITE_OK ) goto commit_abort;
    rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
    if( rc!=SQLITE_OK ) goto commit_abort;

  }
  if( !pPager->noSync && sqliteOsSync(&pPager->fd)!=SQLITE_OK ){
    goto commit_abort;
  }
  rc = pager_unwritelock(pPager);
  pPager->dbSize = -1;
  return rc;

  /* Jump here if anything goes wrong during the commit process.
................................................................................
** process is writing trash into the journal file (SQLITE_CORRUPT) or
** unless a prior malloc() failed (SQLITE_NOMEM).  Appropriate error
** codes are returned for all these occasions.  Otherwise,
** SQLITE_OK is returned.
*/
int sqlitepager_rollback(Pager *pPager){
  int rc;

  if( !pPager->dirtyFile || !pPager->journalOpen ){
    rc = pager_unwritelock(pPager);
    pPager->dbSize = -1;
    return rc;
  }
















  if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
    if( pPager->state>=SQLITE_WRITELOCK ){
      pager_playback(pPager);
    }
    return pager_errcode(pPager);
  }
  if( pPager->state!=SQLITE_WRITELOCK ){







|






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







 







>







 







>
>
>








>







 







>







 







>
>
>
>
>



<
>
|
<
<







 








>

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







 







>







 







>







 







<







>
>
>


>
>
>
>
>




|
<
<
<

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







 







>







 







|
|
|

|



|
|
|
|
<
<
<
<
<
<
<











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


|







 







>

>


>







 







>







 







>
>
>







 







>







 







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







 







>







 







>







 







>







 







>








>
>



>


>
|
<


>

|







 







>





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







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
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
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
...
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

434
435


436
437
438
439
440
441
442
...
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
...
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
...
878
879
880
881
882
883
884

885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906



907
908



909
910




911

912
913
914
915
916
917
918
...
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
....
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043







1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
....
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
....
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
....
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
....
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
....
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
....
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
....
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
....
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
....
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594

1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
....
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
** 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.68 2003/01/16 13:42:43 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

/*
** Macros for troubleshooting.  Normally turned off
*/
#if 0
static Pager *mainPager = 0;
#define SET_PAGER(X)  if( mainPager==0 ) mainPager = (X)
#define CLR_PAGER(X)  if( mainPager==(X) ) mainPager = 0
#define TRACE1(X)     if( pPager==mainPager ) fprintf(stderr,X)
#define TRACE2(X,Y)   if( pPager==mainPager ) fprintf(stderr,X,Y)
#define TRACE3(X,Y,Z) if( pPager==mainPager ) fprintf(stderr,X,Y,Z)
#else
#define SET_PAGER(X)
#define CLR_PAGER(X)
#define TRACE1(X)
#define TRACE2(X,Y)
#define TRACE3(X,Y,Z)
#endif


/*
** The page cache as a whole is always in one of the following
** states:
**
**   SQLITE_UNLOCK       The page cache is not currently reading or 
**                       writing the database file.  There is no
................................................................................
  int nRef;                      /* Number of users of this page */
  PgHdr *pNextFree, *pPrevFree;  /* Freelist of pages where nRef==0 */
  PgHdr *pNextAll, *pPrevAll;    /* A list of all pages */
  PgHdr *pNextCkpt, *pPrevCkpt;  /* List of pages in the checkpoint journal */
  u8 inJournal;                  /* TRUE if has been written to journal */
  u8 inCkpt;                     /* TRUE if written to the checkpoint journal */
  u8 dirty;                      /* TRUE if we need to write back changes */
  u8 needSync;                   /* Sync journal before writing this page */
  u8 alwaysRollback;             /* Disable dont_rollback() for this page */
  /* SQLITE_PAGE_SIZE bytes of page data follow this header */
  /* Pager.nExtra bytes of local data follow the page data */
};

/*
** Convert a pointer to a PgHdr into a pointer to its data
................................................................................
  char *zJournal;             /* Name of the journal file */
  OsFile fd, jfd;             /* File descriptors for database and journal */
  OsFile cpfd;                /* File descriptor for the checkpoint journal */
  int dbSize;                 /* Number of pages in the file */
  int origDbSize;             /* dbSize before the current change */
  int ckptSize;               /* Size of database (in pages) at ckpt_begin() */
  off_t ckptJSize;            /* Size of journal at ckpt_begin() */
#ifndef NDEBUG
  off_t syncJSize;            /* Size of journal at last fsync() call */
#endif
  int ckptNRec;               /* Number of records in the checkpoint journal */
  int nExtra;                 /* Add this many bytes to each in-memory page */
  void (*xDestructor)(void*); /* Call this routine when freeing pages */
  int nPage;                  /* Total number of in-memory pages */
  int nRef;                   /* Number of in-memory pages with PgHdr.nRef>0 */
  int mxPage;                 /* Maximum number of pages to hold in cache */
  int nHit, nMiss, nOvfl;     /* Cache hits, missing, and LRU overflows */
  u8 journalOpen;             /* True if journal file descriptors is valid */
  u8 journalStarted;          /* True if initial magic of journal is synced */
  u8 useJournal;              /* Do not use a rollback journal on this file */
  u8 ckptOpen;                /* True if the checkpoint journal is open */
  u8 ckptInUse;               /* True we are in a checkpoint */
  u8 ckptAutoopen;            /* Open ckpt journal when main journal is opened*/
  u8 noSync;                  /* Do not sync the journal if true */
  u8 state;                   /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
  u8 errMask;                 /* One of several kinds of errors */
................................................................................
    pPager->journalOpen = 0;
    sqliteOsDelete(pPager->zJournal);
    sqliteFree( pPager->aInJournal );
    pPager->aInJournal = 0;
    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      pPg->inJournal = 0;
      pPg->dirty = 0;
      pPg->needSync = 0;
    }
  }else{
    assert( pPager->dirtyFile==0 || pPager->useJournal==0 );
  }
  rc = sqliteOsReadLock(&pPager->fd);
  if( rc==SQLITE_OK ){
    pPager->state = SQLITE_READLOCK;
................................................................................
  /* 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.
  */
  pPg = pager_lookup(pPager, pgRec.pgno);
  if( pPg==0 || pPg->needSync==0 ){
    TRACE2("PLAYBACK %d\n", pgRec.pgno);
    sqliteOsSeek(&pPager->fd, (pgRec.pgno-1)*(off_t)SQLITE_PAGE_SIZE);
    rc = sqliteOsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
  }
  if( pPg ){
    memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE);
    memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);

    pPg->dirty = 0;
    pPg->needSync = 0;


  }
  return rc;
}

/*
** Playback the journal and thus restore the database file to
** the state it was in before we started making changes.  
................................................................................
  
  /* Copy original pages out of the journal and back into the database file.
  */
  for(i=nRec-1; i>=0; i--){
    rc = pager_playback_one_page(pPager, &pPager->jfd);
    if( rc!=SQLITE_OK ) break;
  }


end_playback:
#if !defined(NDEBUG) && defined(SQLITE_TEST)
  /* For pages that were never written into the journal, restore the
  ** memory copy from the original database file.
  **
  ** This is code is used during testing only.  It is necessary to
  ** compensate for the sqliteOsTruncate() call inside 
  ** sqlitepager_rollback().
  */
  if( rc==SQLITE_OK ){
    PgHdr *pPg;
    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      if( (int)pPg->pgno <= pPager->origDbSize ){
        sqliteOsSeek(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)(pPg->pgno-1));
        rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
        if( rc ) break;
      }else{
        memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
      }
      memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
      pPg->needSync = 0;
      pPg->dirty = 0;
    }
  }
#endif
  if( rc!=SQLITE_OK ){
    pager_unwritelock(pPager);
    pPager->errMask |= PAGER_ERR_CORRUPT;
    rc = SQLITE_CORRUPT;
  }else{
    rc = pager_unwritelock(pPager);
  }
................................................................................
  nameLen = strlen(zFullPathname);
  pPager = sqliteMalloc( sizeof(*pPager) + nameLen*2 + 30 );
  if( pPager==0 ){
    sqliteOsClose(&fd);
    sqliteFree(zFullPathname);
    return SQLITE_NOMEM;
  }
  SET_PAGER(pPager);
  pPager->zFilename = (char*)&pPager[1];
  pPager->zJournal = &pPager->zFilename[nameLen+1];
  strcpy(pPager->zFilename, zFullPathname);
  strcpy(pPager->zJournal, zFullPathname);
  sqliteFree(zFullPathname);
  strcpy(&pPager->zJournal[nameLen], "-journal");
  pPager->fd = fd;
................................................................................
  sqliteOsClose(&pPager->fd);
  assert( pPager->journalOpen==0 );
  /* Temp files are automatically deleted by the OS
  ** if( pPager->tempFile ){
  **   sqliteOsDelete(pPager->zFilename);
  ** }
  */
  CLR_PAGER(pPager);
  sqliteFree(pPager);
  return SQLITE_OK;
}

/*
** Return the page number for the given page data.
*/
................................................................................
**
** If we are writing to temporary database, there is no need to preserve
** the integrity of the journal file, so we can save time and skip the
** fsync().
*/
static int syncAllPages(Pager *pPager){
  PgHdr *pPg;

  int rc = SQLITE_OK;

  /* Sync the journal before modifying the main database
  ** (assuming there is a journal and it needs to be synced.)
  */
  if( pPager->needSync ){
    if( !pPager->tempFile ){
      assert( pPager->journalOpen );
      assert( !pPager->noSync );
      TRACE1("SYNC\n");
      rc = sqliteOsSync(&pPager->jfd);
      if( rc!=0 ) return rc;
#ifndef NDEBUG
      rc = sqliteOsFileSize(&pPager->jfd, &pPager->syncJSize);
      if( rc!=0 ) return rc;
#endif
      pPager->journalStarted = 1;
    }
    pPager->needSync = 0;
  }

  /* Erase the needSync flag from every page.



  */
  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){



    pPg->needSync = 0;
  }






  return rc;
}

/*
** Acquire a page.
**
** A read lock on the disk file is obtained when the first page is acquired. 
................................................................................
       if( rc!=SQLITE_OK ){
         rc = sqliteOsUnlock(&pPager->fd);
         assert( rc==SQLITE_OK );
         *ppPage = 0;
         return SQLITE_BUSY;
       }
       pPager->journalOpen = 1;
       pPager->journalStarted = 0;

       /* Playback and delete the journal.  Drop the database write
       ** lock and reacquire the read lock.
       */
       rc = pager_playback(pPager);
       if( rc!=SQLITE_OK ){
         return rc;
................................................................................
      if( pPager->pAll ){
        pPager->pAll->pPrevAll = pPg;
      }
      pPg->pPrevAll = 0;
      pPager->pAll = pPg;
      pPager->nPage++;
    }else{
      /* Find a page to recycle.  Try to locate a page that does not
      ** require us to do an fsync() on the journal.
      */
      pPg = pPager->pFirst;
      while( pPg && pPg->needSync ){
        pPg = pPg->pNextFree;
      }

      /* If we could not find a page that does not require an fsync()
      ** on the journal file then fsync the journal file.  This is a
      ** very slow operation, so we work hard to avoid it.  But sometimes
      ** it can't be helped.







      */
      if( pPg==0 ){
        int rc = syncAllPages(pPager);
        if( rc!=0 ){
          sqlitepager_rollback(pPager);
          *ppPage = 0;
          return SQLITE_IOERR;
        }
        pPg = pPager->pFirst;
      }
      assert( pPg->nRef==0 );

      /* Write the page to the database file if it is dirty.
      */
      if( pPg->dirty ){
        assert( pPg->needSync==0 );
        TRACE2("SAVE %d\n", pPg->pgno);
        sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*(off_t)SQLITE_PAGE_SIZE);
        rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
        if( rc!=SQLITE_OK ){
          sqlitepager_rollback(pPager);
          *ppPage = 0;
          return SQLITE_IOERR;
        }
        pPg->dirty = 0;
      }
      assert( pPg->dirty==0 );

      /* If the page we are recycling is marked as alwaysRollback, then
      ** set the global alwaysRollback flag, thus disabling the
      ** sqlite_dont_rollback() optimization for the rest of this transaction.
      ** It is necessary to do this because the page marked alwaysRollback
      ** might be reloaded at a later time but at that point we won't remember
      ** that is was marked alwaysRollback.  This means that all pages must
      ** be marked as alwaysRollback from here on out.
      */
................................................................................
      }
      pPg->pNextHash = pPg->pPrevHash = 0;
      pPager->nOvfl++;
    }
    pPg->pgno = pgno;
    if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
      sqliteCheckMemory(pPager->aInJournal, pgno/8);
      assert( pPager->journalOpen );
      pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
      pPg->needSync = 0;
    }else{
      pPg->inJournal = 0;
      pPg->needSync = 0;
    }
    if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize
             && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){
      page_add_to_ckpt_list(pPg);
    }else{
      page_remove_from_ckpt_list(pPg);
    }
................................................................................
    sqliteFree(pPager->aInJournal);
    pPager->aInJournal = 0;
    sqliteOsReadLock(&pPager->fd);
    pPager->state = SQLITE_READLOCK;
    return SQLITE_CANTOPEN;
  }
  pPager->journalOpen = 1;
  pPager->journalStarted = 0;
  pPager->needSync = 0;
  pPager->alwaysRollback = 0;
  sqlitepager_pagecount(pPager);
  pPager->origDbSize = pPager->dbSize;
  if( pager_old_format ){
    rc = sqliteOsWrite(&pPager->jfd, aOldJournalMagic,
                       sizeof(aOldJournalMagic));
................................................................................
  }
  if( rc!=SQLITE_OK ){
    rc = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){
      rc = SQLITE_FULL;
    }
  }
#ifndef NDEBUG
  pPager->syncJSize = 0;
#endif
  return rc;  
}

/*
** Acquire a write-lock on the database.  The lock is removed when
** the any of the following happen:
**
................................................................................
    assert( pPager->aInJournal==0 );
    rc = sqliteOsWriteLock(&pPager->fd);
    if( rc!=SQLITE_OK ){
      return rc;
    }
    pPager->state = SQLITE_WRITELOCK;
    pPager->dirtyFile = 0;
    TRACE1("TRANSACTION\n");
    if( pPager->useJournal && !pPager->tempFile ){
      rc = pager_open_journal(pPager);
    }
  }
  return rc;
}

................................................................................
  assert( pPager->journalOpen || !pPager->useJournal );
  pPager->dirtyFile = 1;

  /* 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 && pPager->useJournal ){
    if( (int)pPg->pgno <= pPager->origDbSize ){
      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;
      }
      assert( pPager->aInJournal!=0 );
      pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
      pPg->needSync = !pPager->noSync;
      pPg->inJournal = 1;
      if( pPager->ckptInUse ){
        pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
        page_add_to_ckpt_list(pPg);
      }
      TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync);
    }else{
      pPg->needSync = !pPager->journalStarted && !pPager->noSync;
      TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync);
    }
    if( pPg->needSync ){
      pPager->needSync = 1;
    }
  }

  /* 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 ){
................................................................................
      ** When the database file grows, we must make sure that the last page
      ** gets written at least once so that the disk file will be the correct
      ** size. If you do not write this page and the size of the file
      ** on the disk ends up being too small, that can lead to database
      ** corruption during the next transaction.
      */
    }else{
      TRACE2("DONT_WRITE %d\n", pgno);
      pPg->dirty = 0;
    }
  }
}

/*
** A call to this routine tells the pager that if a rollback occurs,
................................................................................
    assert( pPager->aInJournal!=0 );
    pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
    pPg->inJournal = 1;
    if( pPager->ckptInUse ){
      pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
      page_add_to_ckpt_list(pPg);
    }
    TRACE2("DONT_ROLLBACK %d\n", pPg->pgno);
  }
  if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
    assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
    assert( pPager->aInCkpt!=0 );
    pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
    page_add_to_ckpt_list(pPg);
  }
................................................................................
** If the commit fails for any reason, a rollback attempt is made
** and an error code is returned.  If the commit worked, SQLITE_OK
** is returned.
*/
int sqlitepager_commit(Pager *pPager){
  int rc;
  PgHdr *pPg;
  int dbChanged;

  if( pPager->errMask==PAGER_ERR_FULL ){
    rc = sqlitepager_rollback(pPager);
    if( rc==SQLITE_OK ){
      rc = SQLITE_FULL;
    }
    return rc;
................................................................................
  if( pPager->errMask!=0 ){
    rc = pager_errcode(pPager);
    return rc;
  }
  if( pPager->state!=SQLITE_WRITELOCK ){
    return SQLITE_ERROR;
  }
  TRACE1("COMMIT\n");
  if( pPager->dirtyFile==0 ){
    /* Exit early (without doing the time-consuming sqliteOsSync() calls)
    ** if there have been no changes to the database file. */
    rc = pager_unwritelock(pPager);
    pPager->dbSize = -1;
    return rc;
  }
  assert( pPager->journalOpen );
  if( !pPager->journalStarted && !pPager->noSync ) pPager->needSync = 1;
  assert( pPager->dirtyFile || !pPager->needSync );
  if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){
    goto commit_abort;
  }
  dbChanged = 0;
  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
    if( pPg->dirty==0 ) continue;
    TRACE2("COMMIT-PAGE %d\n", pPg->pgno);
    sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*(off_t)SQLITE_PAGE_SIZE);

    rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
    if( rc!=SQLITE_OK ) goto commit_abort;
    dbChanged = 1;
  }
  if( dbChanged && !pPager->noSync && sqliteOsSync(&pPager->fd)!=SQLITE_OK ){
    goto commit_abort;
  }
  rc = pager_unwritelock(pPager);
  pPager->dbSize = -1;
  return rc;

  /* Jump here if anything goes wrong during the commit process.
................................................................................
** process is writing trash into the journal file (SQLITE_CORRUPT) or
** unless a prior malloc() failed (SQLITE_NOMEM).  Appropriate error
** codes are returned for all these occasions.  Otherwise,
** SQLITE_OK is returned.
*/
int sqlitepager_rollback(Pager *pPager){
  int rc;
  TRACE1("ROLLBACK\n");
  if( !pPager->dirtyFile || !pPager->journalOpen ){
    rc = pager_unwritelock(pPager);
    pPager->dbSize = -1;
    return rc;
  }

#if defined(SQLITE_TEST) && !defined(NDEBUG)
  /* Truncate the journal to the size it was at the conclusion of the
  ** last sqliteOsSync() call.  This is really an error check.  If the
  ** rollback still works, it means that the rollback would have also
  ** worked if it had occurred after an OS crash or unexpected power
  ** loss.
  */
  if( pPager->syncJSize<sizeof(aJournalMagic)+sizeof(Pgno) ){
    pPager->syncJSize = sizeof(aJournalMagic)+sizeof(Pgno);
  }
  TRACE2("TRUNCATE JOURNAL %lld\n", pPager->syncJSize);
  rc =  sqliteOsTruncate(&pPager->jfd, pPager->syncJSize);
  if( rc ) return rc;
#endif

  if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
    if( pPager->state>=SQLITE_WRITELOCK ){
      pager_playback(pPager);
    }
    return pager_errcode(pPager);
  }
  if( pPager->state!=SQLITE_WRITELOCK ){

Changes to src/vdbe.c.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
...
924
925
926
927
928
929
930
931

932
933
934
935
936
937
938
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.197 2003/01/12 17:35:00 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
/*
** Pop the stack N times.  Free any memory associated with the
** popped stack elements.
*/
static void PopStack(Vdbe *p, int N){
  assert( N>=0 );
  if( p->zStack==0 ) return;
  assert( p->aStack );

  while( N-- > 0 ){
    if( p->aStack[p->tos].flags & STK_Dyn ){
      sqliteFree(p->zStack[p->tos]);
    }
    p->aStack[p->tos].flags = 0;
    p->zStack[p->tos] = 0;
    p->tos--;







|







 







|
>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
...
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.198 2003/01/16 13:42:43 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
/*
** Pop the stack N times.  Free any memory associated with the
** popped stack elements.
*/
static void PopStack(Vdbe *p, int N){
  assert( N>=0 );
  if( p->zStack==0 ) return;
  assert( p->aStack || sqlite_malloc_failed );
  if( p->aStack==0 ) return;
  while( N-- > 0 ){
    if( p->aStack[p->tos].flags & STK_Dyn ){
      sqliteFree(p->zStack[p->tos]);
    }
    p->aStack[p->tos].flags = 0;
    p->zStack[p->tos] = 0;
    p->tos--;

Changes to test/trigger3.test.

39
40
41
42
43
44
45



46
47
48
49
50
51
52

do_test trigger3-1.2 {
    execsql {
	SELECT * FROM tbl;
	ROLLBACK;
    }
} {5 5 6}




# FAIL
do_test trigger3-2.1 {
    catchsql {
	BEGIN;
        INSERT INTO tbl VALUES (5, 5, 6);
        INSERT INTO tbl VALUES (2, 5, 6);







>
>
>







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

do_test trigger3-1.2 {
    execsql {
	SELECT * FROM tbl;
	ROLLBACK;
    }
} {5 5 6}
do_test trigger3-1.3 {
    execsql {SELECT * FROM tbl}
} {}

# FAIL
do_test trigger3-2.1 {
    catchsql {
	BEGIN;
        INSERT INTO tbl VALUES (5, 5, 6);
        INSERT INTO tbl VALUES (2, 5, 6);