/ Check-in [dd03a280]
Login

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

Overview
Comment:When a transaction or savepoint rollback occurs, save the positions of all open read-cursors so that they can be restored following the rollback operation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: dd03a2802f3f276525f3cef9a93f825dd8606626
User & Date: dan 2014-11-12 14:56:02
References
2014-11-13
13:42
When a transaction or savepoint rollback occurs, save the positions of all open read-cursors so that they can be restored following the rollback operation. Cherry-pick of check-in [dd03a2802f3f27] check-in: 402780a9 user: drh tags: branch-3.8.7
Context
2014-11-13
13:42
When a transaction or savepoint rollback occurs, save the positions of all open read-cursors so that they can be restored following the rollback operation. Cherry-pick of check-in [dd03a2802f3f27] check-in: 402780a9 user: drh tags: branch-3.8.7
2014-11-12
17:45
Add further tests for rollback operations in the presence of ongoing selects. check-in: eaf3aae0 user: dan tags: trunk
14:56
When a transaction or savepoint rollback occurs, save the positions of all open read-cursors so that they can be restored following the rollback operation. check-in: dd03a280 user: dan tags: trunk
2014-11-11
19:07
Remove some calls to the 'breakpoint' test command. check-in: 1412fcc4 user: mistachkin tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/btree.c.

3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521










3522
3523
3524


3525
3526
3527
3528
3529
3530








3531
3532
3533

3534
3535
3536
3537
3538
3539
3540


3541
3542
3543
3544
3545
3546
3547
....
3561
3562
3563
3564
3565
3566
3567
3568


3569
3570
3571
3572
3573
3574
3575
** references.  Or if the writeOnly flag is set to 1, then only
** trip write cursors and leave read cursors unchanged.
**
** Every cursor is a candidate to be tripped, including cursors
** that belong to other database connections that happen to be
** sharing the cache with pBtree.
**
** This routine gets called when a rollback occurs.  The writeOnly
** flag is set to 1 if the transaction did not make any schema
** changes, in which case the read cursors can continue operating.
** If schema changes did occur in the transaction, then both read
** and write cursors must both be tripped.










*/
void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
  BtCursor *p;


  assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
  if( pBtree==0 ) return;
  sqlite3BtreeEnter(pBtree);
  for(p=pBtree->pBt->pCursor; p; p=p->pNext){
    int i;
    if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ) continue;








    sqlite3BtreeClearCursor(p);
    p->eState = CURSOR_FAULT;
    p->skipNext = errCode;

    for(i=0; i<=p->iPage; i++){
      releasePage(p->apPage[i]);
      p->apPage[i] = 0;
    }
  }
  sqlite3BtreeLeave(pBtree);
}



/*
** Rollback the transaction in progress.
**
** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
** Only write cursors are tripped if writeOnly is true but all cursors are
** tripped if writeOnly is false.  Any attempt to use
................................................................................
  if( tripCode==SQLITE_OK ){
    rc = tripCode = saveAllCursors(pBt, 0, 0);
    if( rc ) writeOnly = 0;
  }else{
    rc = SQLITE_OK;
  }
  if( tripCode ){
    sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);


  }
  btreeIntegrity(p);

  if( p->inTrans==TRANS_WRITE ){
    int rc2;

    assert( TRANS_WRITE==pBt->inTransaction );







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

|

>
>

|



|
>
>
>
>
>
>
>
>



>







>
>







 







|
>
>







3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
....
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
** references.  Or if the writeOnly flag is set to 1, then only
** trip write cursors and leave read cursors unchanged.
**
** Every cursor is a candidate to be tripped, including cursors
** that belong to other database connections that happen to be
** sharing the cache with pBtree.
**
** This routine gets called when a rollback occurs. If the writeOnly
** flag is true, then only write-cursors need be tripped - read-only
** cursors save their current positions so that they may continue 
** following the rollback. Or, if writeOnly is false, all cursors are 
** tripped. In general, writeOnly is false if the transaction being
** rolled back modified the database schema. In this case b-tree root
** pages may be moved or deleted from the database altogether, making
** it unsafe for read cursors to continue.
**
** If the writeOnly flag is true and an error is encountered while 
** saving the current position of a read-only cursor, all cursors, 
** including all read-cursors are tripped.
**
** SQLITE_OK is returned if successful, or if an error occurs while
** saving a cursor position, an SQLite error code.
*/
int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
  BtCursor *p;
  int rc = SQLITE_OK;

  assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
  if( pBtree ){
    sqlite3BtreeEnter(pBtree);
    for(p=pBtree->pBt->pCursor; p; p=p->pNext){
      int i;
      if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
        if( p->eState==CURSOR_VALID ){
          int rc = saveCursorPosition(p);
          if( rc!=SQLITE_OK ){
            (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0);
            break;
          }
        }
      }else{
        sqlite3BtreeClearCursor(p);
        p->eState = CURSOR_FAULT;
        p->skipNext = errCode;
      }
      for(i=0; i<=p->iPage; i++){
        releasePage(p->apPage[i]);
        p->apPage[i] = 0;
      }
    }
    sqlite3BtreeLeave(pBtree);
  }
  return rc;
}

/*
** Rollback the transaction in progress.
**
** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
** Only write cursors are tripped if writeOnly is true but all cursors are
** tripped if writeOnly is false.  Any attempt to use
................................................................................
  if( tripCode==SQLITE_OK ){
    rc = tripCode = saveAllCursors(pBt, 0, 0);
    if( rc ) writeOnly = 0;
  }else{
    rc = SQLITE_OK;
  }
  if( tripCode ){
    int rc2 = sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
    assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) );
    if( rc2!=SQLITE_OK ) rc = rc2;
  }
  btreeIntegrity(p);

  if( p->inTrans==TRANS_WRITE ){
    int rc2;

    assert( TRANS_WRITE==pBt->inTransaction );

Changes to src/btree.h.

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
*/
#define BTREE_INTKEY     1    /* Table has only 64-bit signed integer keys */
#define BTREE_BLOBKEY    2    /* Table has keys only - no data */

int sqlite3BtreeDropTable(Btree*, int, int*);
int sqlite3BtreeClearTable(Btree*, int, int*);
int sqlite3BtreeClearTableOfCursor(BtCursor*);
void sqlite3BtreeTripAllCursors(Btree*, int, int);

void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);

int sqlite3BtreeNewDb(Btree *p);

/*







|







112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
*/
#define BTREE_INTKEY     1    /* Table has only 64-bit signed integer keys */
#define BTREE_BLOBKEY    2    /* Table has keys only - no data */

int sqlite3BtreeDropTable(Btree*, int, int*);
int sqlite3BtreeClearTable(Btree*, int, int*);
int sqlite3BtreeClearTableOfCursor(BtCursor*);
int sqlite3BtreeTripAllCursors(Btree*, int, int);

void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);

int sqlite3BtreeNewDb(Btree *p);

/*

Changes to src/vdbe.c.

2826
2827
2828
2829
2830
2831
2832
2833
2834

2835
2836
2837
2838
2839
2840
2841
        rc = p->rc;
      }else{
        int isSchemaChange;
        iSavepoint = db->nSavepoint - iSavepoint - 1;
        if( p1==SAVEPOINT_ROLLBACK ){
          isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
          for(ii=0; ii<db->nDb; ii++){
            sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT,
                                       isSchemaChange==0);

          }
        }else{
          isSchemaChange = 0;
        }
        for(ii=0; ii<db->nDb; ii++){
          rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
          if( rc!=SQLITE_OK ){







|

>







2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
        rc = p->rc;
      }else{
        int isSchemaChange;
        iSavepoint = db->nSavepoint - iSavepoint - 1;
        if( p1==SAVEPOINT_ROLLBACK ){
          isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
          for(ii=0; ii<db->nDb; ii++){
            rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT,
                                       isSchemaChange==0);
            if( rc!=SQLITE_OK ) goto abort_due_to_error;
          }
        }else{
          isSchemaChange = 0;
        }
        for(ii=0; ii<db->nDb; ii++){
          rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
          if( rc!=SQLITE_OK ){

Added test/rollback2.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
# 2014 November 12
#
# 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.
#
#***********************************************************************
#

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

proc int2hex {i} { format %.2X $i }
db func int2hex int2hex

do_execsql_test 1.0 {
  SELECT int2hex(0), int2hex(100), int2hex(255)
} {00 64 FF}
do_execsql_test 1.1 {
  CREATE TABLE t1(i, h);
  CREATE INDEX i1 ON t1(h);
  WITH data(a, b) AS (
    SELECT 1, int2hex(1)
      UNION ALL
    SELECT a+1, int2hex(a+1) FROM data WHERE a<40
  )
  INSERT INTO t1 SELECT * FROM data;
} {}


proc do_rollback_test {tn args} {
  set A(-setup)    ""
  set A(-select)   ""
  set A(-result)   ""
  set A(-rollback) ROLLBACK

  array set O $args
  foreach k [array names O] {
    if {[info exists A($k)]==0} { error "unknown option: $k" }
    set A($k) $O($k)
  }

  for {set iRollback 0} 1 {incr iRollback} {
    catch { db eval ROLLBACK }
    set res [list]
    db eval $A(-setup)

    set i 0
    db eval $A(-select) x {
      if {$i==$iRollback} { db eval $A(-rollback) }
      foreach k $x(*) { lappend res $x($k) }
      incr i
    }

    do_test $tn.$iRollback [list set {} $res] [list {*}$A(-result)]
    if {$i < $iRollback} break
  }
}

do_rollback_test 2 -setup {
  BEGIN;
    DELETE FROM t1 WHERE (i%2)==1;
} -select {
  SELECT i FROM t1 WHERE (i%2)==0
} -result {
  2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
}

finish_test