/ Check-in [b5df5ac0]
Login

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

Overview
Comment:Permit read operations to continue after a ROLLBACK as long as the schema does not change.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b5df5ac0529f7b0d1e880a7f4a307e7d77b7fa6c
User & Date: drh 2014-11-11 14:59:31
References
2014-11-11
22:55
Enhance ROLLBACK so that pending queries are allowed to continue as long as the schema does not change. This is a cherry-pick of check-in [b5df5ac052]. check-in: d4b2d5d0 user: drh tags: branch-3.8.7
Context
2014-11-11
22:55
Enhance ROLLBACK so that pending queries are allowed to continue as long as the schema does not change. This is a cherry-pick of check-in [b5df5ac052]. check-in: d4b2d5d0 user: drh tags: branch-3.8.7
16:11
Add tests for sqlite3_blob_bytes(). check-in: a066a383 user: dan tags: trunk
14:59
Permit read operations to continue after a ROLLBACK as long as the schema does not change. check-in: b5df5ac0 user: drh tags: trunk
12:20
Add new test file e_blobclose.test, containing tests for sqlite3_blob_close(). check-in: 5a1eac24 user: dan tags: trunk
01:33
Experimental changes that permit read operations to continue after a ROLLBACK, as long as the schema is unchanged. Closed-Leaf check-in: fa6e6a9a user: drh tags: read-after-rollback
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/backup.c.

   603    603       while( *pp!=p ){
   604    604         pp = &(*pp)->pNext;
   605    605       }
   606    606       *pp = p->pNext;
   607    607     }
   608    608   
   609    609     /* If a transaction is still open on the Btree, roll it back. */
   610         -  sqlite3BtreeRollback(p->pDest, SQLITE_OK);
          610  +  sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0);
   611    611   
   612    612     /* Set the error code of the destination database handle. */
   613    613     rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
   614    614     if( p->pDestDb ){
   615    615       sqlite3Error(p->pDestDb, rc);
   616    616   
   617    617       /* Exit the mutexes and free the backup context structure. */

Changes to src/btree.c.

  2209   2209       }
  2210   2210     }
  2211   2211   
  2212   2212     /* Rollback any active transaction and free the handle structure.
  2213   2213     ** The call to sqlite3BtreeRollback() drops any table-locks held by
  2214   2214     ** this handle.
  2215   2215     */
  2216         -  sqlite3BtreeRollback(p, SQLITE_OK);
         2216  +  sqlite3BtreeRollback(p, SQLITE_OK, 0);
  2217   2217     sqlite3BtreeLeave(p);
  2218   2218   
  2219   2219     /* If there are still other outstanding references to the shared-btree
  2220   2220     ** structure, return now. The remainder of this procedure cleans 
  2221   2221     ** up the shared-btree.
  2222   2222     */
  2223   2223     assert( p->wantToLock==0 && p->locked==0 );
................................................................................
  3502   3502     }
  3503   3503     sqlite3BtreeLeave(p);
  3504   3504     return rc;
  3505   3505   }
  3506   3506   
  3507   3507   /*
  3508   3508   ** This routine sets the state to CURSOR_FAULT and the error
  3509         -** code to errCode for every cursor on BtShared that pBtree
  3510         -** references.
         3509  +** code to errCode for every cursor on any BtShared that pBtree
         3510  +** references.  Or if the writeOnly flag is set to 1, then only
         3511  +** trip write cursors and leave read cursors unchanged.
  3511   3512   **
  3512         -** Every cursor is tripped, including cursors that belong
  3513         -** to other database connections that happen to be sharing
  3514         -** the cache with pBtree.
         3513  +** Every cursor is a candidate to be tripped, including cursors
         3514  +** that belong to other database connections that happen to be
         3515  +** sharing the cache with pBtree.
  3515   3516   **
  3516         -** This routine gets called when a rollback occurs.
  3517         -** All cursors using the same cache must be tripped
  3518         -** to prevent them from trying to use the btree after
  3519         -** the rollback.  The rollback may have deleted tables
  3520         -** or moved root pages, so it is not sufficient to
  3521         -** save the state of the cursor.  The cursor must be
  3522         -** invalidated.
         3517  +** This routine gets called when a rollback occurs.  The writeOnly
         3518  +** flag is set to 1 if the transaction did not make any schema
         3519  +** changes, in which case the read cursors can continue operating.
         3520  +** If schema changes did occur in the transaction, then both read
         3521  +** and write cursors must both be tripped.
  3523   3522   */
  3524         -void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
         3523  +void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
  3525   3524     BtCursor *p;
         3525  +  assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
  3526   3526     if( pBtree==0 ) return;
  3527   3527     sqlite3BtreeEnter(pBtree);
  3528   3528     for(p=pBtree->pBt->pCursor; p; p=p->pNext){
  3529   3529       int i;
         3530  +    if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ) continue;
  3530   3531       sqlite3BtreeClearCursor(p);
  3531   3532       p->eState = CURSOR_FAULT;
  3532   3533       p->skipNext = errCode;
  3533   3534       for(i=0; i<=p->iPage; i++){
  3534   3535         releasePage(p->apPage[i]);
  3535   3536         p->apPage[i] = 0;
  3536   3537       }
  3537   3538     }
  3538   3539     sqlite3BtreeLeave(pBtree);
  3539   3540   }
  3540   3541   
  3541   3542   /*
  3542         -** Rollback the transaction in progress.  All cursors will be
  3543         -** invalided by this operation.  Any attempt to use a cursor
  3544         -** that was open at the beginning of this operation will result
  3545         -** in an error.
         3543  +** Rollback the transaction in progress.
         3544  +**
         3545  +** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
         3546  +** Only write cursors are tripped if writeOnly is true but all cursors are
         3547  +** tripped if writeOnly is false.  Any attempt to use
         3548  +** a tripped cursor will result in an error.
  3546   3549   **
  3547   3550   ** This will release the write lock on the database file.  If there
  3548   3551   ** are no active cursors, it also releases the read lock.
  3549   3552   */
  3550         -int sqlite3BtreeRollback(Btree *p, int tripCode){
         3553  +int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
  3551   3554     int rc;
  3552   3555     BtShared *pBt = p->pBt;
  3553   3556     MemPage *pPage1;
  3554   3557   
         3558  +  assert( writeOnly==1 || writeOnly==0 );
         3559  +  assert( tripCode==SQLITE_ABORT_ROLLBACK || tripCode==SQLITE_OK );
  3555   3560     sqlite3BtreeEnter(p);
  3556   3561     if( tripCode==SQLITE_OK ){
  3557   3562       rc = tripCode = saveAllCursors(pBt, 0, 0);
         3563  +    if( rc ) writeOnly = 0;
  3558   3564     }else{
  3559   3565       rc = SQLITE_OK;
  3560   3566     }
  3561   3567     if( tripCode ){
  3562         -    sqlite3BtreeTripAllCursors(p, tripCode);
         3568  +    sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
  3563   3569     }
  3564   3570     btreeIntegrity(p);
  3565   3571   
  3566   3572     if( p->inTrans==TRANS_WRITE ){
  3567   3573       int rc2;
  3568   3574   
  3569   3575       assert( TRANS_WRITE==pBt->inTransaction );

Changes to src/btree.h.

    79     79   #endif
    80     80   int sqlite3BtreeSetAutoVacuum(Btree *, int);
    81     81   int sqlite3BtreeGetAutoVacuum(Btree *);
    82     82   int sqlite3BtreeBeginTrans(Btree*,int);
    83     83   int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
    84     84   int sqlite3BtreeCommitPhaseTwo(Btree*, int);
    85     85   int sqlite3BtreeCommit(Btree*);
    86         -int sqlite3BtreeRollback(Btree*,int);
           86  +int sqlite3BtreeRollback(Btree*,int,int);
    87     87   int sqlite3BtreeBeginStmt(Btree*,int);
    88     88   int sqlite3BtreeCreateTable(Btree*, int*, int flags);
    89     89   int sqlite3BtreeIsInTrans(Btree*);
    90     90   int sqlite3BtreeIsInReadTrans(Btree*);
    91     91   int sqlite3BtreeIsInBackup(Btree*);
    92     92   void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
    93     93   int sqlite3BtreeSchemaLocked(Btree *pBtree);
................................................................................
   112    112   */
   113    113   #define BTREE_INTKEY     1    /* Table has only 64-bit signed integer keys */
   114    114   #define BTREE_BLOBKEY    2    /* Table has keys only - no data */
   115    115   
   116    116   int sqlite3BtreeDropTable(Btree*, int, int*);
   117    117   int sqlite3BtreeClearTable(Btree*, int, int*);
   118    118   int sqlite3BtreeClearTableOfCursor(BtCursor*);
   119         -void sqlite3BtreeTripAllCursors(Btree*, int);
          119  +void sqlite3BtreeTripAllCursors(Btree*, int, int);
   120    120   
   121    121   void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
   122    122   int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
   123    123   
   124    124   int sqlite3BtreeNewDb(Btree *p);
   125    125   
   126    126   /*

Changes to src/main.c.

  1107   1107       sqlite3_free(db->lookaside.pStart);
  1108   1108     }
  1109   1109     sqlite3_free(db);
  1110   1110   }
  1111   1111   
  1112   1112   /*
  1113   1113   ** Rollback all database files.  If tripCode is not SQLITE_OK, then
  1114         -** any open cursors are invalidated ("tripped" - as in "tripping a circuit
         1114  +** any write cursors are invalidated ("tripped" - as in "tripping a circuit
  1115   1115   ** breaker") and made to return tripCode if there are any further
  1116         -** attempts to use that cursor.
         1116  +** attempts to use that cursor.  Read cursors remain open and valid
         1117  +** but are "saved" in case the table pages are moved around.
  1117   1118   */
  1118   1119   void sqlite3RollbackAll(sqlite3 *db, int tripCode){
  1119   1120     int i;
  1120   1121     int inTrans = 0;
         1122  +  int schemaChange;
  1121   1123     assert( sqlite3_mutex_held(db->mutex) );
  1122   1124     sqlite3BeginBenignMalloc();
  1123   1125   
  1124   1126     /* Obtain all b-tree mutexes before making any calls to BtreeRollback(). 
  1125   1127     ** This is important in case the transaction being rolled back has
  1126   1128     ** modified the database schema. If the b-tree mutexes are not taken
  1127   1129     ** here, then another shared-cache connection might sneak in between
  1128   1130     ** the database rollback and schema reset, which can cause false
  1129   1131     ** corruption reports in some cases.  */
  1130   1132     sqlite3BtreeEnterAll(db);
         1133  +  schemaChange = (db->flags & SQLITE_InternChanges)!=0 && db->init.busy==0;
  1131   1134   
  1132   1135     for(i=0; i<db->nDb; i++){
  1133   1136       Btree *p = db->aDb[i].pBt;
  1134   1137       if( p ){
  1135   1138         if( sqlite3BtreeIsInTrans(p) ){
  1136   1139           inTrans = 1;
  1137   1140         }
  1138         -      sqlite3BtreeRollback(p, tripCode);
         1141  +      sqlite3BtreeRollback(p, tripCode, !schemaChange);
  1139   1142       }
  1140   1143     }
  1141   1144     sqlite3VtabRollback(db);
  1142   1145     sqlite3EndBenignMalloc();
  1143   1146   
  1144   1147     if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){
  1145   1148       sqlite3ExpirePreparedStatements(db);

Changes to src/vdbe.c.

  2821   2821             db->autoCommit = 0;
  2822   2822             p->rc = rc = SQLITE_BUSY;
  2823   2823             goto vdbe_return;
  2824   2824           }
  2825   2825           db->isTransactionSavepoint = 0;
  2826   2826           rc = p->rc;
  2827   2827         }else{
         2828  +        int isSchemaChange;
  2828   2829           iSavepoint = db->nSavepoint - iSavepoint - 1;
  2829   2830           if( p1==SAVEPOINT_ROLLBACK ){
         2831  +          isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
  2830   2832             for(ii=0; ii<db->nDb; ii++){
  2831         -            sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT);
         2833  +            sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT,
         2834  +                                       isSchemaChange==0);
  2832   2835             }
         2836  +        }else{
         2837  +          isSchemaChange = 0;
  2833   2838           }
  2834   2839           for(ii=0; ii<db->nDb; ii++){
  2835   2840             rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
  2836   2841             if( rc!=SQLITE_OK ){
  2837   2842               goto abort_due_to_error;
  2838   2843             }
  2839   2844           }
  2840         -        if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
         2845  +        if( isSchemaChange ){
  2841   2846             sqlite3ExpirePreparedStatements(db);
  2842   2847             sqlite3ResetAllSchemasOfConnection(db);
  2843   2848             db->flags = (db->flags | SQLITE_InternChanges);
  2844   2849           }
  2845   2850         }
  2846   2851     
  2847   2852         /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all 
................................................................................
  3230   3235     assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 );
  3231   3236     assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 );
  3232   3237     assert( p->bIsReader );
  3233   3238     assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
  3234   3239             || p->readOnly==0 );
  3235   3240   
  3236   3241     if( p->expired ){
  3237         -    rc = SQLITE_ABORT;
         3242  +    rc = SQLITE_ABORT_ROLLBACK;
  3238   3243       break;
  3239   3244     }
  3240   3245   
  3241   3246     nField = 0;
  3242   3247     pKeyInfo = 0;
  3243   3248     p2 = pOp->p2;
  3244   3249     iDb = pOp->p3;

Changes to src/wal.c.

  2502   2502     
  2503   2503       /* Restore the clients cache of the wal-index header to the state it
  2504   2504       ** was in before the client began writing to the database. 
  2505   2505       */
  2506   2506       memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
  2507   2507   
  2508   2508       for(iFrame=pWal->hdr.mxFrame+1; 
  2509         -        ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; 
         2509  +        rc==SQLITE_OK && iFrame<=iMax; 
  2510   2510           iFrame++
  2511   2511       ){
  2512   2512         /* This call cannot fail. Unless the page for which the page number
  2513   2513         ** is passed as the second argument is (a) in the cache and 
  2514   2514         ** (b) has an outstanding reference, then xUndo is either a no-op
  2515   2515         ** (if (a) is false) or simply expels the page from the cache (if (b)
  2516   2516         ** is false).
................................................................................
  2521   2521         ** committed. As a result, the call to xUndo may not fail.
  2522   2522         */
  2523   2523         assert( walFramePgno(pWal, iFrame)!=1 );
  2524   2524         rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
  2525   2525       }
  2526   2526       if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
  2527   2527     }
  2528         -  assert( rc==SQLITE_OK );
  2529   2528     return rc;
  2530   2529   }
  2531   2530   
  2532   2531   /* 
  2533   2532   ** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 
  2534   2533   ** values. This function populates the array with values required to 
  2535   2534   ** "rollback" the write position of the WAL handle back to the current 

Changes to test/capi3.test.

   908    908     }
   909    909   } {0 {}}
   910    910   do_test capi3-11.9.3 {
   911    911     sqlite3_get_autocommit $DB
   912    912   } 1
   913    913   do_test capi3-11.10 {
   914    914     sqlite3_step $STMT
   915         -} {SQLITE_ERROR}
          915  +} {SQLITE_ROW}
   916    916   ifcapable !autoreset {
   917    917     # If SQLITE_OMIT_AUTORESET is defined, then the statement must be
   918    918     # reset() before it can be passed to step() again.
   919    919     do_test capi3-11.11a { sqlite3_step $STMT } {SQLITE_MISUSE}
   920    920     do_test capi3-11.11b { sqlite3_reset $STMT } {SQLITE_ABORT}
   921    921   }
   922    922   do_test capi3-11.11 {
   923    923     sqlite3_step $STMT
   924         -} {SQLITE_ROW}
          924  +} {SQLITE_DONE}
   925    925   do_test capi3-11.12 {
   926    926     sqlite3_step $STMT
   927    927     sqlite3_step $STMT
   928         -} {SQLITE_DONE}
          928  +} {SQLITE_ROW}
   929    929   do_test capi3-11.13 {
   930    930     sqlite3_finalize $STMT
   931    931   } {SQLITE_OK}
   932    932   do_test capi3-11.14 {
   933    933     execsql {
   934    934       SELECT a FROM t2;
   935    935     }

Changes to test/capi3c.test.

   859    859     }
   860    860   } {0 {}}
   861    861   do_test capi3c-11.9.3 {
   862    862     sqlite3_get_autocommit $DB
   863    863   } 1
   864    864   do_test capi3c-11.10 {
   865    865     sqlite3_step $STMT
   866         -} {SQLITE_ABORT}
          866  +} {SQLITE_ROW}
   867    867   ifcapable !autoreset {
   868    868     # If SQLITE_OMIT_AUTORESET is defined, then the statement must be
   869    869     # reset() before it can be passed to step() again.
   870    870     do_test capi3-11.11a { sqlite3_step $STMT } {SQLITE_MISUSE}
   871    871     do_test capi3-11.11b { sqlite3_reset $STMT } {SQLITE_ABORT}
   872    872   }
   873    873   do_test capi3c-11.11 {
   874    874     sqlite3_step $STMT
   875         -} {SQLITE_ROW}
          875  +} {SQLITE_DONE}
   876    876   do_test capi3c-11.12 {
   877    877     sqlite3_step $STMT
   878    878     sqlite3_step $STMT
   879         -} {SQLITE_DONE}
          879  +} {SQLITE_ROW}
   880    880   do_test capi3c-11.13 {
   881    881     sqlite3_finalize $STMT
   882    882   } {SQLITE_OK}
   883    883   do_test capi3c-11.14 {
   884    884     execsql {
   885    885       SELECT a FROM t2;
   886    886     }

Changes to test/misc8.test.

    30     30   do_execsql_test misc8-1.3 {
    31     31     INSERT INTO t1 VALUES(7,null,9);
    32     32     SELECT eval('SELECT * FROM t1 ORDER BY a',',');
    33     33   } {1,2,3,4,5,6,7,,9}
    34     34   do_catchsql_test misc8-1.4 {
    35     35     BEGIN;
    36     36     INSERT INTO t1 VALUES(10,11,12);
    37         -  SELECT coalesce(b, eval('ROLLBACK')) FROM t1 ORDER BY a;
    38         -} {1 {abort due to ROLLBACK}}
           37  +  SELECT a, coalesce(b, eval('ROLLBACK; SELECT ''bam'';')), c
           38  +   FROM t1 ORDER BY a;
           39  +} {0 {1 2 3 4 5 6 7 bam 9}}
    39     40   do_catchsql_test misc8-1.5 {
    40     41     INSERT INTO t1 VALUES(10,11,12);
    41     42     SELECT a, coalesce(b, eval('SELECT ''bam''')), c
    42     43       FROM t1
    43     44      ORDER BY rowid;
    44     45   } {0 {1 2 3 4 5 6 7 bam 9 10 11 12}}
    45     46   do_catchsql_test misc8-1.6 {
    46     47     SELECT a, coalesce(b, eval('DELETE FROM t1; SELECT ''bam''')), c
    47     48       FROM t1
    48     49      ORDER BY rowid;
    49     50   } {0 {1 2 3 4 5 6 7 bam {}}}
           51  +do_catchsql_test misc8-1.7 {
           52  +  INSERT INTO t1 VALUES(1,2,3),(4,5,6),(7,null,9);
           53  +  BEGIN;
           54  +  CREATE TABLE t2(x);
           55  +  SELECT a, coalesce(b, eval('ROLLBACK; SELECT ''bam''')), c
           56  +    FROM t1
           57  +   ORDER BY rowid;
           58  +} {1 {abort due to ROLLBACK}}
    50     59   
    51     60   
    52     61   finish_test

Changes to test/rollback.test.

    56     56       }
    57     57     } {1 {UNIQUE constraint failed: t3.a}}
    58     58     
    59     59     # Try to continue with the SELECT statement
    60     60     #
    61     61     do_test rollback-1.5 {
    62     62       sqlite3_step $STMT
    63         -  } {SQLITE_ERROR}
           63  +  } {SQLITE_ROW}
    64     64   
    65     65     # Restart the SELECT statement
    66     66     #
    67         -  do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_ABORT}
           67  +  do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_OK}
    68     68   } else {
    69     69     do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_OK}
    70     70   }
    71     71   
    72     72   do_test rollback-1.7 {
    73     73     sqlite3_step $STMT
    74     74   } {SQLITE_ROW}

Changes to test/savepoint.test.

   311    311     } {0 {hellontyeight character blob}}
   312    312     do_test savepoint-5.3.2.2 {
   313    313       catchsql {ROLLBACK TO def}
   314    314     } {0 {}}
   315    315     do_test savepoint-5.3.2.3 {
   316    316       set rc [catch {seek $fd 0; read $fd} res]
   317    317       set rc
   318         -  } {1}
          318  +  } {0}
   319    319     do_test savepoint-5.3.3 {
   320    320       catchsql  {RELEASE def}
   321    321     } {0 {}}
   322    322     do_test savepoint-5.3.4 {
   323    323       close $fd
   324    324       execsql  {savepoint def}
   325    325       set fd [db incrblob blobs x 1]

Changes to test/savepoint7.test.

    26     26       INSERT INTO t1 VALUES(4,5,6);
    27     27       INSERT INTO t1 VALUES(7,8,9);
    28     28       SAVEPOINT x1;
    29     29     }
    30     30     db eval {SELECT * FROM t1} {
    31     31       db eval {
    32     32         SAVEPOINT x2;
           33  +      CREATE TABLE IF NOT EXISTS t3(xyz);
    33     34         INSERT INTO t2 VALUES($a,$b,$c);
    34     35         RELEASE x2;
    35     36       }
    36     37     }
    37     38     db eval {SELECT * FROM t2; RELEASE x1}
    38     39   } {1 2 3 4 5 6 7 8 9}
    39     40   
................................................................................
    42     43     db eval {SELECT * FROM t1} {
    43     44       db eval {
    44     45         SAVEPOINT x2;
    45     46         INSERT INTO t2 VALUES($a,$b,$c);
    46     47         RELEASE x2;
    47     48       }
    48     49     }
    49         -  db eval {SELECT * FROM t2}
           50  +  db eval {SELECT * FROM t2;}
    50     51   } {1 2 3 4 5 6 7 8 9}
    51     52   
    52     53   do_test savepoint7-1.3 {
    53     54     db eval {DELETE FROM t2; BEGIN;}
    54     55     db eval {SELECT * FROM t1} {
    55     56       db eval {
    56     57         SAVEPOINT x2;
................................................................................
    61     62     db eval {SELECT * FROM t2; ROLLBACK;}
    62     63   } {1 2 3 4 5 6 7 8 9}
    63     64   
    64     65   # However, a ROLLBACK of an inner savepoint will abort all queries, including
    65     66   # queries in outer contexts.
    66     67   #
    67     68   do_test savepoint7-2.1 {
    68         -  db eval {DELETE FROM t2; SAVEPOINT x1;}
           69  +  db eval {DELETE FROM t2; SAVEPOINT x1; CREATE TABLE t4(abc);}
    69     70     set rc [catch {
    70     71       db eval {SELECT * FROM t1} {
    71     72         db eval {
    72     73           SAVEPOINT x2;
    73     74           INSERT INTO t2 VALUES($a,$b,$c);
    74     75           ROLLBACK TO x2;
    75     76         }
................................................................................
    81     82   
    82     83   do_test savepoint7-2.2 {
    83     84     db eval {DELETE FROM t2;}
    84     85     set rc [catch {
    85     86       db eval {SELECT * FROM t1} {
    86     87         db eval {
    87     88           SAVEPOINT x2;
           89  +        CREATE TABLE t5(pqr);
    88     90           INSERT INTO t2 VALUES($a,$b,$c);
    89     91           ROLLBACK TO x2;
    90     92         }
    91     93       }
    92     94     } msg]
    93     95     list $rc $msg [db eval {SELECT * FROM t2}]
    94     96   } {1 {callback requested query abort} {}}
    95     97   
    96     98   finish_test

Changes to test/tkt-f777251dc7a.test.

    34     34   
    35     35   proc force_rollback {} {
    36     36     catch {db eval {INSERT OR ROLLBACK INTO t1 VALUES(1)}}
    37     37   }
    38     38   db function force_rollback force_rollback
    39     39   
    40     40   do_test tkt-f7772-1.2 {
           41  +breakpoint
    41     42     catchsql {
    42     43       BEGIN IMMEDIATE;
           44  +    CREATE TABLE xyzzy(abc);
    43     45       SELECT x, force_rollback(), EXISTS(SELECT 1 FROM t3 WHERE w=x) FROM t2;
    44     46     }
    45     47   } {1 {abort due to ROLLBACK}}
    46     48   do_test tkt-f7772-1.3 {
    47     49     sqlite3_get_autocommit db
    48     50   } {1}
    49     51   
................................................................................
    63     65     execsql {
    64     66       BEGIN IMMEDIATE;
    65     67       CREATE TEMP TABLE t3(w, z);
    66     68     }
    67     69     catchsql {
    68     70       SELECT x, force_rollback(), EXISTS(SELECT 1 FROM t3 WHERE w=x) FROM t2
    69     71     }
    70         -} {1 {callback requested query abort}}
           72  +} {1 {abort due to ROLLBACK}}
    71     73   do_test tkt-f7772-2.3 {
    72     74     sqlite3_get_autocommit db
    73     75   } {1}
    74     76   
    75     77   do_test tkt-f7772-3.1 {
    76     78     execsql {
    77     79       DROP TABLE IF EXISTS t1;

Changes to test/trans3.test.

    48     48   do_test trans3-1.3.1 {
    49     49     sqlite3_get_autocommit db
    50     50   } 1
    51     51   do_test trans3-1.4 {
    52     52     db eval {SELECT * FROM t1}
    53     53   } {1 2 3 4}
    54     54   do_test trans3-1.5 {
    55         -  db eval BEGIN
           55  +  db eval {BEGIN; CREATE TABLE xyzzy(abc);}
    56     56     db eval {INSERT INTO t1 VALUES(5);}
    57     57     set ::ecode {}
    58     58     set x [catch {
    59     59        db eval {SELECT * FROM t1} {
    60     60           if {[catch {db eval ROLLBACK} errmsg]} {
    61     61              set ::ecode [sqlite3_extended_errcode db]
    62     62              error $errmsg