SQLite4
Check-in [c633d85c33]
Not logged in

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

Overview
Comment:Fix various savepoint-related problems in bt.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c633d85c33ba011b5660ef678a53feb7ea44d8a1
User & Date: dan 2013-11-14 19:29:24
Context
2013-11-15
09:44
Add a checksum to the database header. check-in: cba26fd7cc user: dan tags: trunk
2013-11-14
19:29
Fix various savepoint-related problems in bt. check-in: c633d85c33 user: dan tags: trunk
2013-11-13
16:34
Fix a problem in the VM code generated for a CREATE INDEX statement. check-in: e42f8bb1c2 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/bt_lock.c.

394
395
396
397
398
399
400


401
402
403
404
405
406
407
  BtLock *p,                      /* Locker handle */
  int (*xCkpt)(BtLock*),          /* Callback to checkpoint database */
  int (*xDel)(BtLock*)            /* Callback to delete wal+shm files */
){
  BtShared *pShared = p->pShared;
  BtLock **pp;
  int rc;                         /* Return code */



  sqlite4_mutex_enter(pShared->pClientMutex);
  for(pp=&p->pShared->pLock; *pp!=p; pp=&(*pp)->pNext);
  *pp = (*pp)->pNext;
  sqlite4_mutex_leave(pShared->pClientMutex);

  rc = btLockLockop(p, BT_LOCK_DMS1, BT_LOCK_EXCL, 1);







>
>







394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
  BtLock *p,                      /* Locker handle */
  int (*xCkpt)(BtLock*),          /* Callback to checkpoint database */
  int (*xDel)(BtLock*)            /* Callback to delete wal+shm files */
){
  BtShared *pShared = p->pShared;
  BtLock **pp;
  int rc;                         /* Return code */
  
  if( p->pShared==0 ) return SQLITE4_OK;

  sqlite4_mutex_enter(pShared->pClientMutex);
  for(pp=&p->pShared->pLock; *pp!=p; pp=&(*pp)->pNext);
  *pp = (*pp)->pNext;
  sqlite4_mutex_leave(pShared->pClientMutex);

  rc = btLockLockop(p, BT_LOCK_DMS1, BT_LOCK_EXCL, 1);

Changes to src/bt_log.c.

242
243
244
245
246
247
248

249
250
251
252
253
254
255
...
409
410
411
412
413
414
415
416
417

418
419
420
421
422
423
424
425
426
427
  assert( (nByte&0x00000007)==4 && nByte>=8 );
  btLogChecksum(nativeCksum, a, 8, aIn, aOut);
  btLogChecksum(nativeCksum, &a[4], nByte-4, aOut, aOut);
}

#define BT_PAGE_DEBUG 0
#define BT_VAL_DEBUG  0


static void btDebugTopology(BtLock *pLock, char *zStr, int iSide, u32 *aLog){
#if BT_PAGE_DEBUG
  fprintf(stderr, "%d:%s: (side=%d) %d..%d  %d..%d  %d..%d\n", 
      pLock->iDebugId, zStr, iSide,
      (int)aLog[0], (int)aLog[1], (int)aLog[2], 
      (int)aLog[3], (int)aLog[4], (int)aLog[5]
................................................................................
#endif
}

#ifndef NDEBUG
static void btDebugLogHeader(
    BtLock *pLock, const char *z, BtWalHdr *pHdr, int iHdr
){
#if BT_PAGE_DEBUG
  static int nCall = 0;

  fprintf(stderr, "%d:%d: %s log-header %d: (iCnt=%d iFirstFrame=%d)\n",
      pLock->iDebugId, nCall++, z, iHdr,
      (int)pHdr->iCnt, (int)pHdr->iFirstFrame
  );
  fflush(stderr);
#endif
}
#endif









>







 







|

>
|

|







242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
  assert( (nByte&0x00000007)==4 && nByte>=8 );
  btLogChecksum(nativeCksum, a, 8, aIn, aOut);
  btLogChecksum(nativeCksum, &a[4], nByte-4, aOut, aOut);
}

#define BT_PAGE_DEBUG 0
#define BT_VAL_DEBUG  0
#define BT_HDR_DEBUG  0

static void btDebugTopology(BtLock *pLock, char *zStr, int iSide, u32 *aLog){
#if BT_PAGE_DEBUG
  fprintf(stderr, "%d:%s: (side=%d) %d..%d  %d..%d  %d..%d\n", 
      pLock->iDebugId, zStr, iSide,
      (int)aLog[0], (int)aLog[1], (int)aLog[2], 
      (int)aLog[3], (int)aLog[4], (int)aLog[5]
................................................................................
#endif
}

#ifndef NDEBUG
static void btDebugLogHeader(
    BtLock *pLock, const char *z, BtWalHdr *pHdr, int iHdr
){
#if BT_HDR_DEBUG
  static int nCall = 0;
  fprintf(stderr, 
      "%d:%d: %s log-header %d: (iCnt=%d iFirstFrame=%d nPgsz=%d)\n",
      pLock->iDebugId, nCall++, z, iHdr,
      (int)pHdr->iCnt, (int)pHdr->iFirstFrame, (int)pHdr->nPgsz
  );
  fflush(stderr);
#endif
}
#endif


Changes to src/bt_main.c.

592
593
594
595
596
597
598



599
600
601
602
603
604
605
...
700
701
702
703
704
705
706


707
708
709
710
711
712
713
...
924
925
926
927
928
929
930

931
932
933
934
935
936
937
....
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
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091


1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121

1122
1123
1124
1125
1126
1127
1128
....
2344
2345
2346
2347
2348
2349
2350


















2351
2352
2353
2354
2355
2356
2357
2358


2359
2360
2361
2362
2363
2364
2365
....
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420



2421

2422
2423
2424
2425
2426
2427
2428
            if( rc==SQLITE4_OK ) rc = SQLITE4_INEXACT;
          }
        }
      }
    }
  }




  return rc;
}

int sqlite4BtCsrSeek(
  bt_cursor *pCsr, 
  const void *pK,                 /* Key to seek for */
  int nK,                         /* Size of key pK in bytes */
................................................................................
*/
static int btCsrStep(bt_cursor *pCsr, int bNext){
  const int pgsz = sqlite4BtPagerPagesize(pCsr->pDb->pPager);
  int rc = SQLITE4_OK;
  int bRequireDescent = 0;

  rc = btCsrReseek(pCsr);


  if( (pCsr->bSkipNext && bNext) || (pCsr->bSkipPrev && bNext==0) ){
    pCsr->bSkipPrev = pCsr->bSkipNext = 0;
    return rc;
  }
  pCsr->bSkipPrev = pCsr->bSkipNext = 0;

  while( rc==SQLITE4_OK ){
................................................................................
    }
  }

  pCsr->ovfl.nKey = nKLocal + nKOvfl;
  pCsr->ovfl.nVal = nVLocal + nVOvfl;

  nReq = pCsr->ovfl.nKey + pCsr->ovfl.nVal;

  rc = sqlite4_buffer_resize(&pCsr->ovfl.buf, nReq);
  if( rc!=SQLITE4_OK ) return rc;

  /* Copy in local data */
  aOut = (u8*)pCsr->ovfl.buf.p;
  memcpy(aOut, pKLocal, nKLocal);
  memcpy(&aOut[nKLocal], pVLocal, nVLocal);
................................................................................

int sqlite4BtCsrKey(bt_cursor *pCsr, const void **ppK, int *pnK){
  const int pgsz = sqlite4BtPagerPagesize(pCsr->pDb->pPager);
  u8 *aData;
  u8 *pCell;
  int iCell = pCsr->aiCell[pCsr->nPg-1];
  int nK;
  int rc = SQLITE4_OK;
  


  aData = (u8*)sqlite4BtPageData(pCsr->apPage[pCsr->nPg-1]);
  assert( btCellCount(aData, pgsz)>iCell );
  pCell = btCellFind(aData, pgsz, iCell);
  pCell += sqlite4BtVarintGet32(pCell, &nK);

  if( nK==0 ){
    /* type (c) leaf cell */
    rc = btCsrBuffer(pCsr, 0);
    if( rc==SQLITE4_OK ){
      *ppK = pCsr->ovfl.buf.p;
      *pnK = pCsr->ovfl.nKey;
    }
  }else{
    *ppK = pCell;
    *pnK = nK;

  }

  return rc;
}

int sqlite4BtCsrData(
  bt_cursor *pCsr,                /* Cursor handle */
  int iOffset,                    /* Offset of requested data */
  int nByte,                      /* Bytes requested (or -ve for all avail.) */
  const void **ppV,               /* OUT: Pointer to data buffer */
  int *pnV                        /* OUT: Size of data buffer in bytes */
){
  const int pgsz = sqlite4BtPagerPagesize(pCsr->pDb->pPager);
  int rc = SQLITE4_OK;
  u8 *aData;
  u8 *pCell;
  int iCell = pCsr->aiCell[pCsr->nPg-1];
  int nK = 0;
  int nV = 0;



  aData = (u8*)sqlite4BtPageData(pCsr->apPage[pCsr->nPg-1]);
  pCell = btCellFind(aData, pgsz, iCell);
  pCell += sqlite4BtVarintGet32(pCell, &nK);
  if( nK>0 ){
    pCell += nK;
    pCell += sqlite4BtVarintGet32(pCell, &nV);
  }

  if( nV==0 ){
    rc = btCsrBuffer(pCsr, 1);
    if( rc==SQLITE4_OK ){
      u8 *aBuf = (u8*)pCsr->ovfl.buf.p;
      *ppV = &aBuf[pCsr->ovfl.nKey];
      *pnV = pCsr->ovfl.nVal;
    }
  }else{
    *ppV = pCell;
    *pnV = (nV-1);
  }

#ifndef NDEBUG
  if( rc==SQLITE4_OK ){
    const void *pK; int nK;
    rc = sqlite4BtCsrKey(pCsr, &pK, &nK);
    if( rc==SQLITE4_OK ){
      BtLock *pLock = (BtLock*)pCsr->pDb->pPager;
      sqlite4BtDebugKV(pLock, "select", (u8*)pK, nK, (u8*)*ppV, *pnV);
    }
  }
#endif


  return rc;
}

/*
** The argument points to a buffer containing an overflow array. Return
** the size of the overflow array in bytes. 
................................................................................
      }
    }
  }else if( nCell==0 || (nFree>(2*pgsz/3) && bLeaf==0) ){
    rc = btBalance(pCsr, bLeaf, 0, 0);
  }
  return rc;
}



















/*
** Insert a new key/value pair or replace an existing one.
*/
int sqlite4BtReplace(bt_db *db, const void *pK, int nK, const void *pV, int nV){
  int rc = SQLITE4_OK;
  bt_cursor csr;



  sqlite4BtDebugKV((BtLock*)db->pPager, "replace", (u8*)pK, nK, (u8*)pV, nV);

  btCheckPageRefs(db);
  btCsrSetup(db, &csr);
  rc = btCsrSeek(&csr, pK, nK, BT_SEEK_GE, BT_CSRSEEK_UPDATE);
  if( rc==SQLITE4_OK ){
    /* The cursor currently points to an entry with key pK/nK. This call
................................................................................
  }
  btCsrReset(&csr, 1);

  btCheckPageRefs(db);
  return rc;
}

static int btSaveAllCursor(bt_cursor *pCsr){
  int rc = SQLITE4_OK;            /* Return code */
  bt_db *pDb = pCsr->pDb;         /* Database handle */
  bt_cursor *p;                   /* Used to iterate through cursors */

  for(p=pDb->pAllCsr; rc==SQLITE4_OK && p; p=p->pNextCsr){
    rc = btCsrBuffer(p, 0);
    if( rc==SQLITE4_OK ){
      assert( p->ovfl.buf.p );
      p->bRequireReseek = 1;
      if( p!=pCsr ) btCsrReleaseAll(p);
    }
  }

  return rc;
}


/*
** Delete the entry that the cursor currently points to.
*/
int sqlite4BtDelete(bt_cursor *pCsr){
  int rc;



  rc = btSaveAllCursor(pCsr);

  if( rc==SQLITE4_OK ){
    rc = btOverflowDelete(pCsr);
  }
  if( rc==SQLITE4_OK ){
    rc =  btDeleteFromPage(pCsr, 1);
  }
  if( rc==SQLITE4_OK ){







>
>
>







 







>
>







 







>







 







|
|
>
>
|
|
|
|

|
|
|
|
|
|
|
|
|
|
>













|






>
>
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|


|
|
|
|
|
|
|
|

>







 







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








>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






>
>
>
|
>







592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
...
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
....
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
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
....
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
....
2423
2424
2425
2426
2427
2428
2429

















2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
            if( rc==SQLITE4_OK ) rc = SQLITE4_INEXACT;
          }
        }
      }
    }
  }

  if( rc!=SQLITE4_OK && rc!=SQLITE4_INEXACT && eCsrseek!=BT_CSRSEEK_UPDATE ){
    btCsrReset(pCsr, 0);
  }
  return rc;
}

int sqlite4BtCsrSeek(
  bt_cursor *pCsr, 
  const void *pK,                 /* Key to seek for */
  int nK,                         /* Size of key pK in bytes */
................................................................................
*/
static int btCsrStep(bt_cursor *pCsr, int bNext){
  const int pgsz = sqlite4BtPagerPagesize(pCsr->pDb->pPager);
  int rc = SQLITE4_OK;
  int bRequireDescent = 0;

  rc = btCsrReseek(pCsr);
  if( rc==SQLITE4_OK && pCsr->nPg==0 ) rc = SQLITE4_NOTFOUND;

  if( (pCsr->bSkipNext && bNext) || (pCsr->bSkipPrev && bNext==0) ){
    pCsr->bSkipPrev = pCsr->bSkipNext = 0;
    return rc;
  }
  pCsr->bSkipPrev = pCsr->bSkipNext = 0;

  while( rc==SQLITE4_OK ){
................................................................................
    }
  }

  pCsr->ovfl.nKey = nKLocal + nKOvfl;
  pCsr->ovfl.nVal = nVLocal + nVOvfl;

  nReq = pCsr->ovfl.nKey + pCsr->ovfl.nVal;
  assert( nReq>0 );
  rc = sqlite4_buffer_resize(&pCsr->ovfl.buf, nReq);
  if( rc!=SQLITE4_OK ) return rc;

  /* Copy in local data */
  aOut = (u8*)pCsr->ovfl.buf.p;
  memcpy(aOut, pKLocal, nKLocal);
  memcpy(&aOut[nKLocal], pVLocal, nVLocal);
................................................................................

int sqlite4BtCsrKey(bt_cursor *pCsr, const void **ppK, int *pnK){
  const int pgsz = sqlite4BtPagerPagesize(pCsr->pDb->pPager);
  u8 *aData;
  u8 *pCell;
  int iCell = pCsr->aiCell[pCsr->nPg-1];
  int nK;
  int rc;

  rc = btCsrReseek(pCsr);
  if( rc==SQLITE4_OK ){
    aData = (u8*)sqlite4BtPageData(pCsr->apPage[pCsr->nPg-1]);
    assert( btCellCount(aData, pgsz)>iCell );
    pCell = btCellFind(aData, pgsz, iCell);
    pCell += sqlite4BtVarintGet32(pCell, &nK);

    if( nK==0 ){
      /* type (c) leaf cell */
      rc = btCsrBuffer(pCsr, 0);
      if( rc==SQLITE4_OK ){
        *ppK = pCsr->ovfl.buf.p;
        *pnK = pCsr->ovfl.nKey;
      }
    }else{
      *ppK = pCell;
      *pnK = nK;
    }
  }

  return rc;
}

int sqlite4BtCsrData(
  bt_cursor *pCsr,                /* Cursor handle */
  int iOffset,                    /* Offset of requested data */
  int nByte,                      /* Bytes requested (or -ve for all avail.) */
  const void **ppV,               /* OUT: Pointer to data buffer */
  int *pnV                        /* OUT: Size of data buffer in bytes */
){
  const int pgsz = sqlite4BtPagerPagesize(pCsr->pDb->pPager);
  int rc;
  u8 *aData;
  u8 *pCell;
  int iCell = pCsr->aiCell[pCsr->nPg-1];
  int nK = 0;
  int nV = 0;

  rc = btCsrReseek(pCsr);
  if( rc==SQLITE4_OK ){
    aData = (u8*)sqlite4BtPageData(pCsr->apPage[pCsr->nPg-1]);
    pCell = btCellFind(aData, pgsz, iCell);
    pCell += sqlite4BtVarintGet32(pCell, &nK);
    if( nK>0 ){
      pCell += nK;
      pCell += sqlite4BtVarintGet32(pCell, &nV);
    }

    if( nV==0 ){
      rc = btCsrBuffer(pCsr, 1);
      if( rc==SQLITE4_OK ){
        u8 *aBuf = (u8*)pCsr->ovfl.buf.p;
        *ppV = &aBuf[pCsr->ovfl.nKey];
        *pnV = pCsr->ovfl.nVal;
      }
    }else{
      *ppV = pCell;
      *pnV = (nV-1);
    }

#ifndef NDEBUG
    if( rc==SQLITE4_OK ){
      const void *pK; int nK;
      rc = sqlite4BtCsrKey(pCsr, &pK, &nK);
      if( rc==SQLITE4_OK ){
        BtLock *pLock = (BtLock*)pCsr->pDb->pPager;
        sqlite4BtDebugKV(pLock, "select", (u8*)pK, nK, (u8*)*ppV, *pnV);
      }
    }
#endif
  }

  return rc;
}

/*
** The argument points to a buffer containing an overflow array. Return
** the size of the overflow array in bytes. 
................................................................................
      }
    }
  }else if( nCell==0 || (nFree>(2*pgsz/3) && bLeaf==0) ){
    rc = btBalance(pCsr, bLeaf, 0, 0);
  }
  return rc;
}

static int btSaveAllCursor(bt_db *pDb, bt_cursor *pCsr){
  int rc = SQLITE4_OK;            /* Return code */
  bt_cursor *p;                   /* Used to iterate through cursors */

  for(p=pDb->pAllCsr; rc==SQLITE4_OK && p; p=p->pNextCsr){
    if( p->nPg>0 ){
      rc = btCsrBuffer(p, 0);
      if( rc==SQLITE4_OK ){
        assert( p->ovfl.buf.p );
        p->bRequireReseek = 1;
        if( p!=pCsr ) btCsrReleaseAll(p);
      }
    }
  }

  return rc;
}

/*
** Insert a new key/value pair or replace an existing one.
*/
int sqlite4BtReplace(bt_db *db, const void *pK, int nK, const void *pV, int nV){
  int rc = SQLITE4_OK;
  bt_cursor csr;

  rc = btSaveAllCursor(db, 0);
  if( rc!=SQLITE4_OK ) return rc;
  sqlite4BtDebugKV((BtLock*)db->pPager, "replace", (u8*)pK, nK, (u8*)pV, nV);

  btCheckPageRefs(db);
  btCsrSetup(db, &csr);
  rc = btCsrSeek(&csr, pK, nK, BT_SEEK_GE, BT_CSRSEEK_UPDATE);
  if( rc==SQLITE4_OK ){
    /* The cursor currently points to an entry with key pK/nK. This call
................................................................................
  }
  btCsrReset(&csr, 1);

  btCheckPageRefs(db);
  return rc;
}



















/*
** Delete the entry that the cursor currently points to.
*/
int sqlite4BtDelete(bt_cursor *pCsr){
  int rc;

  rc = btCsrReseek(pCsr);
  if( rc==SQLITE4_OK ){
    rc = btSaveAllCursor(pCsr->pDb, pCsr);
  }
  if( rc==SQLITE4_OK ){
    rc = btOverflowDelete(pCsr);
  }
  if( rc==SQLITE4_OK ){
    rc =  btDeleteFromPage(pCsr, 1);
  }
  if( rc==SQLITE4_OK ){

Changes to src/bt_pager.c.

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
280
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
312
313
314
315



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

331
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
347
348
349

350
351
352
353
354
355
356
...
358
359
360
361
362
363
364








































































365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
...
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
...
646
647
648
649
650
651
652



653
654
655
656
657


658
659
660
661
662

663
664
665
666
667
668
669
...
678
679
680
681
682
683
684
685

686
687
688
689
690
691
692
    sqlite4_free(p->btl.pEnv, pPg);
  }
}

static void btPurgeCache(BtPager *p){
  int i;
  assert( p->iTransactionLevel==0 );
  assert( p->pDirty==0 );
  assert( p->nTotalRef==0 );

  for(i=0; i<p->hash.nHash; i++){
    BtPage *pPg;
    BtPage *pNext;
    for(pPg=p->hash.aHash[i]; pPg; pPg=pNext){
      pNext = pPg->pNextHash;
................................................................................
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }
  }

  return rc;
}

static void btCloseSavepoints(BtPager *p, int iLevel){
  int nReq = MAX(iLevel - 2, 0);
  if( nReq<p->nSavepoint ){
    int i;
    for(i=p->nSavepoint-1; i>=nReq; i--){
      BtSavepoint *pSavepoint = &p->aSavepoint[i];
      BtSavepage *pSavepage;
      BtSavepage *pNext;

      /* Loop through each of the BtSavepage objects associated with this
      ** savepoint. Detach them from the BtPage objects and free all
      ** allocated memory.  */
      for(pSavepage=pSavepoint->pSavepage; pSavepage; pSavepage=pNext){
        BtPage *pPg = pSavepage->pPg;
        pNext = pSavepage->pNext;

        /* Detach the BtSavepage from its BtPage object */
        assert( pSavepage==pPg->pSavepage );
        pPg->pSavepage = pSavepage->pNextSavepage;

        /* Free associated memory allocations */
        sqlite4_free(p->btl.pEnv, pSavepage->aData);
        sqlite4_free(p->btl.pEnv, pSavepage);
      }
    }

    p->nSavepoint = nReq;
  }
}




/*
** If it has not already been added, add page pPg to the innermost 
** savepoint.
*/
static int btAddToSavepoint(BtPager *p, BtPage *pPg){
  int rc = SQLITE4_OK;
  BtSavepage *pSavepage;
  int iLevel = p->iTransactionLevel;

  /* Check if there is already a BtSavepage for this page associated with
  ** the innermost open savepoint. If so, this function becomes a no-op.  */
  for(pSavepage=pPg->pSavepage; pSavepage; pSavepage=pSavepage->pNextSavepage){
    if( pSavepage->iSavepoint==iLevel ) break;
  }


  if( pSavepage==0 ){
    pSavepage = sqlite4_malloc(p->btl.pEnv, sizeof(BtSavepage));
    if( pSavepage==0 ){
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pSavepage, 0, sizeof(BtSavepage));
    }


    if( rc==SQLITE4_OK && (1 || (pPg->flags & BT_PAGE_DIRTY)) ){
      pSavepage->aData = (u8*)sqlite4_malloc(p->btl.pEnv, p->pgsz);
      if( pSavepage->aData==0 ){
        sqlite4_free(p->btl.pEnv, pSavepage);
        rc = btErrorBkpt(SQLITE4_NOMEM);
      }else{
        memcpy(pSavepage->aData, pPg->aData, p->pgsz);
      }
    }


    if( rc==SQLITE4_OK ){
      pSavepage->pPg = pPg;
      pSavepage->iSavepoint = iLevel;

      pSavepage->pNextSavepage = pPg->pSavepage;
      pPg->pSavepage = pSavepage;

................................................................................
      pSavepage->pNext = p->aSavepoint[iLevel-3].pSavepage;
      p->aSavepoint[iLevel-3].pSavepage = pSavepage;
    }
  }

  return rc;
}









































































/*
** Close a pager database handle.
*/
int sqlite4BtPagerClose(BtPager *p){
  int rc;

  rc = sqlite4BtLockDisconnect((BtLock*)p, btCheckpoint, btCleanup);

  p->iTransactionLevel = 0;
  btCloseSavepoints(p, 0);
  btPurgeCache(p);
  sqlite4BtLogClose(p->pLog, 0);
  sqlite4_free(p->btl.pEnv, p->zFile);
  sqlite4_free(p->btl.pEnv, p->aSavepoint);
  sqlite4_free(p->btl.pEnv, p);
  return rc;
}
................................................................................
*/
static int btCommitTransaction(BtPager *p){
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;
  assert( p->iTransactionLevel>=2 );

  btCloseSavepoints(p, 2);

  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      int nPg = ((pNext==0) ? p->nPg : 0);
................................................................................
*/
static int btRollbackTransaction(BtPager *p){
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;

  assert( p->iTransactionLevel>=2 );
  btCloseSavepoints(p, 2);

  /* Loop through all dirty pages in memory. Discard those with nRef==0.
  ** Reload data from disk for any others.  */
  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
................................................................................
    }
  }
  p->pDirty = 0;

  return rc;
}

static int btRollbackSavepoints(BtPager *p, int iLevel){
  int nReq = iLevel - 2;

  assert( nReq>=1 );
  if( nReq<=p->nSavepoint ){
    int i;
    for(i=p->nSavepoint-1; i>=(nReq-1); i--){
      BtSavepoint *pSavepoint = &p->aSavepoint[i];
      BtSavepage *pSavepage;
      BtSavepage *pNext;

      /* Loop through each of the BtSavepage objects associated with this
      ** savepoint. Detach them from the BtPage objects and free all
      ** allocated memory.  */
      for(pSavepage=pSavepoint->pSavepage; pSavepage; pSavepage=pNext){
        BtPage *pPg = pSavepage->pPg;
        pNext = pSavepage->pNext;

        /* Detach the BtSavepage from its BtPage object */
        assert( pSavepage==pPg->pSavepage );
        pPg->pSavepage = pSavepage->pNextSavepage;

        /* Restore the page data */
        memcpy(pPg->aData, pSavepage->aData, p->pgsz);

        /* Free associated memory allocations */
        assert( pSavepage->aData ); /* temp */
        sqlite4_free(p->btl.pEnv, pSavepage->aData);
        sqlite4_free(p->btl.pEnv, pSavepage);
      }

      pSavepoint->pSavepage = 0;
    }

    p->nSavepoint = nReq;
  }

  return SQLITE4_OK;
}

/*
** Transactions. These methods are more or less the same as their 
** counterparts in bt.h.
*/
int sqlite4BtPagerBegin(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;
  assert( p->btl.pFd );
................................................................................
      p->iTransactionLevel = iLevel;
    }
  }

  return rc;
}




int sqlite4BtPagerCommit(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;

  assert( p->btl.pFd );
  if( p->iTransactionLevel>=iLevel ){


    if( p->iTransactionLevel>=2 && iLevel<2 ){
      /* Commit the main write transaction. */
      rc = btCommitTransaction(p);
    }
    p->iTransactionLevel = iLevel;

    if( iLevel==0 ){
      int rc2 = btCloseReadTransaction(p);
      if( rc==SQLITE4_OK ) rc = rc2;
    }
  }
  return rc;
}
................................................................................
    ** lower, rollback the outermost write transaction. If the requested
    ** level is less than 2, also drop the WRITER lock.  */
    if( p->iTransactionLevel>=2 ){
      if( iLevel<=2 ){
        rc = btRollbackTransaction(p);
        if( iLevel<2 ) sqlite4BtLogSnapshotEndWrite(p->pLog);
      }else{
        rc = btRollbackSavepoints(p, iLevel);

      }
    }

    p->iTransactionLevel = iLevel;
    if( iLevel==0 ){
      int rc2 = btCloseReadTransaction(p);
      if( rc==SQLITE4_OK ) rc = rc2;







<







 







|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|


>
>
>


|







|
|
|
<
|
>

|







>










>







 







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










|







 







|







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>





>
>





>







 







|
>







229
230
231
232
233
234
235

236
237
238
239
240
241
242
...
279
280
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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
...
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
...
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
...
628
629
630
631
632
633
634








































635
636
637
638
639
640
641
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
...
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
    sqlite4_free(p->btl.pEnv, pPg);
  }
}

static void btPurgeCache(BtPager *p){
  int i;
  assert( p->iTransactionLevel==0 );

  assert( p->nTotalRef==0 );

  for(i=0; i<p->hash.nHash; i++){
    BtPage *pPg;
    BtPage *pNext;
    for(pPg=p->hash.aHash[i]; pPg; pPg=pNext){
      pNext = pPg->pNextHash;
................................................................................
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }
  }

  return rc;
}

#ifndef NDEBUG
static void btDebugCheckSavepagesInOrder(BtPage *pPg, int iMax){
  BtSavepage *p;
  int i = iMax;
  for(p=pPg->pSavepage; p; p=p->pNextSavepage){
    assert( p->iSavepoint<=i );




















    i = p->iSavepoint-1;
  }
}
#else
# define btDebugCheckSavepagesInOrder(a,b)
#endif

/*
** If it has not already been added, add page pPg to the innermost
** savepoint.
*/
static int btAddToSavepoint(BtPager *p, BtPage *pPg){
  int rc = SQLITE4_OK;
  BtSavepage *pSavepage;
  int iLevel = p->iTransactionLevel;

  /* Assert that the linked list of BtSavepage objects is sorted in 
  ** descending order of level.  */
  btDebugCheckSavepagesInOrder(pPg, iLevel);


  if( pPg->pSavepage==0 || pPg->pSavepage->iSavepoint!=iLevel ){

    /* Allocate the new BtSavepage structure */
    pSavepage = sqlite4_malloc(p->btl.pEnv, sizeof(BtSavepage));
    if( pSavepage==0 ){
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pSavepage, 0, sizeof(BtSavepage));
    }

    /* Populate the new BtSavepage structure */
    if( rc==SQLITE4_OK && (1 || (pPg->flags & BT_PAGE_DIRTY)) ){
      pSavepage->aData = (u8*)sqlite4_malloc(p->btl.pEnv, p->pgsz);
      if( pSavepage->aData==0 ){
        sqlite4_free(p->btl.pEnv, pSavepage);
        rc = btErrorBkpt(SQLITE4_NOMEM);
      }else{
        memcpy(pSavepage->aData, pPg->aData, p->pgsz);
      }
    }

    /* Link the new BtSavepage structure into the pPg->pSavepage list */
    if( rc==SQLITE4_OK ){
      pSavepage->pPg = pPg;
      pSavepage->iSavepoint = iLevel;

      pSavepage->pNextSavepage = pPg->pSavepage;
      pPg->pSavepage = pSavepage;

................................................................................
      pSavepage->pNext = p->aSavepoint[iLevel-3].pSavepage;
      p->aSavepoint[iLevel-3].pSavepage = pSavepage;
    }
  }

  return rc;
}


/*
** Close enough savepoints (and discard any associated rollback data) to 
** cause the number remaining open to be consistent with transaction
** level iLevel. If the bRollback parameter is true, then the data is
** used to restore page states before is discarded. 
**
** If parameter iLevel is 2 (or lower) this means close all open 
** savepoints.
*/
static int btCloseSavepoints(
  BtPager *p,                     /* Pager handle */
  int iLevel,                     /* New transaction level */
  int bRollback                   /* True to rollback pages */
){
  int nReq = MAX(0, iLevel - 2);

  if( nReq<=p->nSavepoint ){
    int i;
    for(i=p->nSavepoint-1; i>=nReq; i--){
      BtSavepoint *pSavepoint = &p->aSavepoint[i];
      BtSavepage *pSavepg;
      BtSavepage *pNext;

      /* Loop through each of the BtSavepage objects associated with this
      ** savepoint. Detach them from the BtPage objects and free all
      ** allocated memory.  */
      for(pSavepg=pSavepoint->pSavepage; pSavepg; pSavepg=pNext){
        BtPage *pPg = pSavepg->pPg;
        pNext = pSavepg->pNext;

        assert( pSavepg==pPg->pSavepage );
        assert( pSavepg->iSavepoint==pSavepoint->iLevel );

        /* If bRollback is set, restore the page data */
        if( bRollback ){
          memcpy(pPg->aData, pSavepg->aData, p->pgsz);
        }else{
          int iNextSaved = (
              pSavepg->pNextSavepage ? pSavepg->pNextSavepage->iSavepoint : 2
          );
          if( iLevel>iNextSaved ){
            assert( iLevel>=3 );
            assert( p->aSavepoint[iLevel-3].iLevel==iLevel ); 
            pSavepg->pNext = p->aSavepoint[iLevel-3].pSavepage;
            pSavepg->iSavepoint = iLevel;
            p->aSavepoint[iLevel-3].pSavepage = pSavepg;
            pSavepg = 0;
          }
        }

        if( pSavepg ){
          /* Detach the BtSavepage from its BtPage object */
          pPg->pSavepage = pSavepg->pNextSavepage;

          /* Free associated memory allocations */
          assert( pSavepg->aData ); /* temp */
          sqlite4_free(p->btl.pEnv, pSavepg->aData);
          sqlite4_free(p->btl.pEnv, pSavepg);
        }
      }

      pSavepoint->pSavepage = 0;
    }

    p->nSavepoint = nReq;
  }

  return SQLITE4_OK;
}


/*
** Close a pager database handle.
*/
int sqlite4BtPagerClose(BtPager *p){
  int rc;

  rc = sqlite4BtLockDisconnect((BtLock*)p, btCheckpoint, btCleanup);

  p->iTransactionLevel = 0;
  btCloseSavepoints(p, 0, 0);
  btPurgeCache(p);
  sqlite4BtLogClose(p->pLog, 0);
  sqlite4_free(p->btl.pEnv, p->zFile);
  sqlite4_free(p->btl.pEnv, p->aSavepoint);
  sqlite4_free(p->btl.pEnv, p);
  return rc;
}
................................................................................
*/
static int btCommitTransaction(BtPager *p){
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;
  assert( p->iTransactionLevel>=2 );

  btCloseSavepoints(p, 2, 0);

  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      int nPg = ((pNext==0) ? p->nPg : 0);
................................................................................
*/
static int btRollbackTransaction(BtPager *p){
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;

  assert( p->iTransactionLevel>=2 );
  btCloseSavepoints(p, 2, 0);

  /* Loop through all dirty pages in memory. Discard those with nRef==0.
  ** Reload data from disk for any others.  */
  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
................................................................................
    }
  }
  p->pDirty = 0;

  return rc;
}









































/*
** Transactions. These methods are more or less the same as their 
** counterparts in bt.h.
*/
int sqlite4BtPagerBegin(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;
  assert( p->btl.pFd );
................................................................................
      p->iTransactionLevel = iLevel;
    }
  }

  return rc;
}

/*
** The sqlite4_kvstore.xCommit method.
*/
int sqlite4BtPagerCommit(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;

  assert( p->btl.pFd );
  if( p->iTransactionLevel>=iLevel ){
    btCloseSavepoints(p, iLevel, 0);

    if( p->iTransactionLevel>=2 && iLevel<2 ){
      /* Commit the main write transaction. */
      rc = btCommitTransaction(p);
    }
    p->iTransactionLevel = iLevel;

    if( iLevel==0 ){
      int rc2 = btCloseReadTransaction(p);
      if( rc==SQLITE4_OK ) rc = rc2;
    }
  }
  return rc;
}
................................................................................
    ** lower, rollback the outermost write transaction. If the requested
    ** level is less than 2, also drop the WRITER lock.  */
    if( p->iTransactionLevel>=2 ){
      if( iLevel<=2 ){
        rc = btRollbackTransaction(p);
        if( iLevel<2 ) sqlite4BtLogSnapshotEndWrite(p->pLog);
      }else{
        rc = btCloseSavepoints(p, iLevel-1, 1);
        p->nSavepoint++;
      }
    }

    p->iTransactionLevel = iLevel;
    if( iLevel==0 ){
      int rc2 = btCloseReadTransaction(p);
      if( rc==SQLITE4_OK ) rc = rc2;

Changes to src/vdbeapi.c.

741
742
743
744
745
746
747

748
749
750
751
752
753
754
/**************************** sqlite4_column_  *******************************
** The following routines are used to access elements of the current row
** in the result set.
*/
const void *sqlite4_column_blob(sqlite4_stmt *pStmt, int i, int *pnByte){
  const void *val;
  val = sqlite4_value_blob( columnMem(pStmt,i), pnByte );

  return val;
}
double sqlite4_column_double(sqlite4_stmt *pStmt, int i){
  double val = sqlite4_value_double( columnMem(pStmt,i) );
  columnMallocFailure(pStmt);
  return val;
}







>







741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
/**************************** sqlite4_column_  *******************************
** The following routines are used to access elements of the current row
** in the result set.
*/
const void *sqlite4_column_blob(sqlite4_stmt *pStmt, int i, int *pnByte){
  const void *val;
  val = sqlite4_value_blob( columnMem(pStmt,i), pnByte );
  columnMallocFailure(pStmt);
  return val;
}
double sqlite4_column_double(sqlite4_stmt *pStmt, int i){
  double val = sqlite4_value_double( columnMem(pStmt,i) );
  columnMallocFailure(pStmt);
  return val;
}

Changes to test/alter.test.

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
     index {sqlite_<t2>_unique1}    <t2>          \
  ]

# Check that ALTER TABLE works on attached databases.
#
ifcapable attach {
  do_test alter-1.8.1 {
    forcedelete test2.db
    forcedelete test2.db-journal
    execsql {
      ATTACH 'test2.db' AS aux;
    }
  } {}
  do_test alter-1.8.2 {
    execsql {
      CREATE TABLE t4(a PRIMARY KEY, b, c);







|
<







213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
     index {sqlite_<t2>_unique1}    <t2>          \
  ]

# Check that ALTER TABLE works on attached databases.
#
ifcapable attach {
  do_test alter-1.8.1 {
    db_delete test2.db

    execsql {
      ATTACH 'test2.db' AS aux;
    }
  } {}
  do_test alter-1.8.2 {
    execsql {
      CREATE TABLE t4(a PRIMARY KEY, b, c);

Changes to test/permutations.test.

128
129
130
131
132
133
134


135
136



































































137









































138
139
140
141
142
143
144
#   bt
#   veryquick
#   quick
#   full
#
lappend ::testsuitelist xxx



test_suite "bt" -prefix "" -description {
} -files {



































































  select1.test select2.test









































} -initialize {
  kv_default bt
}

test_suite "src4" -prefix "" -description {
} -files {
  simple.test simple2.test







>
>


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







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#   bt
#   veryquick
#   quick
#   full
#
lappend ::testsuitelist xxx

  # attach.test attach3.test 
  # fkey2.test 
test_suite "bt" -prefix "" -description {
} -files {
  simple3.test

  alter.test alter3.test alter4.test
  analyze.test analyze3.test analyze4.test analyze5.test 
  analyze6.test analyze7.test analyze8.test
  auth.test auth2.test auth3.test auth4.test
  aggerror.test
  attach4.test
  autoindex1.test
  badutf.test badutf2.test
  between.test
  bigrow.test
  bind.test
  blob.test
  boundary1.test boundary2.test boundary3.test boundary4.test
  capi2.test
  cast.test
  check.test
  coalesce.test 
  collate1.test collate2.test collate3.test collate4.test collate5.test
  collate6.test collate7.test collate8.test collate9.test collateA.test
  collateerr.test
  covidx.test
  conflict.test 
  count.test
  createtab.test
  cse.test
  ctime.test
  date.test
  default.test
  delete.test delete2.test delete3.test
  descidx1.test descidx2.test descidx3.test 
  distinct.test distinctagg.test
  e_createtable.test e_delete.test e_droptrigger.test e_dropview.test
  e_expr.test e_fkey.test e_insert.test e_reindex.test
  e_resolve.test e_select.test e_select2.test e_update.test
  enc.test enc3.test enc4.test
  errmsg.test
  eval.test
  expr.test
  exec.test
  exists.test
  fkey1.test fkey3.test fkey4.test
  func.test func2.test func3.test 
  fuzz.test fuzz2.test 
  in.test in2.test in3.test in4.test
  index.test index2.test index3.test index4.test 
  insert.test insert2.test insert3.test insert5.test
  join.test join2.test join3.test join4.test join5.test join6.test
  keyword1.test
  kvstore.test kvstore2.test
  laststmtchanges.test
  limit.test
  like.test like2.test
  main.test
  manydb.test
  misc5.test misc6.test
  misuse.test
  notnull.test
  null.test
  num.test num2.test
  pragma3.test
  printf.test 
  quote.test

  savepoint.test savepoint5.test 

  select1.test select2.test select3.test select4.test select5.test 
  select6.test select7.test select8.test select9.test selectA.test 
  selectB.test selectC.test selectF.test

  sort.test
  storage1.test

  subquery.test subquery2.test
  substr.test 

  trace2.test trace3.test

  trigger1.test trigger2.test trigger3.test trigger4.test trigger5.test 
  trigger6.test trigger7.test trigger8.test trigger9.test triggerB.test
  triggerC.test

  update.test
  view.test

  where.test  where5.test where6.test where7.test where8.test whereA.test 
  whereB.test whereC.test

  tkt-02a8e81d44.test tkt-26ff0c2d1e.test tkt-2d1a5c67d.test
  tkt-2ea2425d34.test tkt-31338dca7e.test
  tkt-38cb5df375.test tkt-3998683a16.test tkt-3a77c9714e.test
  tkt-3fe897352e.test tkt-4a03edc4c8.test tkt-54844eea3f.test
  tkt-5e10420e8d.test tkt-752e1646fc.test tkt-80ba201079.test
  tkt-80e031a00f.test tkt-91e2e8ba6f.test tkt-9d68c883.test
  tkt-b1d3a2e531.test tkt-b351d95f9.test  tkt-b72787b1.test
  tkt-bd484a090c.test tkt-d11f09d36e.test
  tkt-d635236375.test tkt-f973c7ac31.test tkt-fa7bf5ec.test
  tkt1443.test tkt1444.test tkt1449.test tkt1473.test tkt1501.test
  tkt1514.test tkt1537.test tkt1873.test
  tkt2141.test tkt2192.test tkt2213.test tkt2285.test tkt2339.test
  tkt2391.test tkt2450.test tkt2640.test tkt2767.test tkt2817.test
  tkt2822.test tkt2832.test tkt2927.test tkt2942.test tkt3121.test
  tkt3201.test tkt3292.test tkt3298.test tkt3334.test tkt3346.test
  tkt3419.test tkt3424.test tkt3442.test tkt3461.test tkt3493.test
  tkt3508.test tkt3522.test tkt3527.test tkt3541.test tkt3554.test
  tkt3581.test tkt35xx.test tkt3630.test tkt3718.test tkt3761.test
  tkt3773.test tkt3841.test tkt3871.test tkt3879.test tkt3911.test
  tkt3918.test tkt3922.test tkt3929.test tkt3935.test tkt3997.test
} -initialize {
  kv_default bt
}

test_suite "src4" -prefix "" -description {
} -files {
  simple.test simple2.test

Changes to test/savepoint.test.

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
} {1 2 3 4 5 6}
do_test savepoint-2.4 {
  execsql {
    ROLLBACK TO one;
  }
  execsql { SELECT * FROM t1 }
} {1 2 3}


do_test savepoint-2.5 {
  execsql {
    INSERT INTO t1 VALUES(7, 8, 9);
    SAVEPOINT two;
    INSERT INTO t1 VALUES(10, 11, 12);
  }







<







126
127
128
129
130
131
132

133
134
135
136
137
138
139
} {1 2 3 4 5 6}
do_test savepoint-2.4 {
  execsql {
    ROLLBACK TO one;
  }
  execsql { SELECT * FROM t1 }
} {1 2 3}


do_test savepoint-2.5 {
  execsql {
    INSERT INTO t1 VALUES(7, 8, 9);
    SAVEPOINT two;
    INSERT INTO t1 VALUES(10, 11, 12);
  }