aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/alter.c1
-rw-r--r--src/build.c35
-rw-r--r--src/delete.c1
-rw-r--r--src/expr.c23
-rw-r--r--src/insert.c16
-rw-r--r--src/sqliteInt.h4
-rw-r--r--src/update.c1
-rw-r--r--src/vdbe.c62
-rw-r--r--src/vdbe.h2
-rw-r--r--src/vdbeapi.c2
-rw-r--r--src/vdbeaux.c46
-rw-r--r--src/vdbeblob.c2
12 files changed, 86 insertions, 109 deletions
diff --git a/src/alter.c b/src/alter.c
index 14b58df33..819d5bbf8 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -362,6 +362,7 @@ void sqlite3AlterRenameTable(
int i = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0);
sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
+ sqlite3MayAbort(pParse);
}
#endif
diff --git a/src/build.c b/src/build.c
index 431d5b5cd..4e0f13475 100644
--- a/src/build.c
+++ b/src/build.c
@@ -195,7 +195,8 @@ void sqlite3FinishCoding(Parse *pParse){
#endif
assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
- pParse->nTab, pParse->nMaxArg, pParse->explain);
+ pParse->nTab, pParse->nMaxArg, pParse->explain,
+ pParse->isMultiWrite && pParse->mayAbort);
pParse->rc = SQLITE_DONE;
pParse->colNamesSet = 0;
}else if( pParse->rc==SQLITE_OK ){
@@ -1878,6 +1879,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
+ sqlite3MayAbort(pParse);
#ifndef SQLITE_OMIT_AUTOVACUUM
/* OP_Destroy stores an in integer r1. If this integer
** is non-zero, then it is the root page number of a table moved to
@@ -2319,8 +2321,8 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
** since sqlite3ReleaseTempRange() was called, it is safe to do so.
*/
sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32);
- sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, 0,
- "indexed columns are not unique", P4_STATIC);
+ sqlite3HaltConstraint(
+ pParse, OE_Abort, "indexed columns are not unique", P4_STATIC);
}
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
@@ -3465,12 +3467,29 @@ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
sqlite3CodeVerifySchema(pParse, iDb);
pToplevel->writeMask |= 1<<iDb;
- if( setStatement && pParse->nested==0 && pParse==pToplevel ){
- /* Every place where this routine is called with setStatement!=0 has
- ** already successfully created a VDBE. */
- assert( pParse->pVdbe );
- sqlite3VdbeAddOp1(pParse->pVdbe, OP_Statement, iDb);
+ pToplevel->isMultiWrite |= setStatement;
+}
+
+/*
+** Set the "may throw abort exception" flag for the statement currently
+** being coded.
+*/
+void sqlite3MayAbort(Parse *pParse){
+ Parse *pToplevel = sqlite3ParseToplevel(pParse);
+ pToplevel->mayAbort = 1;
+}
+
+/*
+** Code an OP_Halt that causes the vdbe to return an SQLITE_CONSTRAINT
+** error. The onError parameter determines which (if any) of the statement
+** and/or current transaction is rolled back.
+*/
+void sqlite3HaltConstraint(Parse *pParse, int onError, char *p4, int p4type){
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ if( onError==OE_Abort ){
+ sqlite3MayAbort(pParse);
}
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type);
}
/*
diff --git a/src/delete.c b/src/delete.c
index f0ca8ab47..6f4b54e91 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -392,6 +392,7 @@ void sqlite3DeleteFrom(
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB);
+ sqlite3MayAbort(pParse);
}else
#endif
{
diff --git a/src/expr.c b/src/expr.c
index 75ff04bce..50b24de41 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2692,20 +2692,27 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
}
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
- int vrc;
- if( !pParse->pTriggerTab ){
- sqlite3ErrorMsg(pParse,
- "RAISE() may only be used within a trigger-program");
- return 0;
- }
assert( pExpr->affinity==OE_Rollback
|| pExpr->affinity==OE_Abort
|| pExpr->affinity==OE_Fail
|| pExpr->affinity==OE_Ignore
);
+ if( !pParse->pTriggerTab ){
+ sqlite3ErrorMsg(pParse,
+ "RAISE() may only be used within a trigger-program");
+ return 0;
+ }
+ if( pExpr->affinity==OE_Abort ){
+ sqlite3MayAbort(pParse);
+ }
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- vrc = (pExpr->affinity==OE_Ignore ? SQLITE_OK : SQLITE_CONSTRAINT);
- sqlite3VdbeAddOp4(v, OP_Halt, vrc, pExpr->affinity, 0, pExpr->u.zToken,0);
+ if( pExpr->affinity==OE_Ignore ){
+ sqlite3VdbeAddOp4(
+ v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
+ }else{
+ sqlite3HaltConstraint(pParse, pExpr->affinity, pExpr->u.zToken, 0);
+ }
+
break;
}
#endif
diff --git a/src/insert.c b/src/insert.c
index a1db400e1..f27d50f23 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -971,6 +971,7 @@ void sqlite3Insert(
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB);
+ sqlite3MayAbort(pParse);
}else
#endif
{
@@ -1169,8 +1170,9 @@ void sqlite3GenerateConstraintChecks(
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|| onError==OE_Ignore || onError==OE_Replace );
switch( onError ){
- case OE_Rollback:
case OE_Abort:
+ sqlite3MayAbort(pParse);
+ case OE_Rollback:
case OE_Fail: {
char *zMsg;
j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull,
@@ -1205,7 +1207,7 @@ void sqlite3GenerateConstraintChecks(
if( onError==OE_Ignore ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
}else{
- sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_CONSTRAINT, onError);
+ sqlite3HaltConstraint(pParse, onError, 0, 0);
}
sqlite3VdbeResolveLabel(v, allOk);
}
@@ -1236,8 +1238,8 @@ void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0,
- "PRIMARY KEY must be unique", P4_STATIC);
+ sqlite3HaltConstraint(
+ pParse, onError, "PRIMARY KEY must be unique", P4_STATIC);
break;
}
case OE_Replace: {
@@ -1350,7 +1352,7 @@ void sqlite3GenerateConstraintChecks(
sqlite3StrAccumAppend(&errMsg,
pIdx->nColumn>1 ? " are not unique" : " is not unique", -1);
zErr = sqlite3StrAccumFinish(&errMsg);
- sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, zErr, 0);
+ sqlite3HaltConstraint(pParse, onError, zErr, 0);
sqlite3DbFree(errMsg.db, zErr);
break;
}
@@ -1747,8 +1749,8 @@ static int xferOptimization(
if( pDest->iPKey>=0 ){
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
- sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0,
- "PRIMARY KEY must be unique", P4_STATIC);
+ sqlite3HaltConstraint(
+ pParse, onError, "PRIMARY KEY must be unique", P4_STATIC);
sqlite3VdbeJumpHere(v, addr2);
autoIncStep(pParse, regAutoinc, regRowid);
}else if( pDest->pIndex==0 ){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 457427380..fa5aaa0b4 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2097,6 +2097,8 @@ struct Parse {
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
u32 writeMask; /* Start a write transaction on these databases */
u32 cookieMask; /* Bitmask of schema verified databases */
+ u8 isMultiWrite; /* True if statement may affect/insert multiple rows */
+ u8 mayAbort; /* True if statement may throw an ABORT exception */
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -2655,6 +2657,8 @@ void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
void sqlite3BeginWriteOperation(Parse*, int, int);
+void sqlite3MayAbort(Parse *);
+void sqlite3HaltConstraint(Parse*, int, char*, int);
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
diff --git a/src/update.c b/src/update.c
index ba1ad91f2..b0df33399 100644
--- a/src/update.c
+++ b/src/update.c
@@ -586,6 +586,7 @@ static void updateVirtualTable(
}
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
+ sqlite3MayAbort(pParse);
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
sqlite3VdbeJumpHere(v, addr);
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
diff --git a/src/vdbe.c b/src/vdbe.c
index 6d972d383..823b5f6f8 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -2452,46 +2452,6 @@ case OP_Count: { /* out2-prerelease */
}
#endif
-/* Opcode: Statement P1 * * * *
-**
-** Begin an individual statement transaction which is part of a larger
-** transaction. This is needed so that the statement
-** can be rolled back after an error without having to roll back the
-** entire transaction. The statement transaction will automatically
-** commit when the VDBE halts.
-**
-** If the database connection is currently in autocommit mode (that
-** is to say, if it is in between BEGIN and COMMIT)
-** and if there are no other active statements on the same database
-** connection, then this operation is a no-op. No statement transaction
-** is needed since any error can use the normal ROLLBACK process to
-** undo changes.
-**
-** If a statement transaction is started, then a statement journal file
-** will be allocated and initialized.
-**
-** The statement is begun on the database file with index P1. The main
-** database file has an index of 0 and the file used for temporary tables
-** has an index of 1.
-*/
-case OP_Statement: {
- Btree *pBt;
- if( db->autoCommit==0 || db->activeVdbeCnt>1 ){
- assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( db->aDb[pOp->p1].pBt!=0 );
- pBt = db->aDb[pOp->p1].pBt;
- assert( sqlite3BtreeIsInTrans(pBt) );
- assert( (p->btreeMask & (1<<pOp->p1))!=0 );
- if( p->iStatement==0 ){
- assert( db->nStatement>=0 && db->nSavepoint>=0 );
- db->nStatement++;
- p->iStatement = db->nSavepoint + db->nStatement;
- }
- rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
- }
- break;
-}
-
/* Opcode: Savepoint P1 * * P4 *
**
** Open, release or rollback the savepoint named by parameter P4, depending
@@ -2720,6 +2680,16 @@ case OP_AutoCommit: {
** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained
** on the file.
**
+** If a write-transaction is started and the Vdbe.usesStmtJournal flag is
+** true (this flag is set if the Vdbe may modify more than one row and may
+** throw an ABORT exception), a statement transaction may also be opened.
+** More specifically, a statement transaction is opened iff the database
+** connection is currently not in autocommit mode, or if there are other
+** active statements. A statement transaction allows the affects of this
+** VDBE to be rolled back after an error without having to roll back the
+** entire transaction. If no error is encountered, the statement transaction
+** will automatically commit when the VDBE halts.
+**
** If P2 is zero, then a read-lock is obtained on the database file.
*/
case OP_Transaction: {
@@ -2739,6 +2709,18 @@ case OP_Transaction: {
if( rc!=SQLITE_OK && rc!=SQLITE_READONLY /* && rc!=SQLITE_BUSY */ ){
goto abort_due_to_error;
}
+
+ if( pOp->p2 && p->usesStmtJournal
+ && (db->autoCommit==0 || db->activeVdbeCnt>1)
+ ){
+ assert( sqlite3BtreeIsInTrans(pBt) );
+ if( p->iStatement==0 ){
+ assert( db->nStatement>=0 && db->nSavepoint>=0 );
+ db->nStatement++;
+ p->iStatement = db->nSavepoint + db->nStatement;
+ }
+ rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
+ }
}
break;
}
diff --git a/src/vdbe.h b/src/vdbe.h
index 3fef87382..3df12c363 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -184,7 +184,7 @@ void sqlite3VdbeUsesBtree(Vdbe*, int);
VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
int sqlite3VdbeMakeLabel(Vdbe*);
void sqlite3VdbeDelete(Vdbe*);
-void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int);
+void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int);
int sqlite3VdbeFinalize(Vdbe*);
void sqlite3VdbeResolveLabel(Vdbe*, int);
int sqlite3VdbeCurrentAddr(Vdbe*);
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index fc3af4004..634c29c45 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -76,7 +76,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt;
sqlite3_mutex_enter(v->db->mutex);
rc = sqlite3VdbeReset(v);
- sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0);
+ sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0, 0);
assert( (rc & (v->db->errMask))==rc );
rc = sqlite3ApiExit(v->db, rc);
sqlite3_mutex_leave(v->db->mutex);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 35ae1e355..b410c4322 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -250,29 +250,13 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){
** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument
** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by
** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
-**
-** This routine also does the following optimization: It scans for
-** instructions that might cause a statement rollback. Such instructions
-** are:
-**
-** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort.
-** * OP_Destroy
-** * OP_VUpdate
-** * OP_VRename
-**
-** If no such instruction is found, then every Statement instruction
-** is changed to a Noop. In this way, we avoid creating the statement
-** journal file unnecessarily.
*/
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int i;
int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
int *aLabel = p->aLabel;
- int doesStatementRollback = 0;
- int hasStatementBegin = 0;
p->readOnly = 1;
- p->usesStmtJournal = 0;
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
@@ -282,21 +266,9 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
}else if( opcode==OP_VUpdate ){
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
#endif
- }
- if( opcode==OP_Halt ){
- if( pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort ){
- doesStatementRollback = 1;
- }
- }else if( opcode==OP_Statement ){
- hasStatementBegin = 1;
- p->usesStmtJournal = 1;
- }else if( opcode==OP_Destroy || opcode==OP_Program ){
- doesStatementRollback = 1;
}else if( opcode==OP_Transaction && pOp->p2!=0 ){
p->readOnly = 0;
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( opcode==OP_VUpdate || opcode==OP_VRename ){
- doesStatementRollback = 1;
}else if( opcode==OP_VFilter ){
int n;
assert( p->nOp - i >= 3 );
@@ -315,20 +287,6 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
p->aLabel = 0;
*pMaxFuncArgs = nMaxArgs;
-
- /* If we never rollback a statement transaction, then statement
- ** transactions are not needed. So change every OP_Statement
- ** opcode into an OP_Noop. This avoid a call to sqlite3OsOpenExclusive()
- ** which can be expensive on some platforms.
- */
- if( hasStatementBegin && !doesStatementRollback ){
- p->usesStmtJournal = 0;
- for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
- if( pOp->opcode==OP_Statement ){
- pOp->opcode = OP_Noop;
- }
- }
- }
}
/*
@@ -1237,7 +1195,8 @@ void sqlite3VdbeMakeReady(
int nMem, /* Number of memory cells to allocate */
int nCursor, /* Number of cursors to allocate */
int nArg, /* Maximum number of args in SubPrograms */
- int isExplain /* True if the EXPLAIN keywords is present */
+ int isExplain, /* True if the EXPLAIN keywords is present */
+ int usesStmtJournal /* True to set Vdbe.usesStmtJournal */
){
int n;
sqlite3 *db = p->db;
@@ -1273,6 +1232,7 @@ void sqlite3VdbeMakeReady(
u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc];
int nByte;
resolveP2Values(p, &nArg);
+ p->usesStmtJournal = usesStmtJournal;
if( isExplain && nMem<10 ){
nMem = 10;
}
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index b17af8acf..009234f4e 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -204,7 +204,7 @@ int sqlite3_blob_open(
sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
sqlite3VdbeChangeP2(v, 7, pTab->nCol);
if( !db->mallocFailed ){
- sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0);
+ sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0);
}
}