diff options
author | rdc <rdc@noemail.net> | 2004-02-20 22:53:38 +0000 |
---|---|---|
committer | rdc <rdc@noemail.net> | 2004-02-20 22:53:38 +0000 |
commit | b0c374ffbb7bcc4e590b69233e1b994fa296abf4 (patch) | |
tree | 621c4acc2bca8fbf8b93883fcb6b87109728346e /src | |
parent | fcabd4641e1a273de8ddadce87e2097148bb8a26 (diff) | |
download | sqlite-b0c374ffbb7bcc4e590b69233e1b994fa296abf4.tar.gz sqlite-b0c374ffbb7bcc4e590b69233e1b994fa296abf4.zip |
Fixed behaviour of last_insert_rowid() with triggers and add last_statement_change_count() function that works correctly with triggers. (CVS 1251)
FossilOrigin-Name: 3383413a53bff0fef0765144de3bb9a298a5bb5c
Diffstat (limited to 'src')
-rw-r--r-- | src/delete.c | 6 | ||||
-rw-r--r-- | src/func.c | 18 | ||||
-rw-r--r-- | src/insert.c | 7 | ||||
-rw-r--r-- | src/main.c | 6 | ||||
-rw-r--r-- | src/sqliteInt.h | 33 | ||||
-rw-r--r-- | src/trigger.c | 2 | ||||
-rw-r--r-- | src/update.c | 3 | ||||
-rw-r--r-- | src/vdbe.c | 74 | ||||
-rw-r--r-- | src/vdbeInt.h | 15 |
9 files changed, 143 insertions, 21 deletions
diff --git a/src/delete.c b/src/delete.c index 0b49b7cda..4a065f30a 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.59 2004/02/16 03:44:02 drh Exp $ +** $Id: delete.c,v 1.60 2004/02/20 22:53:39 rdc Exp $ */ #include "sqliteInt.h" @@ -299,6 +299,7 @@ void sqliteDeleteFrom( pParse->nTab = iCur; } } + sqliteVdbeAddOp(v, OP_SetCounts, 0, 0); sqliteEndWriteOperation(pParse); /* @@ -347,7 +348,8 @@ void sqliteGenerateRowDelete( int addr; addr = sqliteVdbeAddOp(v, OP_NotExists, iCur, 0); sqliteGenerateRowIndexDelete(db, v, pTab, iCur, 0); - sqliteVdbeAddOp(v, OP_Delete, iCur, count); + sqliteVdbeAddOp(v, OP_Delete, iCur, + (count?OPFLAG_NCHANGE:0) | OPFLAG_CSCHANGE); sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v)); } diff --git a/src/func.c b/src/func.c index 688f50cff..2f30619de 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.39 2004/02/11 09:46:32 drh Exp $ +** $Id: func.c,v 1.40 2004/02/20 22:53:39 rdc Exp $ */ #include <ctype.h> #include <math.h> @@ -209,6 +209,16 @@ static void last_insert_rowid(sqlite_func *context, int arg, const char **argv){ sqlite_set_result_int(context, sqlite_last_insert_rowid(db)); } +static void change_count(sqlite_func *context, int arg, const char **argv){ + sqlite *db = sqlite_user_data(context); + sqlite_set_result_int(context, sqlite_changes(db)); +} +static void last_statement_change_count(sqlite_func *context, int arg, + const char **argv){ + sqlite *db = sqlite_user_data(context); + sqlite_set_result_int(context, sqlite_last_statement_changes(db)); +} + /* ** Implementation of the like() SQL function. This function implements ** the build-in LIKE operator. The first argument to the function is the @@ -613,6 +623,12 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){ sqlite_create_function(db, "last_insert_rowid", 0, last_insert_rowid, db); sqlite_function_type(db, "last_insert_rowid", SQLITE_NUMERIC); + sqlite_create_function(db, "change_count", 0, change_count, db); + sqlite_function_type(db, "change_count", SQLITE_NUMERIC); + sqlite_create_function(db, "last_statement_change_count", 0, + last_statement_change_count, db); + sqlite_function_type(db, "last_statement_change_count", SQLITE_NUMERIC); + for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){ sqlite_create_aggregate(db, aAggs[i].zName, aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0); diff --git a/src/insert.c b/src/insert.c index ba02810fd..679502e65 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.91 2004/02/16 03:44:02 drh Exp $ +** $Id: insert.c,v 1.92 2004/02/20 22:53:39 rdc Exp $ */ #include "sqliteInt.h" @@ -535,6 +535,7 @@ void sqliteInsert( } } + sqliteVdbeAddOp(v, OP_SetCounts, 0, 0); sqliteEndWriteOperation(pParse); /* @@ -906,7 +907,9 @@ void sqliteCompleteInsertion( sqliteVdbeAddOp(v, OP_Dup, 1, 0); sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); } - sqliteVdbeAddOp(v, OP_PutIntKey, base, pParse->trigStack?0:1); + sqliteVdbeAddOp(v, OP_PutIntKey, base, + (pParse->trigStack?0:OPFLAG_NCHANGE) | + (isUpdate?0:OPFLAG_LASTROWID) | OPFLAG_CSCHANGE); if( isUpdate && recnoChng ){ sqliteVdbeAddOp(v, OP_Pop, 1, 0); } diff --git a/src/main.c b/src/main.c index 659ebfb25..3c8b2d680 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.157 2004/02/20 14:50:58 drh Exp $ +** $Id: main.c,v 1.158 2004/02/20 22:53:39 rdc Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -545,6 +545,10 @@ int sqlite_changes(sqlite *db){ return db->nChange; } +int sqlite_last_statement_changes(sqlite *db){ + return db->lsChange; +} + /* ** Close an existing SQLite database */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4fb8953d7..2bdbdb021 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.214 2004/02/20 14:50:58 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.215 2004/02/20 22:53:39 rdc Exp $ */ #include "config.h" #include "sqlite.h" @@ -309,6 +309,24 @@ struct Db { ** are stored. If 1, then a file is created to hold those tables. If ** 2, then they are held in memory. 0 means use the default value in ** the TEMP_STORE macro. +** +** The sqlite.lastRowid records the last insert rowid generated by an +** insert statement. Inserts on views do not affect its value. Each +** trigger has its own context, so that lastRowid can be updated inside +** triggers as usual. The previous value will be restored once the trigger +** exits. Upon entering a before or instead of trigger, lastRowid is no +** longer (since after version 2.8.12) reset to -1. +** +** The sqlite.nChange does not count changes within triggers and keeps no +** context. It is reset at start of sqlite_exec. +** The sqlite.lsChange represents the number of changes made by the last +** insert, update, or delete statement. It remains constant throughout the +** length of a statement and is then updated by OP_SetCounts. It keeps a +** context stack just like lastRowid so that the count of changes +** within a trigger is not seen outside the trigger. Changes to views do not +** affect the value of lsChange. +** The sqlite.csChange keeps track of the number of current changes (since +** the last statement) and is used to update sqlite_lsChange. */ struct sqlite { int nDb; /* Number of backends currently in use */ @@ -328,10 +346,12 @@ struct sqlite { void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*);/* Invoked at every commit. */ Hash aFunc; /* All functions that can be in SQL exprs */ - int lastRowid; /* ROWID of most recent insert */ + int lastRowid; /* ROWID of most recent insert (see above) */ int priorNewRowid; /* Last randomly generated ROWID */ int magic; /* Magic number for detect library misuse */ - int nChange; /* Number of rows changed */ + int nChange; /* Number of rows changed (see above) */ + int lsChange; /* Last statement change count (see above) */ + int csChange; /* Current statement change count (see above) */ struct sqliteInitInfo { /* Information used during initialization */ int iDb; /* When back is being initialized */ int newTnum; /* Rootpage of table being initialized */ @@ -908,6 +928,13 @@ struct AuthContext { }; /* +** Bitfield flags for P2 value in OP_PutIntKey and OP_Delete +*/ +#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */ +#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */ +#define OPFLAG_CSCHANGE 4 /* Set to update db->csChange */ + +/* * Each trigger present in the database schema is stored as an instance of * struct Trigger. * diff --git a/src/trigger.c b/src/trigger.c index e727dcc0c..d8a501f05 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -746,7 +746,9 @@ int sqliteCodeRowTrigger( sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1); sqliteExprDelete(whenExpr); + sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPush, 0, 0); codeTriggerProgram(pParse, pTrigger->step_list, orconf); + sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPop, 0, 0); /* Pop the entry off the trigger stack */ pParse->trigStack = pParse->trigStack->pNext; diff --git a/src/update.c b/src/update.c index 75f006a66..41077e152 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.68 2004/02/16 03:44:02 drh Exp $ +** $Id: update.c,v 1.69 2004/02/20 22:53:39 rdc Exp $ */ #include "sqliteInt.h" @@ -430,6 +430,7 @@ void sqliteUpdate( sqliteVdbeAddOp(v, OP_Close, oldIdx, 0); } + sqliteVdbeAddOp(v, OP_SetCounts, 0, 0); sqliteEndWriteOperation(pParse); /* diff --git a/src/vdbe.c b/src/vdbe.c index e6731d3f8..d28b943ce 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.265 2004/02/16 03:44:02 drh Exp $ +** $Id: vdbe.c,v 1.266 2004/02/20 22:53:39 rdc Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -2895,9 +2895,12 @@ case OP_NewRecno: { ** stack. The key is the next value down on the stack. The key must ** be an integer. The stack is popped twice by this instruction. ** -** If P2==1 then the row change count is incremented. If P2==0 the -** row change count is unmodified. The rowid is stored for subsequent -** return by the sqlite_last_insert_rowid() function if P2 is 1. +** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is +** incremented (otherwise not). If the OPFLAG_CSCHANGE flag is set, +** then the current statement change count is incremented (otherwise not). +** If the OPFLAG_LASTROWID flag of P2 is set, then rowid is +** stored for subsequent return by the sqlite_last_insert_rowid() function +** (otherwise it's unmodified). */ /* Opcode: PutStrKey P1 * * ** @@ -2928,10 +2931,9 @@ case OP_PutStrKey: { nKey = sizeof(int); iKey = intToKey(pNos->i); zKey = (char*)&iKey; - if( pOp->p2 ){ - db->nChange++; - db->lastRowid = pNos->i; - } + if( pOp->p2 & OPFLAG_NCHANGE ) db->nChange++; + if( pOp->p2 & OPFLAG_LASTROWID ) db->lastRowid = pNos->i; + if( pOp->p2 & OPFLAG_CSCHANGE ) db->csChange++; if( pC->nextRowidValid && pTos->i>=pC->nextRowid ){ pC->nextRowidValid = 0; } @@ -2980,8 +2982,9 @@ case OP_PutStrKey: { ** the next Next instruction will be a no-op. Hence it is OK to delete ** a record from within an Next loop. ** -** The row change counter is incremented if P2==1 and is unmodified -** if P2==0. +** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is +** incremented (otherwise not). If OPFLAG_CSCHANGE flag is set, +** then the current statement change count is incremented (otherwise not). ** ** If P1 is a pseudo-table, then this instruction is a no-op. */ @@ -2995,7 +2998,19 @@ case OP_Delete: { rc = sqliteBtreeDelete(pC->pCursor); pC->nextRowidValid = 0; } - if( pOp->p2 ) db->nChange++; + if( pOp->p2 & OPFLAG_NCHANGE ) db->nChange++; + if( pOp->p2 & OPFLAG_CSCHANGE ) db->csChange++; + break; +} + +/* Opcode: SetCounts * * * +** +** Called at end of statement. Updates lsChange (last statement change count) +** and resets csChange (current statement change count) to 0. +*/ +case OP_SetCounts: { + db->lsChange=db->csChange; + db->csChange=0; break; } @@ -3850,6 +3865,43 @@ case OP_ListPop: { break; } +/* Opcode: ContextPush * * * +** +** Save the current Vdbe context such that it can be restored by a ContextPop +** opcode. The context stores the last insert row id, the last statement change +** count, and the current statement change count. +*/ +case OP_ContextPush: { + p->contextStackDepth++; + assert(p->contextStackDepth > 0); + p->contextStack = sqliteRealloc(p->contextStack, + sizeof(Context) * p->contextStackDepth); + if( p->contextStack==0 ) goto no_mem; + p->contextStack[p->contextStackDepth - 1].lastRowid = p->db->lastRowid; + p->contextStack[p->contextStackDepth - 1].lsChange = p->db->lsChange; + p->contextStack[p->contextStackDepth - 1].csChange = p->db->csChange; + break; +} + +/* Opcode: ContextPop * * * +** +** Restore the Vdbe context to the state it was in when contextPush was last +** executed. The context stores the last insert row id, the last statement +** change count, and the current statement change count. +*/ +case OP_ContextPop: { + assert(p->contextStackDepth > 0); + p->contextStackDepth--; + p->db->lastRowid = p->contextStack[p->contextStackDepth].lastRowid; + p->db->lsChange = p->contextStack[p->contextStackDepth].lsChange; + p->db->csChange = p->contextStack[p->contextStackDepth].csChange; + if( p->contextStackDepth == 0 ){ + sqliteFree(p->contextStack); + p->contextStack = 0; + } + break; +} + /* Opcode: SortPut * * * ** ** The TOS is the key and the NOS is the data. Pop both from the stack diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 2fcd0a448..79b6b51a5 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -208,6 +208,19 @@ struct Keylist { }; /* +** A Context stores the last insert rowid, the last statement change count, +** and the current statement change count (i.e. changes since last statement). +** Elements of Context structure type make up the ContextStack, which is +** updated by the ContextPush and ContextPop opcodes (used by triggers) +*/ +typedef struct Context Context; +struct Context { + int lastRowid; /* Last insert rowid (from db->lastRowid) */ + int lsChange; /* Last statement change count (from db->lsChange) */ + int csChange; /* Current statement change count (from db->csChange) */ +}; + +/* ** An instance of the virtual machine. This structure contains the complete ** state of the virtual machine. ** @@ -250,6 +263,8 @@ struct Vdbe { Keylist *pList; /* A list of ROWIDs */ int keylistStackDepth; /* The size of the "keylist" stack */ Keylist **keylistStack; /* The stack used by opcodes ListPush & ListPop */ + int contextStackDepth; /* The size of the "context" stack */ + Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/ int pc; /* The program counter */ int rc; /* Value to return */ unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */ |