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

Overview
Comment:Add code to prevent database writers from overwriting portions of the log that might be required by present or future database readers or recoverers.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 407a82adbf7d4b673e5f77c1efd7e6a5bc43b4bb
User & Date: dan 2013-10-29 17:46:10.091
Context
2013-10-30
18:37
Add sub-transactions to btree module. check-in: 9e00823074 user: dan tags: trunk
2013-10-29
17:46
Add code to prevent database writers from overwriting portions of the log that might be required by present or future database readers or recoverers. check-in: 407a82adbf user: dan tags: trunk
2013-10-28
19:48
Add code for taking and querying read-locks. Untested. check-in: ef0cf1ade3 user: dan tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btInt.h.
188
189
190
191
192
193
194

195

196
197
198
199
200
201
202
int sqlite4BtLogClose(BtLog*, int bCleanup);

int sqlite4BtLogRead(BtLog*, u32 pgno, u8 *aData);
int sqlite4BtLogWrite(BtLog*, u32 pgno, u8 *aData, int bCommit);

int sqlite4BtLogSnapshotOpen(BtLog*);
int sqlite4BtLogSnapshotClose(BtLog*);

int sqlite4BtLogSnapshotWritable(BtLog*);


int sqlite4BtLogSize(BtLog*);
int sqlite4BtLogCheckpoint(BtLog*);

int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame);

#ifndef NDEBUG







>
|
>







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
int sqlite4BtLogClose(BtLog*, int bCleanup);

int sqlite4BtLogRead(BtLog*, u32 pgno, u8 *aData);
int sqlite4BtLogWrite(BtLog*, u32 pgno, u8 *aData, int bCommit);

int sqlite4BtLogSnapshotOpen(BtLog*);
int sqlite4BtLogSnapshotClose(BtLog*);

int sqlite4BtLogSnapshotWrite(BtLog*);
int sqlite4BtLogSnapshotEndWrite(BtLog*);

int sqlite4BtLogSize(BtLog*);
int sqlite4BtLogCheckpoint(BtLog*);

int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame);

#ifndef NDEBUG
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
int sqlite4BtLockWriterUnlock(BtLock*);

/* Obtain and release CHECKPOINTER lock */
int sqlite4BtLockCkpt(BtLock*);
int sqlite4BtLockCkptUnlock(BtLock*);

/* Obtain and release READER locks.  */
int sqlite4BtLockReader(BtLock*, u32 *aLog, BtReadSlot *aLock);
int sqlite4BtLockReaderUnlock(BtLock*);

/* Query READER locks.  */
int sqlite4BtLockReaderQuery(BtLock*, u32*, BtReadSlot*, u32*, int*);

/* Obtain pointers to shared-memory chunks */
int sqlite4BtLockShmMap(BtLock*, int iChunk, int nByte, u8 **ppOut);







|







245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
int sqlite4BtLockWriterUnlock(BtLock*);

/* Obtain and release CHECKPOINTER lock */
int sqlite4BtLockCkpt(BtLock*);
int sqlite4BtLockCkptUnlock(BtLock*);

/* Obtain and release READER locks.  */
int sqlite4BtLockReader(BtLock*, u32 *aLog, u32 iFirst, BtReadSlot *aLock);
int sqlite4BtLockReaderUnlock(BtLock*);

/* Query READER locks.  */
int sqlite4BtLockReaderQuery(BtLock*, u32*, BtReadSlot*, u32*, int*);

/* Obtain pointers to shared-memory chunks */
int sqlite4BtLockShmMap(BtLock*, int iChunk, int nByte, u8 **ppOut);
Changes to src/bt_lock.c.
341
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
** Argument aLog points to an array of 6 frame addresses. These are the 
** first and last frames in each of log regions A, B and C. Argument 
** aLock points to the array of read-lock slots in shared memory.
*/
int sqlite4BtLockReader(
  BtLock *pLock,                  /* Lock module handle */
  u32 *aLog,                      /* Current log file topology */

  BtReadSlot *aSlot               /* Array of read-lock slots (in shmem) */
){
  int rc = SQLITE4_BUSY;          /* Return code */
  int i;                          /* Loop counter */





  /* Find the first and last log frames that this client will use. */




  u32 iLast = aLog[5];
  u32 iFirst = 0;
  for(i=0; iFirst==0 && i<3; i++){

    iFirst = aLog[i*2];
  }

  if( iFirst==0 ){
    rc = btLockLockop(pLock, BT_LOCK_READER_DBONLY, BT_LOCK_SHARED, 0);
  }else{
    int nAttempt = 100;           /* Remaining lock attempts */

    while( rc==SQLITE4_BUSY && (nAttempt--)>0 ){

      /* Try to find a slot populated with the values required. */







>




>

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


|







341
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
** Argument aLog points to an array of 6 frame addresses. These are the 
** first and last frames in each of log regions A, B and C. Argument 
** aLock points to the array of read-lock slots in shared memory.
*/
int sqlite4BtLockReader(
  BtLock *pLock,                  /* Lock module handle */
  u32 *aLog,                      /* Current log file topology */
  u32 iFirst,                     /* First log frame to lock */
  BtReadSlot *aSlot               /* Array of read-lock slots (in shmem) */
){
  int rc = SQLITE4_BUSY;          /* Return code */
  int i;                          /* Loop counter */
  u32 iLast = aLog[5];            /* Last frame to lock */

  /* If page iFirst does not appear to be part of the log at all, then
  ** the entire log has been checkpointed (and iFirst is the "next" 
  ** frame to use). Handle this case in the same way as an empty 
  ** log file.  
  **
  ** It is also possible that the iFirst value (read from the shared-memory
  ** checkpoint header) is much newer than the aLog[] values (read from
  ** the snapshot header). If so, the caller will figure it out.  */
  if( iFirst<aLog[0] && iFirst>aLog[1] 
   && iFirst<aLog[2] && iFirst>aLog[3] 
   && iFirst<aLog[4] && iFirst>aLog[5] 
  ){
    iLast = 0;
  }

  if( iLast==0 ){
    rc = btLockLockop(pLock, BT_LOCK_READER_DBONLY, BT_LOCK_SHARED, 0);
  }else{
    int nAttempt = 100;           /* Remaining lock attempts */

    while( rc==SQLITE4_BUSY && (nAttempt--)>0 ){

      /* Try to find a slot populated with the values required. */
Changes to src/bt_log.c.
1045
1046
1047
1048
1049
1050
1051

1052
1053

1054
1055


1056
1057
1058
1059
1060
1061




1062
1063
1064
1065
1066
1067
1068
1069


1070
1071

1072





1073
1074
1075



1076
1077
1078
1079
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
    if( nAttempt==0 ) rc = SQLITE4_PROTOCOL;
  }

  return rc;
}

int sqlite4BtLogSnapshotOpen(BtLog *pLog){

  int rc = SQLITE4_NOTFOUND;
  BtShmHdr shmhdr;


  while( rc==SQLITE4_NOTFOUND ){


    /* Attempt to read a copy of the BtShmHdr from shared-memory. */
    rc = btLogSnapshot(pLog, &pLog->snapshot);

    /* Take a read lock on the database */
    if( rc==SQLITE4_OK ){
      BtReadSlot *aReadlock = btLogShm(pLog)->aReadlock;




      rc = sqlite4BtLockReader(pLog->pLock, pLog->snapshot.aLog, aReadlock);
    }

    /* Check that the BtShmHdr in shared-memory has not changed. If it has,
    ** drop the read-lock and re-attempt the entire operation. */
    if( rc==SQLITE4_OK ){
      rc = btLogSnapshot(pLog, &shmhdr);
    }


    if( rc==SQLITE4_OK && memcmp(&shmhdr, &pLog->snapshot, sizeof(BtShmHdr)) ){
      sqlite4BtLockReaderUnlock(pLog->pLock);

      rc = SQLITE4_NOTFOUND;





    }
  }




  if( rc==SQLITE4_OK ){
    int iRegion;
    u32 *aLog = pLog->snapshot.aLog;
    BtShm *pShm = btLogShm(pLog);
    u32 iCkpt = pShm->ckpt.iFirstRecover;

    for(iRegion=0; iRegion<3; iRegion++){
      if( aLog[iRegion*2] ){
        if( iCkpt>=aLog[iRegion*2] && iCkpt<=aLog[iRegion*2+1] ){
          aLog[iRegion*2] = iCkpt;
          break;
        }else{
          aLog[iRegion*2] = 0;
          aLog[iRegion*2+1] = 0;
        }
      }
    }

  }

  return rc;
}

int sqlite4BtLogSnapshotClose(BtLog *pLog){
  sqlite4BtLockReaderUnlock(pLog->pLock);
  return SQLITE4_OK;
}






int sqlite4BtLogSnapshotWritable(BtLog *pLog){































































  return 1;




}

static void btLogMergeInplace(
  u32 *aLeft, int nLeft,          /* Left hand input array */
  u32 *aRight, int nRight,        /* Right hand input array */
  u32 *aSpace,                    /* Temporary space */
  int *pnOut                      /* OUT: Size of aLeft[] after merge */







>


>


>
>





|
>
>
>
>
|







>
>
|
<
>
|
>
>
>
>
>



>
>
>


<
<
<
<


|
|







<










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







1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
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
1141
1142
1143
1144
1145
1146
1147
1148
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
    if( nAttempt==0 ) rc = SQLITE4_PROTOCOL;
  }

  return rc;
}

int sqlite4BtLogSnapshotOpen(BtLog *pLog){
  u32 *aLog = pLog->snapshot.aLog;
  int rc = SQLITE4_NOTFOUND;
  BtShmHdr shmhdr;
  u32 iFirstRead = 0;

  while( rc==SQLITE4_NOTFOUND ){
    BtShm *pShm;

    /* Attempt to read a copy of the BtShmHdr from shared-memory. */
    rc = btLogSnapshot(pLog, &pLog->snapshot);

    /* Take a read lock on the database */
    if( rc==SQLITE4_OK ){
      BtReadSlot *aReadlock;
      pShm = btLogShm(pLog);

      aReadlock = pShm->aReadlock;
      iFirstRead = pShm->ckpt.iFirstRead;
      rc = sqlite4BtLockReader(pLog->pLock, aLog, iFirstRead, aReadlock);
    }

    /* Check that the BtShmHdr in shared-memory has not changed. If it has,
    ** drop the read-lock and re-attempt the entire operation. */
    if( rc==SQLITE4_OK ){
      rc = btLogSnapshot(pLog, &shmhdr);
    }
    if( rc==SQLITE4_OK ){
      if( iFirstRead!=pShm->ckpt.iFirstRead 
       || memcmp(&shmhdr, &pLog->snapshot, sizeof(BtShmHdr)) 

      ){
        rc = SQLITE4_NOTFOUND;
      }
    }
    
    if( rc!=SQLITE4_OK ){
      sqlite4BtLockReaderUnlock(pLog->pLock);
    }
  }

  /* If a snapshot was successfully read, adjust it so that the aLog[] 
  ** array specifies that no frames before iFirstRead is ever read from 
  ** the log file.  */
  if( rc==SQLITE4_OK ){
    int iRegion;




    for(iRegion=0; iRegion<3; iRegion++){
      if( aLog[iRegion*2] ){
        if( iFirstRead>=aLog[iRegion*2] && iFirstRead<=aLog[iRegion*2+1] ){
          aLog[iRegion*2] = iFirstRead;
          break;
        }else{
          aLog[iRegion*2] = 0;
          aLog[iRegion*2+1] = 0;
        }
      }
    }

  }

  return rc;
}

int sqlite4BtLogSnapshotClose(BtLog *pLog){
  sqlite4BtLockReaderUnlock(pLog->pLock);
  return SQLITE4_OK;
}

/*
** The log handle has already successfully opened a read-only snapshot
** when this function is called. This function attempts to upgrade it
** to a read-write snapshot. 
*/
int sqlite4BtLogSnapshotWrite(BtLog *pLog){
  BtLock *pLock = pLog->pLock;
  int rc;

  rc = sqlite4BtLockWriter(pLock);
  if( rc==SQLITE4_OK ){
    BtShm *pShm = btLogShm(pLog);
    BtShmHdr shmhdr;

    /* Check if this connection is currently reading from the latest
    ** database snapshot. Set rc to SQLITE4_BUSY if it is not.  */
    rc = btLogSnapshot(pLog, &shmhdr);
    if( rc==SQLITE4_OK && memcmp(&pShm->hdr1, &pShm->hdr2, sizeof(BtShmHdr)) ){
      memcpy(&pShm->hdr1, &shmhdr, sizeof(BtShmHdr));
      memcpy(&pShm->hdr2, &shmhdr, sizeof(BtShmHdr));
    }
    if( rc==SQLITE4_OK && pLog->snapshot.iNextFrame!=shmhdr.iNextFrame ){
      rc = SQLITE4_BUSY;
    }

    /* Currently, pLog->snapshot.aLog[] contains a map of the frames
    ** that this connection was required to consider in order to read
    ** from the read-only snapshot. The following block edits this so
    ** that it contains a map of all frames that are currently in use
    ** by any reader, or may be used by any future reader or recovery
    ** process.  */
    if( rc==SQLITE4_OK ){
      u32 *aLog = shmhdr.aLog;
      u32 iRecover = pShm->ckpt.iFirstRecover;
      u32 iRead = 0;

      rc = sqlite4BtLockReaderQuery(pLock, aLog, pShm->aReadlock, &iRead, 0);

      if( rc==SQLITE4_OK ){
        /* Now "trim" the snapshot so that it accesses nothing earlier than
        ** either iRecover or iRead (whichever occurs first in the log). */
        u32 iTrim = iRecover;
        if( iRead ){
          int iIdxRead = sqlite4BtLogFrameToIdx(aLog, iRead);
          if( sqlite4BtLogFrameToIdx(aLog, iRecover)>iIdxRead ) iTrim = iRead;
        }

        if( iTrim==0 || iTrim==shmhdr.iNextFrame || btLogIsEmpty(pLog) ){
          memset(aLog, 0, sizeof(u32)*6);
        }else{
          int i;
          for(i=0; i<3; i++){
            int bIn = (aLog[2*i]<=iTrim && iTrim<=aLog[2*i+1]);
            if( bIn ){
              aLog[2*i] = iTrim;
              break;
            }else{
              aLog[2*i] = aLog[2*i+1] = 0;
            }
          }
        }
      }

      if( rc==SQLITE4_OK ){
        memcpy(pLog->snapshot.aLog, aLog, sizeof(u32)*6);
      }
    }
  }

  return rc;
}

int sqlite4BtLogSnapshotEndWrite(BtLog *pLog){
  return sqlite4BtLockWriterUnlock(pLog->pLock);
}

static void btLogMergeInplace(
  u32 *aLeft, int nLeft,          /* Left hand input array */
  u32 *aRight, int nRight,        /* Right hand input array */
  u32 *aSpace,                    /* Temporary space */
  int *pnOut                      /* OUT: Size of aLeft[] after merge */
1362
1363
1364
1365
1366
1367
1368
1369
1370

1371
1372
1373
1374
1375
    ** writers that it is now safe to recycle pages before this point
    ** (assuming all live readers are cleared).  */
    if( rc==SQLITE4_OK ){
      pShm->ckpt.iFirstRecover = iFirstRead;
      pVfs->xShmBarrier(pLog->pFd);
    }

    /* Free the buffer and drop the checkpointer lock */
    sqlite4_free(pLock->pEnv, aBuf);

    sqlite4BtLockCkptUnlock(pLog->pLock);
  }
  return rc;
}








|

>





1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
    ** writers that it is now safe to recycle pages before this point
    ** (assuming all live readers are cleared).  */
    if( rc==SQLITE4_OK ){
      pShm->ckpt.iFirstRecover = iFirstRead;
      pVfs->xShmBarrier(pLog->pFd);
    }

    /* Free buffers and drop the checkpointer lock */
    sqlite4_free(pLock->pEnv, aBuf);
    sqlite4_free(pLock->pEnv, aPgno);
    sqlite4BtLockCkptUnlock(pLog->pLock);
  }
  return rc;
}

Changes to src/bt_pager.c.
1
2
3
4
5
6
7
8
/*;
** 2013 September 14
**
** 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.
|







1
2
3
4
5
6
7
8
/*
** 2013 September 14
**
** 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.
347
348
349
350
351
352
353









354
355
356
357
358
359
360
  if( rc==SQLITE4_OK ){
    /* If the read transaction was successfully opened, the transaction 
    ** level is now 1.  */
    p->iTransactionLevel = 1;
  }
  return rc;
}










static int btCloseReadTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==0 );
  rc = sqlite4BtLogSnapshotClose(p->pLog);

  /* Purge the page cache. */







>
>
>
>
>
>
>
>
>







347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  if( rc==SQLITE4_OK ){
    /* If the read transaction was successfully opened, the transaction 
    ** level is now 1.  */
    p->iTransactionLevel = 1;
  }
  return rc;
}

static int btOpenWriteTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==1 );
  assert( p->btl.pFd );

  rc = sqlite4BtLogSnapshotWrite(p->pLog);
  return rc;
}

static int btCloseReadTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==0 );
  rc = sqlite4BtLogSnapshotClose(p->pLog);

  /* Purge the page cache. */
384
385
386
387
388
389
390

391

392
393
394
395
396
397
398
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      int bCommit = (pNext==0);
      rc = sqlite4BtLogWrite(p->pLog, pPg->pgno, pPg->aData, bCommit);
    }
  }
  p->pDirty = 0;



  if( rc==SQLITE4_OK ){
    rc = p->btl.pVfs->xWrite(p->btl.pFd, 0, (void*)&p->dbhdr, sizeof(BtDbhdr));
  }

  if( p->nAutoCkpt && sqlite4BtLogSize(p->pLog)>=p->nAutoCkpt ){
    p->bDoAutoCkpt = 1;
  }







>

>







393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      int bCommit = (pNext==0);
      rc = sqlite4BtLogWrite(p->pLog, pPg->pgno, pPg->aData, bCommit);
    }
  }
  p->pDirty = 0;
  sqlite4BtLogSnapshotEndWrite(p->pLog);

  /* TODO: Fix this. */
  if( rc==SQLITE4_OK ){
    rc = p->btl.pVfs->xWrite(p->btl.pFd, 0, (void*)&p->dbhdr, sizeof(BtDbhdr));
  }

  if( p->nAutoCkpt && sqlite4BtLogSize(p->pLog)>=p->nAutoCkpt ){
    p->bDoAutoCkpt = 1;
  }
465
466
467
468
469
470
471

472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488





489
490
491
492
493
494
495
      btHashRemove(p, pPg);
      btFreePage(p, pPg);
    }else if( rc==SQLITE4_OK && (pPg->pgno<=p->dbhdr.nPg) ){
      rc = btLoadPageData(p, pPg);
    }
  }
  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 );

  if( p->iTransactionLevel<iLevel ){
    /* Open a read transaction if one is not already open */
    if( p->iTransactionLevel==0 ){
      rc = btOpenReadTransaction(p);
    }






    if( rc==SQLITE4_OK ){
      assert( p->iTransactionLevel>=1 && iLevel>=p->iTransactionLevel );
      p->iTransactionLevel = iLevel;
    }
  }








>

















>
>
>
>
>







476
477
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
510
511
512
      btHashRemove(p, pPg);
      btFreePage(p, pPg);
    }else if( rc==SQLITE4_OK && (pPg->pgno<=p->dbhdr.nPg) ){
      rc = btLoadPageData(p, pPg);
    }
  }
  p->pDirty = 0;
  sqlite4BtLogSnapshotEndWrite(p->pLog);

  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 );

  if( p->iTransactionLevel<iLevel ){
    /* Open a read transaction if one is not already open */
    if( p->iTransactionLevel==0 ){
      rc = btOpenReadTransaction(p);
    }

    /* Open a write transaction if one is required */
    if( rc==SQLITE4_OK && p->iTransactionLevel<2 && iLevel>=2 ){
      rc = btOpenWriteTransaction(p);
    }

    if( rc==SQLITE4_OK ){
      assert( p->iTransactionLevel>=1 && iLevel>=p->iTransactionLevel );
      p->iTransactionLevel = iLevel;
    }
  }

Changes to test/simple3.test.
66
67
68
69
70
71
72



73
74
75
76
77
78
79
do_execsql_test 1.8 {
  DELETE FROM t1 WHERE 1;
}

do_execsql_test 1.9 {
  SELECT * FROM t1;
  DROP TABLE t1;



  SELECT * FROM sqlite_kvstore;
}

#--------------------------------------------------------------------------

set val [string repeat x 200]
do_execsql_test 2.0 {







>
>
>







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
do_execsql_test 1.8 {
  DELETE FROM t1 WHERE 1;
}

do_execsql_test 1.9 {
  SELECT * FROM t1;
  DROP TABLE t1;
}

do_execsql_test 1.10 {
  SELECT * FROM sqlite_kvstore;
}

#--------------------------------------------------------------------------

set val [string repeat x 200]
do_execsql_test 2.0 {