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
This commit is contained in:
rdc
2004-02-20 22:53:38 +00:00
parent fcabd4641e
commit b0c374ffbb
12 changed files with 163 additions and 38 deletions
+4 -2
View File
@@ -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));
}
+17 -1
View File
@@ -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);
+5 -2
View File
@@ -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);
}
+5 -1
View File
@@ -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
*/
+30 -3
View File
@@ -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 */
@@ -907,6 +927,13 @@ struct AuthContext {
Parse *pParse; /* The Parse structure */
};
/*
** 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.
+2
View File
@@ -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;
+2 -1
View File
@@ -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);
/*
+63 -11
View File
@@ -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
+15
View File
@@ -207,6 +207,19 @@ struct Keylist {
int aKey[1]; /* One or more keys. Extra space allocated as needed */
};
/*
** 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 */