/ Check-in [97491d4e]
Login

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

Overview
Comment:Handle some of the IO error conditions that may occur in a shared-cache context. (CVS 2980)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:97491d4eb5fc24d8f5cc7605db844359ecc6a818
User & Date: danielk1977 2006-01-20 10:55:05
Context
2006-01-20
15:45
Fix and test the processing of sqlite3_result_error() withing aggregate functions. Allow errors to come from the step function (a new capability). Ticket #1632. (CVS 2981) check-in: fd4a6bb1 user: drh tags: trunk
10:55
Handle some of the IO error conditions that may occur in a shared-cache context. (CVS 2980) check-in: 97491d4e user: danielk1977 tags: trunk
2006-01-19
17:42
Make sure sqlite3_thread_cleanup() does not try to allocate memory. (CVS 2979) check-in: 0208e422 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
....
4429
4430
4431
4432
4433
4434
4435
4436
4437



4438
4439
4440
4441
4442
4443
4444
....
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
....
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.302 2006/01/19 08:43:31 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    if( pCur->wrFlag ) r++; 
  }
  return r;
}
#endif

#ifdef SQLITE_TEST
/*
** Print debugging information about all cursors to standard output.
*/
void sqlite3BtreeCursorList(Btree *p){
  BtCursor *pCur;
  BtShared *pBt = p->pBt;
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
................................................................................
  /* 
  ** Find the parent page.
  */
  assert( pPage->isInit );
  assert( sqlite3pager_iswriteable(pPage->aData) );
  pBt = pPage->pBt;
  pParent = pPage->pParent;
  sqlite3pager_write(pParent->aData);
  assert( pParent );



  TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));

#ifndef SQLITE_OMIT_QUICKBALANCE
  /*
  ** A special case:  If a new entry has just been inserted into a
  ** table (that is, a btree with integer keys and all data at the leaves)
  ** and the new entry is the right-most entry in the tree (it has the
................................................................................
  return SQLITE_OK;
}
int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){
  return btreePageDump(p->pBt, pgno, recursive, 0);
}
#endif

#ifdef SQLITE_TEST
/*
** Fill aResult[] with information about the entry and page that the
** cursor is pointing to.
** 
**   aResult[0] =  The page number
**   aResult[1] =  The entry number
**   aResult[2] =  Total number of entries on this page
................................................................................
}

/*
** The following debugging interface has to be in this file (rather
** than in, for example, test1.c) so that it can get access to
** the definition of BtShared.
*/
#if defined(SQLITE_TEST) && defined(TCLSH)
#include <tcl.h>
int sqlite3_shared_cache_report(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){







|







 







|







 







<

>
>
>







 







|







 







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
....
4429
4430
4431
4432
4433
4434
4435

4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
....
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
....
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.303 2006/01/20 10:55:05 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    if( pCur->wrFlag ) r++; 
  }
  return r;
}
#endif

#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
/*
** Print debugging information about all cursors to standard output.
*/
void sqlite3BtreeCursorList(Btree *p){
  BtCursor *pCur;
  BtShared *pBt = p->pBt;
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
................................................................................
  /* 
  ** Find the parent page.
  */
  assert( pPage->isInit );
  assert( sqlite3pager_iswriteable(pPage->aData) );
  pBt = pPage->pBt;
  pParent = pPage->pParent;

  assert( pParent );
  if( SQLITE_OK!=(rc = sqlite3pager_write(pParent->aData)) ){
    return rc;
  }
  TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));

#ifndef SQLITE_OMIT_QUICKBALANCE
  /*
  ** A special case:  If a new entry has just been inserted into a
  ** table (that is, a btree with integer keys and all data at the leaves)
  ** and the new entry is the right-most entry in the tree (it has the
................................................................................
  return SQLITE_OK;
}
int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){
  return btreePageDump(p->pBt, pgno, recursive, 0);
}
#endif

#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
/*
** Fill aResult[] with information about the entry and page that the
** cursor is pointing to.
** 
**   aResult[0] =  The page number
**   aResult[1] =  The entry number
**   aResult[2] =  Total number of entries on this page
................................................................................
}

/*
** The following debugging interface has to be in this file (rather
** than in, for example, test1.c) so that it can get access to
** the definition of BtShared.
*/
#if defined(SQLITE_DEBUG) && defined(TCLSH)
#include <tcl.h>
int sqlite3_shared_cache_report(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
....
3013
3014
3015
3016
3017
3018
3019

3020
3021
3022
3023
3024
3025




3026

3027
3028
3029
3030

3031
3032

3033
3034
3035
3036
3037
3038
3039
....
3367
3368
3369
3370
3371
3372
3373

3374
3375
3376
3377

3378
3379






3380
3381
3382
3383
3384
3385
3386
** 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.245 2006/01/18 18:22:43 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_PROTOCOL,
** the error becomes persistent. All subsequent API calls on this Pager
** will immediately return the same error code.
*/
static int pager_error(Pager *pPager, int rc){
  assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK );
  if( 
    rc==SQLITE_FULL || 
    rc==SQLITE_IOERR || 
    rc==SQLITE_CORRUPT ||
    rc==SQLITE_PROTOCOL
  ){
    pPager->errCode = rc;
  }
  return rc;
}
................................................................................
          assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
          CODEC(pPager, pData, pPg->pgno, 7);
          cksum = pager_cksum(pPager, pPg->pgno, pData);
          saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager);
          store32bits(cksum, pPg, pPager->pageSize);
          szPg = pPager->pageSize+8;
          store32bits(pPg->pgno, pPg, -4);

          rc = sqlite3OsWrite(pPager->jfd, &((char*)pData)[-4], szPg);
          pPager->journalOff += szPg;
          TRACE4("JOURNAL %d page %d needSync=%d\n",
                  PAGERID(pPager), pPg->pgno, pPg->needSync);
          CODEC(pPager, pData, pPg->pgno, 0);
          *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved;




          if( rc!=SQLITE_OK ){

            sqlite3pager_rollback(pPager);
            if( !pPager->errCode ){
              pager_error(pPager, SQLITE_FULL);
            }

            return rc;
          }

          pPager->nRec++;
          assert( pPager->aInJournal!=0 );
          pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
          pPg->needSync = !pPager->noSync;
          if( pPager->stmtInUse ){
            pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
            page_add_to_stmt_list(pPg);
................................................................................
    rc2 = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){
      rc = rc2;
    }
  }else{
    rc = pager_playback(pPager);
  }

  if( rc!=SQLITE_OK ){
    rc = SQLITE_CORRUPT_BKPT;
    pager_error(pPager, SQLITE_CORRUPT);
  }

  pPager->dbSize = -1;
  return rc;






}

/*
** Return TRUE if the database file is opened read-only.  Return FALSE
** if the database is (in theory) writable.
*/
int sqlite3pager_isreadonly(Pager *pPager){







|







 







|
|







 







>






>
>
>
>

>




>


>







 







>


<

>

<
>
>
>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
....
3013
3014
3015
3016
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
....
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384

3385
3386
3387

3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
** 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.246 2006/01/20 10:55:05 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_PROTOCOL,
** the error becomes persistent. All subsequent API calls on this Pager
** will immediately return the same error code.
*/
static int pager_error(Pager *pPager, int rc){
  assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK );
  if( 
    rc==SQLITE_FULL ||
    rc==SQLITE_IOERR ||
    rc==SQLITE_CORRUPT ||
    rc==SQLITE_PROTOCOL
  ){
    pPager->errCode = rc;
  }
  return rc;
}
................................................................................
          assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
          CODEC(pPager, pData, pPg->pgno, 7);
          cksum = pager_cksum(pPager, pPg->pgno, pData);
          saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager);
          store32bits(cksum, pPg, pPager->pageSize);
          szPg = pPager->pageSize+8;
          store32bits(pPg->pgno, pPg, -4);

          rc = sqlite3OsWrite(pPager->jfd, &((char*)pData)[-4], szPg);
          pPager->journalOff += szPg;
          TRACE4("JOURNAL %d page %d needSync=%d\n",
                  PAGERID(pPager), pPg->pgno, pPg->needSync);
          CODEC(pPager, pData, pPg->pgno, 0);
          *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved;

	  /* An error has occured writing to the journal file. The 
          ** transaction will be rolled back by the layer above.
          */
          if( rc!=SQLITE_OK ){
#if 0
            sqlite3pager_rollback(pPager);
            if( !pPager->errCode ){
              pager_error(pPager, SQLITE_FULL);
            }
#endif
            return rc;
          }

          pPager->nRec++;
          assert( pPager->aInJournal!=0 );
          pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
          pPg->needSync = !pPager->noSync;
          if( pPager->stmtInUse ){
            pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
            page_add_to_stmt_list(pPg);
................................................................................
    rc2 = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){
      rc = rc2;
    }
  }else{
    rc = pager_playback(pPager);
  }
#if 0
  if( rc!=SQLITE_OK ){
    rc = SQLITE_CORRUPT_BKPT;

  }
#endif
  pPager->dbSize = -1;


  /* If an error occurs during a ROLLBACK, we can no longer trust the pager
  ** cache. So call pager_error() on the way out to make any error 
  ** persistent.
  */
  return pager_error(pPager, rc);
}

/*
** Return TRUE if the database file is opened read-only.  Return FALSE
** if the database is (in theory) writable.
*/
int sqlite3pager_isreadonly(Pager *pPager){

Changes to src/test1.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
....
3589
3590
3591
3592
3593
3594
3595

3596
3597
3598
3599

3600
3601
3602
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the printf() interface to SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.199 2006/01/19 11:28:07 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

................................................................................
      (char*)&sqlite3_sync_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_fullsync_count",
      (char*)&sqlite3_fullsync_count, TCL_LINK_INT);
#endif /* OS_UNIX */
  set_options(interp);

  {

    extern int sqlite3_shared_cache_report(void *, Tcl_Interp *,
                                    int, Tcl_Obj *CONST[]);
    Tcl_CreateObjCommand(interp, "sqlite_shared_cache_report", 
        sqlite3_shared_cache_report, 0, 0);

  }
  return TCL_OK;
}







|







 







>




>



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
....
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the printf() interface to SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.200 2006/01/20 10:55:05 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

................................................................................
      (char*)&sqlite3_sync_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_fullsync_count",
      (char*)&sqlite3_fullsync_count, TCL_LINK_INT);
#endif /* OS_UNIX */
  set_options(interp);

  {
#ifdef SQLITE_DEBUG
    extern int sqlite3_shared_cache_report(void *, Tcl_Interp *,
                                    int, Tcl_Obj *CONST[]);
    Tcl_CreateObjCommand(interp, "sqlite_shared_cache_report", 
        sqlite3_shared_cache_report, 0, 0);
#endif
  }
  return TCL_OK;
}

Changes to src/test3.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
593
594
595
596
597
598
599

600
601
602
603
604
605
606
...
610
611
612
613
614
615
616

617
618
619
620
621
622
623
....
1180
1181
1182
1183
1184
1185
1186

1187
1188
1189
1190
1191
1192
1193
....
1217
1218
1219
1220
1221
1222
1223

1224
1225
1226
1227
1228
1229
1230
....
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440




1441
1442
1443
1444
1445
1446
1447
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the btree.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test3.c,v 1.64 2005/12/30 16:28:02 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "pager.h"
#include "btree.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
}

/*
** Usage:   btree_cursor_list ID
**
** Print information about all cursors to standard output for debugging.
*/

static int btree_cursor_list(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  const char **argv      /* Text of each argument */
){
  Btree *pBt;
................................................................................
       " ID\"", 0);
    return TCL_ERROR;
  }
  pBt = sqlite3TextToPtr(argv[1]);
  sqlite3BtreeCursorList(pBt);
  return SQLITE_OK;
}


/*
** Usage:   btree_cursor ID TABLENUM WRITEABLE
**
** Create a new cursor.  Return the ID for the cursor.
*/
static int btree_cursor(
................................................................................
**   aResult[4] =  Number of free bytes on this page
**   aResult[5] =  Number of free blocks on the page
**   aResult[6] =  Total payload size (local + overflow)
**   aResult[7] =  Header size in bytes
**   aResult[8] =  Local payload size
**   aResult[9] =  Parent page number
*/

static int btree_cursor_info(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  const char **argv      /* Text of each argument */
){
  BtCursor *pCur;
................................................................................
  for(i=0; i<sizeof(aResult)/sizeof(aResult[0]); i++){
    sqlite3_snprintf(40,&zBuf[j]," %d", aResult[i]);
    j += strlen(&zBuf[j]);
  }
  Tcl_AppendResult(interp, &zBuf[1], 0);
  return SQLITE_OK;
}


/*
** The command is provided for the purpose of setting breakpoints.
** in regression test scripts.
**
** By setting a GDB breakpoint on this procedure and executing the
** btree_breakpoint command in a test script, we can stop GDB at
................................................................................
     { "btree_key",                (Tcl_CmdProc*)btree_key                },
     { "btree_data",               (Tcl_CmdProc*)btree_data               },
     { "btree_fetch_key",          (Tcl_CmdProc*)btree_fetch_key          },
     { "btree_fetch_data",         (Tcl_CmdProc*)btree_fetch_data         },
     { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
     { "btree_first",              (Tcl_CmdProc*)btree_first              },
     { "btree_last",               (Tcl_CmdProc*)btree_last               },
     { "btree_cursor_info",        (Tcl_CmdProc*)btree_cursor_info        },
     { "btree_cursor_list",        (Tcl_CmdProc*)btree_cursor_list        },
     { "btree_integrity_check",    (Tcl_CmdProc*)btree_integrity_check    },
     { "btree_breakpoint",         (Tcl_CmdProc*)btree_breakpoint         },
     { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
     { "btree_begin_statement",    (Tcl_CmdProc*)btree_begin_statement    },
     { "btree_commit_statement",   (Tcl_CmdProc*)btree_commit_statement   },
     { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement },
     { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
     { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     },




  };
  int i;

  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  Tcl_LinkVar(interp, "pager_refinfo_enable", (char*)&pager3_refinfo_enable,







|







 







>







 







>







 







>







 







>







 







<
<








>
>
>
>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
...
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
....
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
....
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
....
1428
1429
1430
1431
1432
1433
1434


1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the btree.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test3.c,v 1.65 2006/01/20 10:55:05 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "pager.h"
#include "btree.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
}

/*
** Usage:   btree_cursor_list ID
**
** Print information about all cursors to standard output for debugging.
*/
#ifdef SQLITE_DEBUG
static int btree_cursor_list(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  const char **argv      /* Text of each argument */
){
  Btree *pBt;
................................................................................
       " ID\"", 0);
    return TCL_ERROR;
  }
  pBt = sqlite3TextToPtr(argv[1]);
  sqlite3BtreeCursorList(pBt);
  return SQLITE_OK;
}
#endif

/*
** Usage:   btree_cursor ID TABLENUM WRITEABLE
**
** Create a new cursor.  Return the ID for the cursor.
*/
static int btree_cursor(
................................................................................
**   aResult[4] =  Number of free bytes on this page
**   aResult[5] =  Number of free blocks on the page
**   aResult[6] =  Total payload size (local + overflow)
**   aResult[7] =  Header size in bytes
**   aResult[8] =  Local payload size
**   aResult[9] =  Parent page number
*/
#ifdef SQLITE_DEBUG
static int btree_cursor_info(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  const char **argv      /* Text of each argument */
){
  BtCursor *pCur;
................................................................................
  for(i=0; i<sizeof(aResult)/sizeof(aResult[0]); i++){
    sqlite3_snprintf(40,&zBuf[j]," %d", aResult[i]);
    j += strlen(&zBuf[j]);
  }
  Tcl_AppendResult(interp, &zBuf[1], 0);
  return SQLITE_OK;
}
#endif

/*
** The command is provided for the purpose of setting breakpoints.
** in regression test scripts.
**
** By setting a GDB breakpoint on this procedure and executing the
** btree_breakpoint command in a test script, we can stop GDB at
................................................................................
     { "btree_key",                (Tcl_CmdProc*)btree_key                },
     { "btree_data",               (Tcl_CmdProc*)btree_data               },
     { "btree_fetch_key",          (Tcl_CmdProc*)btree_fetch_key          },
     { "btree_fetch_data",         (Tcl_CmdProc*)btree_fetch_data         },
     { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
     { "btree_first",              (Tcl_CmdProc*)btree_first              },
     { "btree_last",               (Tcl_CmdProc*)btree_last               },


     { "btree_integrity_check",    (Tcl_CmdProc*)btree_integrity_check    },
     { "btree_breakpoint",         (Tcl_CmdProc*)btree_breakpoint         },
     { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
     { "btree_begin_statement",    (Tcl_CmdProc*)btree_begin_statement    },
     { "btree_commit_statement",   (Tcl_CmdProc*)btree_commit_statement   },
     { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement },
     { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
     { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     },
#ifdef SQLITE_DEBUG
     { "btree_cursor_info",        (Tcl_CmdProc*)btree_cursor_info        },
     { "btree_cursor_list",        (Tcl_CmdProc*)btree_cursor_list        },
#endif
  };
  int i;

  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  Tcl_LinkVar(interp, "pager_refinfo_enable", (char*)&pager3_refinfo_enable,

Changes to src/vdbeaux.c.

1106
1107
1108
1109
1110
1111
1112





1113
1114
1115
1116
1117
1118
1119
....
1149
1150
1151
1152
1153
1154
1155





























1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168

1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191

1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
....
1213
1214
1215
1216
1217
1218
1219
1220

1221





1222
1223




1224
1225
1226


























1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256


1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271

1272
1273
1274
1275
1276
1277
1278
  for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){
    if( pOther==pVdbe ) continue;
    if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue;
    closeAllCursors(pOther);
    pOther->aborted = 1;
  }
}






/* 
** This routine checks that the sqlite3.activeVdbeCnt count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
** This is an internal self-check only - it is not an essential processing
** step.
................................................................................
** lock contention, return SQLITE_BUSY.  If SQLITE_BUSY is returned, it
** means the close did not happen and needs to be repeated.
*/
int sqlite3VdbeHalt(Vdbe *p){
  sqlite3 *db = p->db;
  int i;
  int (*xFunc)(Btree *pBt) = 0;  /* Function to call on each btree backend */






























  if( sqlite3MallocFailed() ){
    p->rc = SQLITE_NOMEM;
  }

  if( p->magic!=VDBE_MAGIC_RUN ){
    /* Already halted.  Nothing to do. */
    assert( p->magic==VDBE_MAGIC_HALT );
    return SQLITE_OK;
  }
  closeAllCursors(p);
  checkActiveVdbeCnt(db);
  if( p->pc<0 ){

    /* No commit or rollback needed if the program never started */
  }else if( db->autoCommit && db->activeVdbeCnt==1 ){

    if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && p->rc!=SQLITE_NOMEM)){
      /* The auto-commit flag is true, there are no other active queries
      ** using this handle and the vdbe program was successful or hit an
      ** 'OR FAIL' constraint. This means a commit is required.
      */
      int rc = vdbeCommit(db);
      if( rc==SQLITE_BUSY ){
        return SQLITE_BUSY;
      }else if( rc!=SQLITE_OK ){
        p->rc = rc;
        sqlite3RollbackAll(db);
      }else{
        sqlite3CommitInternalChanges(db);
      }
    }else{
      sqlite3RollbackAll(db);
    }
  }else{

    if( p->rc==SQLITE_NOMEM ){

      /* This loop does static analysis of the query to see which of the
      ** following three categories it falls into:
      **
      **     Read-only
      **     Query with statement journal          -> rollback statement
      **     Query without statement journal       -> rollback transaction
      **
      ** We could do something more elegant than this static analysis (i.e.
      ** store the type of query as part of the compliation phase), but 
      ** handling malloc() failure is a fairly obscure edge case so this is
      ** probably easier.
      **
      ** Todo: This means we always override the p->errorAction value for a
      ** malloc() failure. Is there any other choice here though?
      */
      int isReadOnly = 1;
      int isStatement = 0;
      assert(p->aOp || p->nOp==0);
      for(i=0; i<p->nOp; i++){ 
        switch( p->aOp[i].opcode ){
          case OP_Transaction:
................................................................................
            isReadOnly = 0;
            break;
          case OP_Statement:
            isStatement = 1;
            break;
        }
      }
      if( (isReadOnly||isStatement) && p->errorAction!=OE_Rollback ){

        p->errorAction = OE_Abort;





      }else{ 
        p->errorAction = OE_Rollback;




      }
    }



























    if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
      xFunc = sqlite3BtreeCommitStmt;
    }else if( p->errorAction==OE_Abort ){
      xFunc = sqlite3BtreeRollbackStmt;
    }else{
      abortOtherActiveVdbes(p);
      sqlite3RollbackAll(db);
      db->autoCommit = 1;
    }
  }

  /* If xFunc is not NULL, then it is one of 
  ** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on
  ** each backend. If an error occurs and the return code is still
  ** SQLITE_OK, set the return code to the new error value.
  */
  assert(!xFunc ||
    xFunc==sqlite3BtreeCommitStmt ||
    xFunc==sqlite3BtreeRollbackStmt
  );
  for(i=0; xFunc && i<db->nDb; i++){ 
    int rc;
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){
      rc = xFunc(pBt);
      if( p->rc==SQLITE_OK ) p->rc = rc;
    }
  }

  /* If this was an INSERT, UPDATE or DELETE, set the change counter. */


  if( p->changeCntOn && p->pc>=0 ){
    if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
      sqlite3VdbeSetChanges(db, p->nChange);
    }else{
      sqlite3VdbeSetChanges(db, 0);
    }
    p->nChange = 0;
  }

  /* Rollback or commit any schema changes that occurred. */
  if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
    sqlite3ResetInternalSchema(db, 0);
    db->flags = (db->flags | SQLITE_InternChanges);
  }


  /* We have successfully halted and closed the VM.  Record this fact. */
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
  }
  p->magic = VDBE_MAGIC_HALT;
  checkActiveVdbeCnt(db);








>
>
>
>
>







 







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




<







<
>
|
|

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




|
|



|
|
|
<
<







 







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







1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
....
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193

1194
1195
1196
1197
1198
1199
1200

1201
1202
1203
1204
1205


















1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219


1220
1221
1222
1223
1224
1225
1226
....
1227
1228
1229
1230
1231
1232
1233

1234
1235
1236
1237
1238
1239
1240
1241

1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279

1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
  for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){
    if( pOther==pVdbe ) continue;
    if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue;
    closeAllCursors(pOther);
    pOther->aborted = 1;
  }
}

static void rollbackAll(sqlite3 *db, Vdbe *pVdbe){
  abortOtherActiveVdbes(pVdbe);
  sqlite3RollbackAll(db);
}

/* 
** This routine checks that the sqlite3.activeVdbeCnt count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
** This is an internal self-check only - it is not an essential processing
** step.
................................................................................
** lock contention, return SQLITE_BUSY.  If SQLITE_BUSY is returned, it
** means the close did not happen and needs to be repeated.
*/
int sqlite3VdbeHalt(Vdbe *p){
  sqlite3 *db = p->db;
  int i;
  int (*xFunc)(Btree *pBt) = 0;  /* Function to call on each btree backend */
  int isSpecialError;            /* Set to true if SQLITE_NOMEM or IOERR */

  /* This function contains the logic that determines if a statement or
  ** transaction will be committed or rolled back as a result of the
  ** execution of this virtual machine. 
  **
  ** Special errors:
  **
  **     If an SQLITE_NOMEM error has occured in a statement that writes to
  **     the database, then either a statement or transaction must be rolled
  **     back to ensure the tree-structures are in a consistent state. A
  **     statement transaction is rolled back if one is open, otherwise the
  **     entire transaction must be rolled back.
  **
  **     If an SQLITE_IOERR error has occured in a statement that writes to
  **     the database, then the entire transaction must be rolled back. The
  **     I/O error may have caused garbage to be written to the journal 
  **     file. Were the transaction to continue and eventually be rolled 
  **     back that garbage might end up in the database file.
  **     
  **     In both of the above cases, the Vdbe.errorAction variable is 
  **     ignored. If the sqlite3.autoCommit flag is false and a transaction
  **     is rolled back, it will be set to true.
  **
  ** Other errors:
  **
  ** No error:
  **
  */

  if( sqlite3MallocFailed() ){
    p->rc = SQLITE_NOMEM;
  }

  if( p->magic!=VDBE_MAGIC_RUN ){
    /* Already halted.  Nothing to do. */
    assert( p->magic==VDBE_MAGIC_HALT );
    return SQLITE_OK;
  }
  closeAllCursors(p);
  checkActiveVdbeCnt(db);


  /* No commit or rollback needed if the program never started */
  if( p->pc>=0 ){

    /* Check for one of the special errors - SQLITE_NOMEM or SQLITE_IOERR */


















    isSpecialError = ((p->rc==SQLITE_NOMEM || p->rc==SQLITE_IOERR)?1:0);
    if( isSpecialError ){
      /* This loop does static analysis of the query to see which of the
      ** following three categories it falls into:
      **
      **     Read-only
      **     Query with statement journal
      **     Query without statement journal
      **
      ** We could do something more elegant than this static analysis (i.e.
      ** store the type of query as part of the compliation phase), but 
      ** handling malloc() or IO failure is a fairly obscure edge case so 
      ** this is probably easier. Todo: Might be an opportunity to reduce 
      ** code size a very small amount though...


      */
      int isReadOnly = 1;
      int isStatement = 0;
      assert(p->aOp || p->nOp==0);
      for(i=0; i<p->nOp; i++){ 
        switch( p->aOp[i].opcode ){
          case OP_Transaction:
................................................................................
            isReadOnly = 0;
            break;
          case OP_Statement:
            isStatement = 1;
            break;
        }
      }

  
      /* If the query was read-only, we need do no rollback at all. Otherwise,
      ** proceed with the special handling.
      */
      if( !isReadOnly ){
        if( p->rc==SQLITE_NOMEM && isStatement ){
          xFunc = sqlite3BtreeRollbackStmt;
        }else{

          /* We are forced to roll back the active transaction. Before doing
          ** so, abort any other statements this handle currently has active.
          */
          db->autoCommit = 1;
        }
      }
    }
  
    /* If the auto-commit flag is set and this is the only active vdbe, then
    ** we do either a commit or rollback of the current transaction. 
    **
    ** Note: This block also runs if one of the special errors handled 
    ** above has occured. 
    */
    if( db->autoCommit && db->activeVdbeCnt==1 ){
      if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
	/* The auto-commit flag is true, and the vdbe program was 
        ** successful or hit an 'OR FAIL' constraint. This means a commit 
        ** is required.
        */
        int rc = vdbeCommit(db);
        if( rc==SQLITE_BUSY ){
          return SQLITE_BUSY;
        }else if( rc!=SQLITE_OK ){
          p->rc = rc;
          rollbackAll(db, p);
        }else{
          sqlite3CommitInternalChanges(db);
        }
      }else{
        rollbackAll(db, p);
      }
    }else if( !xFunc ){
      if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
        xFunc = sqlite3BtreeCommitStmt;
      }else if( p->errorAction==OE_Abort ){
        xFunc = sqlite3BtreeRollbackStmt;
      }else{

        rollbackAll(db, p);
        db->autoCommit = 1;
      }
    }
  
    /* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or
    ** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs
    ** and the return code is still SQLITE_OK, set the return code to the new
    ** error value.
    */
    assert(!xFunc ||
      xFunc==sqlite3BtreeCommitStmt ||
      xFunc==sqlite3BtreeRollbackStmt
    );
    for(i=0; xFunc && i<db->nDb; i++){ 
      int rc;
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = xFunc(pBt);
        if( p->rc==SQLITE_OK ) p->rc = rc;
      }
    }
  
    /* If this was an INSERT, UPDATE or DELETE and the statement was committed, 
    ** set the change counter. 
    */
    if( p->changeCntOn && p->pc>=0 ){
      if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
        sqlite3VdbeSetChanges(db, p->nChange);
      }else{
        sqlite3VdbeSetChanges(db, 0);
      }
      p->nChange = 0;
    }
  
    /* Rollback or commit any schema changes that occurred. */
    if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
      sqlite3ResetInternalSchema(db, 0);
      db->flags = (db->flags | SQLITE_InternChanges);
    }
  }

  /* We have successfully halted and closed the VM.  Record this fact. */
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
  }
  p->magic = VDBE_MAGIC_HALT;
  checkActiveVdbeCnt(db);

Changes to test/malloc3.test.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
478
479
480
481
482
483
484


















485
486
487
488
489
490
491
...
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.6 2006/01/10 18:27:42 danielk1977 Exp $

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

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
................................................................................
  do_test $testid {
    execsql {
      SELECT * FROM tbl2, def WHERE d = x;
    }
  } {1 2 3 1 2 3}
}



















#
# End of test program declaration
#--------------------------------------------------------------------------

proc run_test {arglist {pcstart 0} {iFailStart 1}} {
  if {[llength $arglist] %2} {
    error "Uneven number of arguments to TEST"
................................................................................
# if {$iFail > ($iFailStart+1)} return
  }
}

# Turn of the Tcl interface's prepared statement caching facility.
db cache size 0

run_test $::run_test_script
# run_test [lrange $::run_test_script 0 3] 0 63
sqlite_malloc_fail 0
db close

pp_check_for_leaks

finish_test








|







 







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







 







|








9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
...
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.7 2006/01/20 10:55:05 danielk1977 Exp $

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

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
................................................................................
  do_test $testid {
    execsql {
      SELECT * FROM tbl2, def WHERE d = x;
    }
  } {1 2 3 1 2 3}
}

# Test what happens when a malloc() fails while there are other active
# statements. This changes the way sqlite3VdbeHalt() works.
TEST 32 {
  if {![info exists ::STMT32]} {
    set sql "SELECT name FROM sqlite_master"
    set ::STMT32 [sqlite3_prepare $::DB $sql -1 DUMMY]
    do_test $testid {
      sqlite3_step $::STMT32
    } {SQLITE_ROW}
  }
puts [execsql {SELECT * FROM ghi}]
}
SQL {
  BEGIN;
  INSERT INTO ghi SELECT * FROM ghi;
  COMMIT;
}

#
# End of test program declaration
#--------------------------------------------------------------------------

proc run_test {arglist {pcstart 0} {iFailStart 1}} {
  if {[llength $arglist] %2} {
    error "Uneven number of arguments to TEST"
................................................................................
# if {$iFail > ($iFailStart+1)} return
  }
}

# Turn of the Tcl interface's prepared statement caching facility.
db cache size 0

run_test $::run_test_script 76 6
# run_test [lrange $::run_test_script 0 3] 0 63
sqlite_malloc_fail 0
db close

pp_check_for_leaks

finish_test

Changes to test/quick.test.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
#    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 runs all tests.
#
# $Id: quick.test,v 1.41 2006/01/10 07:14:24 danielk1977 Exp $

proc lshift {lvar} {
  upvar $lvar l
  set ret [lindex $l 0]
  set l [lrange $l 1 end]
  return $ret
}
................................................................................
  btree5.test
  btree6.test
  corrupt.test
  crash.test
  malloc.test
  malloc2.test
  malloc3.test
  malloc4.test
  memleak.test
  misuse.test
  quick.test

  autovacuum_crash.test
  btree8.test
  utf16.test

}

if {[sqlite3 -has-codec]} {
  # lappend EXCLUDE \
  #  conflict.test
}








|







 







<







>







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#    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 runs all tests.
#
# $Id: quick.test,v 1.42 2006/01/20 10:55:05 danielk1977 Exp $

proc lshift {lvar} {
  upvar $lvar l
  set ret [lindex $l 0]
  set l [lrange $l 1 end]
  return $ret
}
................................................................................
  btree5.test
  btree6.test
  corrupt.test
  crash.test
  malloc.test
  malloc2.test
  malloc3.test

  memleak.test
  misuse.test
  quick.test

  autovacuum_crash.test
  btree8.test
  utf16.test
  shared_err.test
}

if {[sqlite3 -has-codec]} {
  # lappend EXCLUDE \
  #  conflict.test
}

Added test/shared_err.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
# 2005 December 30
#
# 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.
#
#***********************************************************************
#
# The focus of the tests in this file are IO errors that occur in a shared
# cache context. What happens to connection B if one connection A encounters
# an IO-error whilst reading or writing the file-system?
#
# $Id: shared_err.test,v 1.1 2006/01/20 10:55:05 danielk1977 Exp $

proc skip {args} {}


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

ifcapable !shared_cache||!subquery {
  finish_test
  return
}
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]

skip \
do_ioerr_test shared_ioerr-1 -tclprep {
  sqlite3 db2 test.db
  execsql {
    PRAGMA read_uncommitted = 1;
    CREATE TABLE t1(a,b,c);
    BEGIN;
    SELECT * FROM sqlite_master;
  } db2
} -sqlbody {
  SELECT * FROM sqlite_master;
  INSERT INTO t1 VALUES(1,2,3);
  BEGIN TRANSACTION;
  INSERT INTO t1 VALUES(1,2,3);
  INSERT INTO t1 VALUES(4,5,6);
  ROLLBACK;
  SELECT * FROM t1;
  BEGIN TRANSACTION;
  INSERT INTO t1 VALUES(1,2,3);
  INSERT INTO t1 VALUES(4,5,6);
  COMMIT;
  SELECT * FROM t1;
  DELETE FROM t1 WHERE a<100;
} -cleanup {
  do_test shared_ioerr-$n.cleanup.1 {
    set res [catchsql {
      SELECT * FROM t1;
    } db2]
    set possible_results [list            \
      "1 {disk I/O error}"                \
      "0 {1 2 3}"                         \
      "0 {1 2 3 1 2 3 4 5 6}"             \
      "0 {1 2 3 1 2 3 4 5 6 1 2 3 4 5 6}" \
      "0 {}"                              \
    ]
    set rc [expr [lsearch -exact $possible_results $res] >= 0]
    if {$rc != 1} {
      puts ""
      puts "Result: $res"
    }
    set rc
  } {1}
  db2 close
}

do_ioerr_test shared_ioerr-2 -tclprep {
  sqlite3 db2 test.db
  execsql {
    PRAGMA read_uncommitted = 1;
    BEGIN;
    CREATE TABLE t1(a, b);
    INSERT INTO t1(oid) VALUES(NULL);
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    INSERT INTO t1(oid) SELECT NULL FROM t1;
    UPDATE t1 set a = oid, b = 'abcdefghijklmnopqrstuvwxyz0123456789';
    CREATE INDEX i1 ON t1(a);
    COMMIT;
    BEGIN;
    SELECT * FROM sqlite_master;
  } db2
} -tclbody {
  set ::residx 0
  execsql {DELETE FROM t1 WHERE 0 = (a % 2);}
  incr ::residx

  # When this transaction begins the table contains 512 entries. The
  # two statements together add 512+146 more if it succeeds. 
  # (1024/7==146)
  execsql {BEGIN;}
  execsql {INSERT INTO t1 SELECT a+1, b FROM t1;}
  execsql {INSERT INTO t1 SELECT 'string' || a, b FROM t1 WHERE 0 = (a%7);}
  execsql {COMMIT;}

  incr ::residx
} -cleanup {
  do_test shared_ioerr-2.$n.cleanup.1 {
    set res [catchsql {
      SELECT max(a), min(a), count(*) FROM (SELECT a FROM t1 order by a);
    } db2]
    set possible_results [list \
      {0 {1024 1 1024}}        \
      {0 {1023 1 512}}         \
      {0 {string994 1 1170}}   \
    ]
    set idx [lsearch -exact $possible_results $res]
    set success [expr {$idx==$::residx || $res=="1 {disk I/O error}"}]
    if {!$success} {
      puts ""
      puts "Result: \"$res\" ($::residx)"
    }
    set success
  } {1}
  db2 close
}

catch {db close}
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test