/ Check-in [f256bc07]
Login

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

Overview
Comment:When a forced rollback occurs, cause all active statements on the same database connection to halt immediately with an SQLITE_ABORT error code. This is a partial fix to ticket [f777251dc7].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | trunk | experimental
Files: files | file ages | folders
SHA1:f256bc0796ca8bd4569dd6de37621c961efaecd1
User & Date: drh 2009-10-15 23:03:11
Original Comment: When a forced rollback occurs, cause all active statements on the same database connection to halt immediately with an SQLITE_ABORT error code. This is a partial fix to ticket [f777251dc7].
Context
2009-10-15
23:03
When a forced rollback occurs, cause all active statements on the same database connection to halt immediately with an SQLITE_ABORT error code. This is a partial fix to ticket [f777251dc7]. Closed-Leaf check-in: f256bc07 user: drh tags: trunk, experimental
2009-10-14
11:33
Version 3.6.19 check-in: c1d499af user: drh tags: trunk, release
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

721
722
723
724
725
726
727




728
729
730
731
732
733
734
      }
      sqlite3BtreeRollback(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }
  sqlite3VtabRollback(db);
  sqlite3EndBenignMalloc();





  if( db->flags&SQLITE_InternChanges ){
    sqlite3ExpirePreparedStatements(db);
    sqlite3ResetInternalSchema(db, 0);
  }

  /* Any deferred constraint violations have now been resolved. */







>
>
>
>







721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
      }
      sqlite3BtreeRollback(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }
  sqlite3VtabRollback(db);
  sqlite3EndBenignMalloc();
  if( inTrans ){
    sqlite3_interrupt(db);
    db->interruptErrorCode = SQLITE_ABORT;
  }

  if( db->flags&SQLITE_InternChanges ){
    sqlite3ExpirePreparedStatements(db);
    sqlite3ResetInternalSchema(db, 0);
  }

  /* Any deferred constraint violations have now been resolved. */

Changes to src/sqliteInt.h.

789
790
791
792
793
794
795

796
797
798
799
800
801
802
....
2508
2509
2510
2511
2512
2513
2514

2515
2516
2517
2518
2519
2520
2521
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  u8 autoCommit;                /* The auto-commit flag. */
  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
  u8 dfltJournalMode;           /* Default journal mode for attached dbs */

  signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */
  int nextPagesize;             /* Pagesize after VACUUM if >0 */
  int nTable;                   /* Number of tables in the database */
  CollSeq *pDfltColl;           /* The default collating sequence (BINARY) */
  i64 lastRowid;                /* ROWID of most recent insert (see above) */
  u32 magic;                    /* Magic number for detect library misuse */
  int nChange;                  /* Value returned by sqlite3_changes() */
................................................................................
  void *sqlite3TestTextToPtr(const char*);
#endif
void sqlite3SetString(char **, sqlite3*, const char*, ...);
void sqlite3ErrorMsg(Parse*, const char*, ...);
void sqlite3ErrorClear(Parse*);
int sqlite3Dequote(char*);
int sqlite3KeywordCode(const unsigned char*, int);

int sqlite3RunParser(Parse*, const char*, char **);
void sqlite3FinishCoding(Parse*);
int sqlite3GetTempReg(Parse*);
void sqlite3ReleaseTempReg(Parse*,int);
int sqlite3GetTempRange(Parse*,int);
void sqlite3ReleaseTempRange(Parse*,int,int);
Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);







>







 







>







789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
....
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  u8 autoCommit;                /* The auto-commit flag. */
  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
  u8 dfltJournalMode;           /* Default journal mode for attached dbs */
  u8 interruptErrorCode;        /* Error code returned after isInterrupted */
  signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */
  int nextPagesize;             /* Pagesize after VACUUM if >0 */
  int nTable;                   /* Number of tables in the database */
  CollSeq *pDfltColl;           /* The default collating sequence (BINARY) */
  i64 lastRowid;                /* ROWID of most recent insert (see above) */
  u32 magic;                    /* Magic number for detect library misuse */
  int nChange;                  /* Value returned by sqlite3_changes() */
................................................................................
  void *sqlite3TestTextToPtr(const char*);
#endif
void sqlite3SetString(char **, sqlite3*, const char*, ...);
void sqlite3ErrorMsg(Parse*, const char*, ...);
void sqlite3ErrorClear(Parse*);
int sqlite3Dequote(char*);
int sqlite3KeywordCode(const unsigned char*, int);
void sqlite3ResetInterrupt(sqlite3*);
int sqlite3RunParser(Parse*, const char*, char **);
void sqlite3FinishCoding(Parse*);
int sqlite3GetTempReg(Parse*);
void sqlite3ReleaseTempReg(Parse*,int);
int sqlite3GetTempRange(Parse*,int);
void sqlite3ReleaseTempRange(Parse*,int,int);
Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);

Changes to src/tokenize.c.

382
383
384
385
386
387
388










389
390
391
392
393
394
395
...
400
401
402
403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
...
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
      *tokenType = keywordCode((char*)z, i);
      return i;
    }
  }
  *tokenType = TK_ILLEGAL;
  return 1;
}











/*
** Run the parser on the given SQL string.  The parser structure is
** passed in.  An SQLITE_ status code is returned.  If an error occurs
** then an and attempt is made to write an error message into 
** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that
** error message.
................................................................................
  void *pEngine;                  /* The LEMON-generated LALR(1) parser */
  int tokenType;                  /* type of the next token */
  int lastTokenParsed = -1;       /* type of the previous token */
  u8 enableLookaside;             /* Saved value of db->lookaside.bEnabled */
  sqlite3 *db = pParse->db;       /* The database connection */
  int mxSqlLen;                   /* Max length of an SQL string */


  mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
  if( db->activeVdbeCnt==0 ){
    db->u1.isInterrupted = 0;
  }

  pParse->rc = SQLITE_OK;
  pParse->zTail = zSql;
  i = 0;
  assert( pzErrMsg!=0 );
  pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc);
  if( pEngine==0 ){
    db->mallocFailed = 1;
................................................................................
      pParse->rc = SQLITE_TOOBIG;
      break;
    }
    switch( tokenType ){
      case TK_SPACE: {
        if( db->u1.isInterrupted ){
          sqlite3ErrorMsg(pParse, "interrupt");
          pParse->rc = SQLITE_INTERRUPT;
          goto abort_parse;
        }
        break;
      }
      case TK_ILLEGAL: {
        sqlite3DbFree(db, *pzErrMsg);
        *pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"",







>
>
>
>
>
>
>
>
>
>







 







<

<
<
<
>







 







|







382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
...
410
411
412
413
414
415
416

417



418
419
420
421
422
423
424
425
...
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
      *tokenType = keywordCode((char*)z, i);
      return i;
    }
  }
  *tokenType = TK_ILLEGAL;
  return 1;
}

/*
** If there are no active VDBEs, then reset the interrupt flag.
*/
void sqlite3ResetInterrupt(sqlite3 *db){
  if( db->activeVdbeCnt==0 ){
    db->u1.isInterrupted = 0;
    db->interruptErrorCode = SQLITE_INTERRUPT;
  }
}

/*
** Run the parser on the given SQL string.  The parser structure is
** passed in.  An SQLITE_ status code is returned.  If an error occurs
** then an and attempt is made to write an error message into 
** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that
** error message.
................................................................................
  void *pEngine;                  /* The LEMON-generated LALR(1) parser */
  int tokenType;                  /* type of the next token */
  int lastTokenParsed = -1;       /* type of the previous token */
  u8 enableLookaside;             /* Saved value of db->lookaside.bEnabled */
  sqlite3 *db = pParse->db;       /* The database connection */
  int mxSqlLen;                   /* Max length of an SQL string */


  mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];



  sqlite3ResetInterrupt(db);
  pParse->rc = SQLITE_OK;
  pParse->zTail = zSql;
  i = 0;
  assert( pzErrMsg!=0 );
  pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc);
  if( pEngine==0 ){
    db->mallocFailed = 1;
................................................................................
      pParse->rc = SQLITE_TOOBIG;
      break;
    }
    switch( tokenType ){
      case TK_SPACE: {
        if( db->u1.isInterrupted ){
          sqlite3ErrorMsg(pParse, "interrupt");
          pParse->rc = db->interruptErrorCode;
          goto abort_parse;
        }
        break;
      }
      case TK_ILLEGAL: {
        sqlite3DbFree(db, *pzErrMsg);
        *pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"",

Changes to src/vdbe.c.

731
732
733
734
735
736
737

738
739
740
741
742
743
744
...
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
....
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
....
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
....
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
    }else if( (opProperty & OPFLG_IN3)!=0 ){
      assert( pOp->p3>0 );
      assert( pOp->p3<=p->nMem );
      pIn3 = &p->aMem[pOp->p3];
      REGISTER_TRACE(pOp->p3, pIn3);
    }


    switch( pOp->opcode ){

/*****************************************************************************
** What follows is a massive switch statement where each case implements a
** separate instruction in the virtual machine.  If we follow the usual
** indentation conventions, each case should be indented by 6 spaces.  But
** that is a lot of wasted space on the left margin.  So the code within
................................................................................
**
** An unconditional jump to address P2.
** The next instruction executed will be 
** the one at index P2 from the beginning of
** the program.
*/
case OP_Goto: {             /* jump */
  CHECK_FOR_INTERRUPT;
  pc = pOp->p2 - 1;
  break;
}

/* Opcode:  Gosub P1 P2 * * *
**
** Write the current address onto register P1
................................................................................
*/
case OP_Prev:          /* jump */
case OP_Next: {        /* jump */
  VdbeCursor *pC;
  BtCursor *pCrsr;
  int res;

  CHECK_FOR_INTERRUPT;
  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  if( pC==0 ){
    break;  /* See ticket #2273 */
  }
  pCrsr = pC->pCursor;
  if( pCrsr==0 ){
................................................................................
** register P3.  Or, if boolean index P1 is initially empty, leave P3
** unchanged and jump to instruction P2.
*/
case OP_RowSetRead: {       /* jump, out3 */
  Mem *pIdx;
  i64 val;
  assert( pOp->p1>0 && pOp->p1<=p->nMem );
  CHECK_FOR_INTERRUPT;
  pIdx = &p->aMem[pOp->p1];
  pOut = &p->aMem[pOp->p3];
  if( (pIdx->flags & MEM_RowSet)==0 
   || sqlite3RowSetNext(pIdx->u.pRowSet, &val)==0
  ){
    /* The boolean index is empty */
    sqlite3VdbeMemSetNull(pIdx);
................................................................................
  goto vdbe_error_halt;

  /* Jump to here if the sqlite3_interrupt() API sets the interrupt
  ** flag.
  */
abort_due_to_interrupt:
  assert( db->u1.isInterrupted );
  rc = SQLITE_INTERRUPT;
  p->rc = rc;
  sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
  goto vdbe_error_halt;
}







>







 







<







 







<







 







<







 







|




731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
...
777
778
779
780
781
782
783

784
785
786
787
788
789
790
....
4150
4151
4152
4153
4154
4155
4156

4157
4158
4159
4160
4161
4162
4163
....
4698
4699
4700
4701
4702
4703
4704

4705
4706
4707
4708
4709
4710
4711
....
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
    }else if( (opProperty & OPFLG_IN3)!=0 ){
      assert( pOp->p3>0 );
      assert( pOp->p3<=p->nMem );
      pIn3 = &p->aMem[pOp->p3];
      REGISTER_TRACE(pOp->p3, pIn3);
    }

    CHECK_FOR_INTERRUPT;
    switch( pOp->opcode ){

/*****************************************************************************
** What follows is a massive switch statement where each case implements a
** separate instruction in the virtual machine.  If we follow the usual
** indentation conventions, each case should be indented by 6 spaces.  But
** that is a lot of wasted space on the left margin.  So the code within
................................................................................
**
** An unconditional jump to address P2.
** The next instruction executed will be 
** the one at index P2 from the beginning of
** the program.
*/
case OP_Goto: {             /* jump */

  pc = pOp->p2 - 1;
  break;
}

/* Opcode:  Gosub P1 P2 * * *
**
** Write the current address onto register P1
................................................................................
*/
case OP_Prev:          /* jump */
case OP_Next: {        /* jump */
  VdbeCursor *pC;
  BtCursor *pCrsr;
  int res;


  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  if( pC==0 ){
    break;  /* See ticket #2273 */
  }
  pCrsr = pC->pCursor;
  if( pCrsr==0 ){
................................................................................
** register P3.  Or, if boolean index P1 is initially empty, leave P3
** unchanged and jump to instruction P2.
*/
case OP_RowSetRead: {       /* jump, out3 */
  Mem *pIdx;
  i64 val;
  assert( pOp->p1>0 && pOp->p1<=p->nMem );

  pIdx = &p->aMem[pOp->p1];
  pOut = &p->aMem[pOp->p3];
  if( (pIdx->flags & MEM_RowSet)==0 
   || sqlite3RowSetNext(pIdx->u.pRowSet, &val)==0
  ){
    /* The boolean index is empty */
    sqlite3VdbeMemSetNull(pIdx);
................................................................................
  goto vdbe_error_halt;

  /* Jump to here if the sqlite3_interrupt() API sets the interrupt
  ** flag.
  */
abort_due_to_interrupt:
  assert( db->u1.isInterrupted );
  rc = db->interruptErrorCode;
  p->rc = rc;
  sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
  goto vdbe_error_halt;
}

Changes to src/vdbeaux.c.

1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
....
2127
2128
2129
2130
2131
2132
2133

2134
2135
2136
2137
2138
2139
2140
  do{
    i = p->pc++;
  }while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
  if( i>=nRow ){
    p->rc = SQLITE_OK;
    rc = SQLITE_DONE;
  }else if( db->u1.isInterrupted ){
    p->rc = SQLITE_INTERRUPT;
    rc = SQLITE_ERROR;
    sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
  }else{
    char *z;
    Op *pOp;
    if( i<p->nOp ){
      pOp = &p->aOp[i];
................................................................................
  /* We have successfully halted and closed the VM.  Record this fact. */
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
    if( !p->readOnly ){
      db->writeVdbeCnt--;
    }
    assert( db->activeVdbeCnt>=db->writeVdbeCnt );

  }
  p->magic = VDBE_MAGIC_HALT;
  checkActiveVdbeCnt(db);
  if( p->db->mallocFailed ){
    p->rc = SQLITE_NOMEM;
  }








|







 







>







1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
....
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
  do{
    i = p->pc++;
  }while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
  if( i>=nRow ){
    p->rc = SQLITE_OK;
    rc = SQLITE_DONE;
  }else if( db->u1.isInterrupted ){
    p->rc = db->interruptErrorCode;
    rc = SQLITE_ERROR;
    sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
  }else{
    char *z;
    Op *pOp;
    if( i<p->nOp ){
      pOp = &p->aOp[i];
................................................................................
  /* We have successfully halted and closed the VM.  Record this fact. */
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
    if( !p->readOnly ){
      db->writeVdbeCnt--;
    }
    assert( db->activeVdbeCnt>=db->writeVdbeCnt );
    sqlite3ResetInterrupt(db);
  }
  p->magic = VDBE_MAGIC_HALT;
  checkActiveVdbeCnt(db);
  if( p->db->mallocFailed ){
    p->rc = SQLITE_NOMEM;
  }

Changes to test/tkt2820.test.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  }

  # The INSERT statement within the loop should fail on a
  # constraint violation on the second inserted row.  This
  # should cause the entire INSERT to rollback using a statement
  # journal.
  #
  db eval {SELECT name FROM sqlite_master} {
    catch {db eval {
      INSERT INTO t1 SELECT a+1 FROM t1 ORDER BY a DESC
    }}
  }
  db eval {SELECT a FROM t1 ORDER BY a}
} {1 2}

finish_test







|



|




79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  }

  # The INSERT statement within the loop should fail on a
  # constraint violation on the second inserted row.  This
  # should cause the entire INSERT to rollback using a statement
  # journal.
  #
  catch {db eval {SELECT name FROM sqlite_master} {
    catch {db eval {
      INSERT INTO t1 SELECT a+1 FROM t1 ORDER BY a DESC
    }}
  }}
  db eval {SELECT a FROM t1 ORDER BY a}
} {1 2}

finish_test