/ Check-in [3fb97a63]
Login

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

Overview
Comment:Delay opening temporary pager files until they are first written. (CVS 4260)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3fb97a63ef70662abdba18ce8b480e6b0badcfb1
User & Date: danielk1977 2007-08-21 14:27:02
Context
2007-08-21
15:13
Readded the sqlite3_open_v2() interface. No test cases yet. Additional progress toward adding mutexes to all interfaces. (CVS 4261) check-in: 3787563e user: drh tags: trunk
14:27
Delay opening temporary pager files until they are first written. (CVS 4260) check-in: 3fb97a63 user: danielk1977 tags: trunk
13:51
Remove the obsolete static mutexes. Use only the lastest static mutex code. (CVS 4259) check-in: 6225cd46 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
544
545
546
547
548
549
550










551
552
553
554
555
556
557
...
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
....
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
....
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
....
1331
1332
1333
1334
1335
1336
1337






1338

1339
1340
1341
1342
1343
1344
1345
....
1819
1820
1821
1822
1823
1824
1825




1826
1827
1828
1829

1830
1831
1832
1833
1834
1835
1836
....
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
....
1995
1996
1997
1998
1999
2000
2001
2002

2003
2004
2005
2006
2007
2008
2009
....
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030


2031
2032
2033
2034
2035
2036
2037
2038
....
2566
2567
2568
2569
2570
2571
2572








2573
2574
2575
2576
2577
2578
2579
....
2830
2831
2832
2833
2834
2835
2836




2837
2838
2839
2840
2841
2842
2843
** 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.363 2007/08/21 10:44:16 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
** on success or an error code is something goes wrong.
*/
static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
  char ac[4];
  put32bits(ac, val);
  return sqlite3OsWrite(fd, ac, 4, offset);
}











/*
** This function should be called when an error occurs within the pager
** code. The first argument is a pointer to the pager structure, the
** second the error-code about to be returned by a pager API function. 
** The value returned is a copy of the second argument to this function. 
**
................................................................................

/*
** Unlock the database file.
*/
static void pager_unlock(Pager *pPager){
  if( !pPager->exclusiveMode ){
    if( !MEMDB ){
      sqlite3OsUnlock(pPager->fd, NO_LOCK);
      pPager->dbSize = -1;
      IOTRACE(("UNLOCK %p\n", pPager))
    }
    pPager->state = PAGER_UNLOCK;
    pPager->changeCountDone = 0;
  }
}
................................................................................
    pPager->nRec = 0;
  }else{
    assert( pPager->aInJournal==0 );
    assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
  }

  if( !pPager->exclusiveMode ){
    rc2 = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
    pPager->state = PAGER_SHARED;
  }else if( pPager->state==PAGER_SYNCED ){
    pPager->state = PAGER_EXCLUSIVE;
  }
  pPager->origDbSize = 0;
  pPager->setMaster = 0;
  pPager->needSync = 0;
................................................................................

/*
** Truncate the main file of the given pager to the number of pages
** indicated. Also truncate the cached representation of the file.
*/
static int pager_truncate(Pager *pPager, int nPage){
  int rc = SQLITE_OK;
  if( pPager->state>=PAGER_EXCLUSIVE ){
    rc = sqlite3OsTruncate(pPager->fd, pPager->pageSize*(i64)nPage);
  }
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
    pager_truncate_cache(pPager);
  }
  return rc;
................................................................................
/*
** Set the sectorSize for the given pager.
**
** The sector size is the larger of the sector size reported
** by sqlite3OsSectorSize() and the pageSize.
*/
static void setSectorSize(Pager *pPager){






  pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);

  if( pPager->sectorSize<pPager->pageSize ){
    pPager->sectorSize = pPager->pageSize;
  }
}

/*
** Playback the journal and thus restore the database file to
................................................................................
          int fout = 0;
          rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, oflag, &fout);
          readOnly = (fout&SQLITE_OPEN_READONLY);
        }
      }
    }
  }else{




    rc = sqlite3PagerOpentemp(pVfs, pPager->fd, pPager->zFilename);
    if( rc==SQLITE_OK ){
      tempFile = 1;
    }

  }

  if( pPager && rc==SQLITE_OK ){
    pPager->pTmpSpace = (char *)sqlite3_malloc(SQLITE_DEFAULT_PAGE_SIZE);
  }

  /* If an error occured in either of the blocks above, free the memory 
................................................................................
  pPager->noSync = pPager->tempFile || !useJournal;
  pPager->fullSync = (pPager->noSync?0:1);
  pPager->sync_flags = SQLITE_SYNC_NORMAL;
  /* pPager->pFirst = 0; */
  /* pPager->pFirstSynced = 0; */
  /* pPager->pLast = 0; */
  pPager->nExtra = FORCE_ALIGNMENT(nExtra);
  assert(pPager->fd->pMethods||memDb);
  if( !memDb ){
    setSectorSize(pPager);
  }
  /* pPager->pBusyHandler = 0; */
  /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
  *ppPager = pPager;
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
................................................................................
** these cases sqlite3OsRead() will return an error, to which the correct 
** response is to zero the memory at pDest and continue.  A real IO error 
** will presumably recur and be picked up later (Todo: Think about this).
*/
int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
  int rc = SQLITE_OK;
  memset(pDest, 0, N);
  if( MEMDB==0 ){

    IOTRACE(("DBHDR %p 0 %d\n", pPager, N))
    rc = sqlite3OsRead(pPager->fd, pDest, N, 0);
    if( rc==SQLITE_IOERR_SHORT_READ ){
      rc = SQLITE_OK;
    }
  }
  return rc;
................................................................................
**
** If the PENDING_BYTE lies on the page directly after the end of the
** file, then consider this page part of the file too. For example, if
** PENDING_BYTE is byte 4096 (the first byte of page 5) and the size of the
** file is 4096 bytes, 5 is returned instead of 4.
*/
int sqlite3PagerPagecount(Pager *pPager){
  i64 n;
  int rc;
  assert( pPager!=0 );
  if( pPager->errCode ){
    return 0;
  }
  if( pPager->dbSize>=0 ){
    n = pPager->dbSize;
  } else {


    if( (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){
      pager_error(pPager, rc);
      return 0;
    }
    if( n>0 && n<pPager->pageSize ){
      n = 1;
    }else{
      n /= pPager->pageSize;
................................................................................
  rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  pList = sort_pagelist(pList);
  while( pList ){








    assert( pList->dirty );
    /* If there are dirty pages in the page cache with page numbers greater
    ** than Pager.dbSize, this means sqlite3PagerTruncate() was called to
    ** make the file smaller (presumably by auto-vacuum code). Do not write
    ** any such pages to the file.
    */
    if( pList->pgno<=pPager->dbSize ){
................................................................................
/*
** Read the content of page pPg out of the database file.
*/
static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){
  int rc;
  i64 offset;
  assert( MEMDB==0 );




  offset = (pgno-1)*(i64)pPager->pageSize;
  rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize, offset);
  PAGER_INCR(sqlite3_pager_readdb_count);
  PAGER_INCR(pPager->nRead);
  IOTRACE(("PGIN %p %d\n", pPager, pgno));
  if( pgno==1 ){
    memcpy(&pPager->dbFileVers, &((u8*)PGHDR_TO_DATA(pPg))[24],







|







 







>
>
>
>
>
>
>
>
>
>







 







|







 







|







 







|







 







>
>
>
>
>
>
|
>







 







>
>
>
>
|
<
|
<
>







 







|







 







|
>







 







|








>
>
|







 







>
>
>
>
>
>
>
>







 







>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
...
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
....
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
....
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
....
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
....
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847

1848

1849
1850
1851
1852
1853
1854
1855
1856
....
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
....
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
....
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
....
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
....
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
** 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.364 2007/08/21 14:27:02 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
** on success or an error code is something goes wrong.
*/
static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
  char ac[4];
  put32bits(ac, val);
  return sqlite3OsWrite(fd, ac, 4, offset);
}

/*
** If file pFd is open, call sqlite3OsUnlock() on it.
*/
static int osUnlock(sqlite3_file *pFd, int eLock){
  if( !pFd->pMethods ){
    return SQLITE_OK;
  }
  return sqlite3OsUnlock(pFd, eLock);
}

/*
** This function should be called when an error occurs within the pager
** code. The first argument is a pointer to the pager structure, the
** second the error-code about to be returned by a pager API function. 
** The value returned is a copy of the second argument to this function. 
**
................................................................................

/*
** Unlock the database file.
*/
static void pager_unlock(Pager *pPager){
  if( !pPager->exclusiveMode ){
    if( !MEMDB ){
      osUnlock(pPager->fd, NO_LOCK);
      pPager->dbSize = -1;
      IOTRACE(("UNLOCK %p\n", pPager))
    }
    pPager->state = PAGER_UNLOCK;
    pPager->changeCountDone = 0;
  }
}
................................................................................
    pPager->nRec = 0;
  }else{
    assert( pPager->aInJournal==0 );
    assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
  }

  if( !pPager->exclusiveMode ){
    rc2 = osUnlock(pPager->fd, SHARED_LOCK);
    pPager->state = PAGER_SHARED;
  }else if( pPager->state==PAGER_SYNCED ){
    pPager->state = PAGER_EXCLUSIVE;
  }
  pPager->origDbSize = 0;
  pPager->setMaster = 0;
  pPager->needSync = 0;
................................................................................

/*
** Truncate the main file of the given pager to the number of pages
** indicated. Also truncate the cached representation of the file.
*/
static int pager_truncate(Pager *pPager, int nPage){
  int rc = SQLITE_OK;
  if( pPager->state>=PAGER_EXCLUSIVE && pPager->fd->pMethods ){
    rc = sqlite3OsTruncate(pPager->fd, pPager->pageSize*(i64)nPage);
  }
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
    pager_truncate_cache(pPager);
  }
  return rc;
................................................................................
/*
** Set the sectorSize for the given pager.
**
** The sector size is the larger of the sector size reported
** by sqlite3OsSectorSize() and the pageSize.
*/
static void setSectorSize(Pager *pPager){
  assert(pPager->fd->pMethods||pPager->tempFile);
  if( !pPager->tempFile ){
    /* Sector size doesn't matter for temporary files. Also, the file
    ** may not have been opened yet, in whcih case the OsSectorSize()
    ** call will segfault.
    */
    pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
  }
  if( pPager->sectorSize<pPager->pageSize ){
    pPager->sectorSize = pPager->pageSize;
  }
}

/*
** Playback the journal and thus restore the database file to
................................................................................
          int fout = 0;
          rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, oflag, &fout);
          readOnly = (fout&SQLITE_OPEN_READONLY);
        }
      }
    }
  }else{
    /* If a temporary file is requested, it is not opened immediately.
    ** In this case we accept the default page size and delay actually
    ** opening the file until the first call to OsWrite().
    */ 
    /* rc = sqlite3PagerOpentemp(pVfs, pPager->fd, pPager->zFilename); */

    tempFile = 1;

    pPager->state = PAGER_EXCLUSIVE;
  }

  if( pPager && rc==SQLITE_OK ){
    pPager->pTmpSpace = (char *)sqlite3_malloc(SQLITE_DEFAULT_PAGE_SIZE);
  }

  /* If an error occured in either of the blocks above, free the memory 
................................................................................
  pPager->noSync = pPager->tempFile || !useJournal;
  pPager->fullSync = (pPager->noSync?0:1);
  pPager->sync_flags = SQLITE_SYNC_NORMAL;
  /* pPager->pFirst = 0; */
  /* pPager->pFirstSynced = 0; */
  /* pPager->pLast = 0; */
  pPager->nExtra = FORCE_ALIGNMENT(nExtra);
  assert(pPager->fd->pMethods||memDb||tempFile);
  if( !memDb ){
    setSectorSize(pPager);
  }
  /* pPager->pBusyHandler = 0; */
  /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
  *ppPager = pPager;
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
................................................................................
** these cases sqlite3OsRead() will return an error, to which the correct 
** response is to zero the memory at pDest and continue.  A real IO error 
** will presumably recur and be picked up later (Todo: Think about this).
*/
int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
  int rc = SQLITE_OK;
  memset(pDest, 0, N);
  assert(MEMDB||pPager->fd->pMethods||pPager->tempFile);
  if( pPager->fd->pMethods ){
    IOTRACE(("DBHDR %p 0 %d\n", pPager, N))
    rc = sqlite3OsRead(pPager->fd, pDest, N, 0);
    if( rc==SQLITE_IOERR_SHORT_READ ){
      rc = SQLITE_OK;
    }
  }
  return rc;
................................................................................
**
** If the PENDING_BYTE lies on the page directly after the end of the
** file, then consider this page part of the file too. For example, if
** PENDING_BYTE is byte 4096 (the first byte of page 5) and the size of the
** file is 4096 bytes, 5 is returned instead of 4.
*/
int sqlite3PagerPagecount(Pager *pPager){
  i64 n = 0;
  int rc;
  assert( pPager!=0 );
  if( pPager->errCode ){
    return 0;
  }
  if( pPager->dbSize>=0 ){
    n = pPager->dbSize;
  } else {
    assert(pPager->fd->pMethods||pPager->tempFile);
    if( (pPager->fd->pMethods)
     && (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){
      pager_error(pPager, rc);
      return 0;
    }
    if( n>0 && n<pPager->pageSize ){
      n = 1;
    }else{
      n /= pPager->pageSize;
................................................................................
  rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  pList = sort_pagelist(pList);
  while( pList ){

    /* If the file has not yet been opened, open it now. */
    if( !pPager->fd->pMethods ){
      assert(pPager->tempFile);
      rc = sqlite3PagerOpentemp(pPager->pVfs, pPager->fd, pPager->zFilename);
      if( rc ) return rc;
    }

    assert( pList->dirty );
    /* If there are dirty pages in the page cache with page numbers greater
    ** than Pager.dbSize, this means sqlite3PagerTruncate() was called to
    ** make the file smaller (presumably by auto-vacuum code). Do not write
    ** any such pages to the file.
    */
    if( pList->pgno<=pPager->dbSize ){
................................................................................
/*
** Read the content of page pPg out of the database file.
*/
static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){
  int rc;
  i64 offset;
  assert( MEMDB==0 );
  assert(pPager->fd->pMethods||pPager->tempFile);
  if( !pPager->fd->pMethods ){
    return SQLITE_IOERR_SHORT_READ;
  }
  offset = (pgno-1)*(i64)pPager->pageSize;
  rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize, offset);
  PAGER_INCR(sqlite3_pager_readdb_count);
  PAGER_INCR(pPager->nRead);
  IOTRACE(("PGIN %p %d\n", pPager, pgno));
  if( pgno==1 ){
    memcpy(&pPager->dbFileVers, &((u8*)PGHDR_TO_DATA(pPg))[24],

Changes to test/conflict.test.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
281
282
283
284
285
286
287



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests for the conflict resolution extension
# to SQLite.
#
# $Id: conflict.test,v 1.29 2007/04/06 21:42:22 drh Exp $

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

ifcapable !conflict {
  finish_test
  return
................................................................................
#   cmd    An UPDATE command to execute against table t1
#   t0     True if there is an error from $cmd
#   t1     Content of "b" column of t1 assuming no error in $cmd
#   t2     Content of "x" column of t3
#   t3     Number of temporary files for tables
#   t4     Number of temporary files for statement journals
#



foreach {i conf1 cmd t0 t1 t2 t3 t4} {
  1 {}       UPDATE                  1 {6 7 8 9}  1 0 1
  2 REPLACE  UPDATE                  0 {7 6 9}    1 1 0
  3 IGNORE   UPDATE                  0 {6 7 3 9}  1 1 0
  4 FAIL     UPDATE                  1 {6 7 3 4}  1 0 0
  5 ABORT    UPDATE                  1 {1 2 3 4}  1 0 1
  6 ROLLBACK UPDATE                  1 {1 2 3 4}  0 0 0
  7 REPLACE  {UPDATE OR IGNORE}      0 {6 7 3 9}  1 1 0
  8 IGNORE   {UPDATE OR REPLACE}     0 {7 6 9}    1 1 0
  9 FAIL     {UPDATE OR IGNORE}      0 {6 7 3 9}  1 1 0
 10 ABORT    {UPDATE OR REPLACE}     0 {7 6 9}    1 1 0
 11 ROLLBACK {UPDATE OR IGNORE}      0 {6 7 3 9}  1 1 0
 12 {}       {UPDATE OR IGNORE}      0 {6 7 3 9}  1 1 0
 13 {}       {UPDATE OR REPLACE}     0 {7 6 9}    1 1 0
 14 {}       {UPDATE OR FAIL}        1 {6 7 3 4}  1 0 0
 15 {}       {UPDATE OR ABORT}       1 {1 2 3 4}  1 0 1
 16 {}       {UPDATE OR ROLLBACK}    1 {1 2 3 4}  0 0 0
} {
  if {$t0} {set t1 {column a is not unique}}
  if {[info exists TEMP_STORE] && $TEMP_STORE>=2} {
    set t3 $t4







|







 







>
>
>


|
|



|
|
|
|
|
|
|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests for the conflict resolution extension
# to SQLite.
#
# $Id: conflict.test,v 1.30 2007/08/21 14:27:02 danielk1977 Exp $

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

ifcapable !conflict {
  finish_test
  return
................................................................................
#   cmd    An UPDATE command to execute against table t1
#   t0     True if there is an error from $cmd
#   t1     Content of "b" column of t1 assuming no error in $cmd
#   t2     Content of "x" column of t3
#   t3     Number of temporary files for tables
#   t4     Number of temporary files for statement journals
#
# Update: Since temporary table files are now opened lazily, and none
# of the following tests use large quantities of data, t3 is always 0.
#
foreach {i conf1 cmd t0 t1 t2 t3 t4} {
  1 {}       UPDATE                  1 {6 7 8 9}  1 0 1
  2 REPLACE  UPDATE                  0 {7 6 9}    1 0 0
  3 IGNORE   UPDATE                  0 {6 7 3 9}  1 0 0
  4 FAIL     UPDATE                  1 {6 7 3 4}  1 0 0
  5 ABORT    UPDATE                  1 {1 2 3 4}  1 0 1
  6 ROLLBACK UPDATE                  1 {1 2 3 4}  0 0 0
  7 REPLACE  {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
  8 IGNORE   {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
  9 FAIL     {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
 10 ABORT    {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
 11 ROLLBACK {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
 12 {}       {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
 13 {}       {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
 14 {}       {UPDATE OR FAIL}        1 {6 7 3 4}  1 0 0
 15 {}       {UPDATE OR ABORT}       1 {1 2 3 4}  1 0 1
 16 {}       {UPDATE OR ROLLBACK}    1 {1 2 3 4}  0 0 0
} {
  if {$t0} {set t1 {column a is not unique}}
  if {[info exists TEMP_STORE] && $TEMP_STORE>=2} {
    set t3 $t4