diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 8 | ||||
-rw-r--r-- | src/delete.c | 10 | ||||
-rw-r--r-- | src/insert.c | 159 | ||||
-rw-r--r-- | src/sqliteInt.h | 27 | ||||
-rw-r--r-- | src/tokenize.c | 7 | ||||
-rw-r--r-- | src/update.c | 10 |
6 files changed, 157 insertions, 64 deletions
diff --git a/src/build.c b/src/build.c index 53d03b743..850ec7fa2 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.552 2009/06/18 17:22:39 drh Exp $ +** $Id: build.c,v 1.553 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" @@ -174,6 +174,12 @@ void sqlite3FinishCoding(Parse *pParse){ ** shared-cache feature is enabled. */ codeTableLocks(pParse); + + /* Initialize any AUTOINCREMENT data structures required. + */ + sqlite3AutoincrementBegin(pParse); + + /* Finally, jump back to the beginning of the executable code. */ sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto); } } diff --git a/src/delete.c b/src/delete.c index 95ef3cef7..cbd8b375e 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 ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.203 2009/05/28 01:00:55 drh Exp $ +** $Id: delete.c,v 1.204 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" @@ -475,6 +475,14 @@ void sqlite3DeleteFrom( } } + /* Update the sqlite_sequence table by storing the content of the + ** maximum rowid counter values recorded while inserting into + ** autoincrement tables. + */ + if( pParse->nested==0 && pParse->trigStack==0 ){ + sqlite3AutoincrementEnd(pParse); + } + /* ** Return the number of rows that were deleted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not diff --git a/src/insert.c b/src/insert.c index 27549f001..f6d8b1c39 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.268 2009/05/29 19:00:13 drh Exp $ +** $Id: insert.c,v 1.269 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" @@ -162,22 +162,24 @@ static int readsTable(Vdbe *v, int iStartAddr, int iDb, Table *pTab){ #ifndef SQLITE_OMIT_AUTOINCREMENT /* -** Write out code to initialize the autoincrement logic. This code -** looks up the current autoincrement value in the sqlite_sequence -** table and stores that value in a register. Code generated by -** autoIncStep() will keep that register holding the largest -** rowid value. Code generated by autoIncEnd() will write the new -** largest value of the counter back into the sqlite_sequence table. +** Locate or create an AutoincInfo structure associated with table pTab +** which is in database iDb. Return the register number for the register +** that holds the maximum rowid. ** -** This routine returns the index of the mem[] cell that contains -** the maximum rowid counter. +** There is at most one AutoincInfo structure per table even if the +** same table is autoincremented multiple times due to inserts within +** triggers. A new AutoincInfo structure is created if this is the +** first use of table pTab. On 2nd and subsequent uses, the original +** AutoincInfo structure is used. ** -** Three consecutive registers are allocated by this routine. The -** first two hold the name of the target table and the maximum rowid -** inserted into the target table, respectively. -** The third holds the rowid in sqlite_sequence where we will -** write back the revised maximum rowid. This routine returns the -** index of the second of these three registers. +** Three memory locations are allocated: +** +** (1) Register to hold the name of the pTab table. +** (2) Register to hold the maximum ROWID of pTab. +** (3) Register to hold the rowid in sqlite_sequence of pTab +** +** The 2nd register is the one that is returned. That is all the +** insert routine needs to know about. */ static int autoIncBegin( Parse *pParse, /* Parsing context */ @@ -186,29 +188,56 @@ static int autoIncBegin( ){ int memId = 0; /* Register holding maximum rowid */ if( pTab->tabFlags & TF_Autoincrement ){ - Vdbe *v = pParse->pVdbe; - Db *pDb = &pParse->db->aDb[iDb]; - int iCur = pParse->nTab++; - int addr; /* Address of the top of the loop */ - assert( v ); - pParse->nMem++; /* Holds name of table */ - memId = ++pParse->nMem; - pParse->nMem++; - sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenRead); + AutoincInfo *pInfo; + + pInfo = pParse->pAinc; + while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } + if( pInfo==0 ){ + pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo)); + if( pInfo==0 ) return 0; + pInfo->pNext = pParse->pAinc; + pParse->pAinc = pInfo; + pInfo->pTab = pTab; + pInfo->iDb = iDb; + pParse->nMem++; /* Register to hold name of table */ + pInfo->regCtr = ++pParse->nMem; /* Max rowid register */ + pParse->nMem++; /* Rowid in sqlite_sequence */ + } + memId = pInfo->regCtr; + } + return memId; +} + +/* +** This routine generates code that will initialize all of the +** register used by the autoincrement tracker. +*/ +void sqlite3AutoincrementBegin(Parse *pParse){ + AutoincInfo *p; /* Information about an AUTOINCREMENT */ + sqlite3 *db = pParse->db; /* The database connection */ + Db *pDb; /* Database only autoinc table */ + int memId; /* Register holding max rowid */ + int addr; /* A VDBE address */ + Vdbe *v = pParse->pVdbe; /* VDBE under construction */ + + assert( v ); /* We failed long ago if this is not so */ + for(p = pParse->pAinc; p; p = p->pNext){ + pDb = &db->aDb[p->iDb]; + memId = p->regCtr; + sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); addr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, pTab->zName, 0); - sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addr+9); - sqlite3VdbeAddOp3(v, OP_Column, iCur, 0, memId); + sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); + sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); + sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId); sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, memId+1); - sqlite3VdbeAddOp3(v, OP_Column, iCur, 1, memId); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); + sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId); sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9); - sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+2); + sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); sqlite3VdbeAddOp2(v, OP_Integer, 0, memId); - sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); + sqlite3VdbeAddOp0(v, OP_Close); } - return memId; } /* @@ -226,32 +255,43 @@ static void autoIncStep(Parse *pParse, int memId, int regRowid){ } /* -** After doing one or more inserts, the maximum rowid is stored -** in reg[memId]. Generate code to write this value back into the -** the sqlite_sequence table. +** This routine generates the code needed to write autoincrement +** maximum rowid values back into the sqlite_sequence register. +** Every statement that might do an INSERT into an autoincrement +** table (either directly or through triggers) needs to call this +** routine just before the "exit" code. */ -static void autoIncEnd( - Parse *pParse, /* The parsing context */ - int iDb, /* Index of the database holding pTab */ - Table *pTab, /* Table we are inserting into */ - int memId /* Memory cell holding the maximum rowid */ -){ - if( pTab->tabFlags & TF_Autoincrement ){ - int iCur = pParse->nTab++; - Vdbe *v = pParse->pVdbe; - Db *pDb = &pParse->db->aDb[iDb]; - int j1; - int iRec = ++pParse->nMem; /* Memory cell used for record */ - - assert( v ); - sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); +void sqlite3AutoincrementEnd(Parse *pParse){ + AutoincInfo *p; + Vdbe *v = pParse->pVdbe; + sqlite3 *db = pParse->db; + + assert( v ); + for(p = pParse->pAinc; p; p = p->pNext){ + Db *pDb = &db->aDb[p->iDb]; + int j1, j2, j3, j4, j5; + int iRec; + int memId = p->regCtr; + + iRec = sqlite3GetTempReg(pParse); + sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); - sqlite3VdbeAddOp2(v, OP_NewRowid, iCur, memId+1); + j2 = sqlite3VdbeAddOp0(v, OP_Rewind); + j3 = sqlite3VdbeAddOp3(v, OP_Column, 0, 0, iRec); + j4 = sqlite3VdbeAddOp3(v, OP_Eq, memId-1, 0, iRec); + sqlite3VdbeAddOp2(v, OP_Next, 0, j3); + sqlite3VdbeJumpHere(v, j2); + sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1); + j5 = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeJumpHere(v, j4); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, j5); sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec); - sqlite3VdbeAddOp3(v, OP_Insert, iCur, iRec, memId+1); + sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - sqlite3VdbeAddOp1(v, OP_Close, iCur); + sqlite3VdbeAddOp0(v, OP_Close); + sqlite3ReleaseTempReg(pParse, iRec); } } #else @@ -261,7 +301,6 @@ static void autoIncEnd( */ # define autoIncBegin(A,B,C) (0) # define autoIncStep(A,B,C) -# define autoIncEnd(A,B,C,D) #endif /* SQLITE_OMIT_AUTOINCREMENT */ @@ -507,7 +546,7 @@ void sqlite3Insert( if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ assert( !pTrigger ); assert( pList==0 ); - goto insert_cleanup; + goto insert_end; } #endif /* SQLITE_OMIT_XFER_OPT */ @@ -989,11 +1028,14 @@ void sqlite3Insert( } } +insert_end: /* Update the sqlite_sequence table by storing the content of the - ** counter value in memory regAutoinc back into the sqlite_sequence - ** table. + ** maximum rowid counter values recorded while inserting into + ** autoincrement tables. */ - autoIncEnd(pParse, iDb, pTab, regAutoinc); + if( pParse->nested==0 && pParse->trigStack==0 ){ + sqlite3AutoincrementEnd(pParse); + } /* ** Return the number of rows inserted. If this routine is @@ -1716,7 +1758,6 @@ static int xferOptimization( sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); sqlite3VdbeChangeP4(v, -1, pDest->zName, 0); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); - autoIncEnd(pParse, iDbDest, pDest, regAutoinc); for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index b0d047b71..81c8e4630 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.886 2009/06/19 14:06:03 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.887 2009/06/23 20:28:54 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -581,6 +581,7 @@ struct BusyHandler { */ typedef struct AggInfo AggInfo; typedef struct AuthContext AuthContext; +typedef struct AutoincInfo AutoincInfo; typedef struct Bitvec Bitvec; typedef struct RowSet RowSet; typedef struct CollSeq CollSeq; @@ -1927,6 +1928,22 @@ struct SelectDest { }; /* +** During code generation of statements that do inserts into AUTOINCREMENT +** tables, the following information is attached to the Table.u.autoInc.p +** pointer of each autoincrement table to record some side information that +** the code generator needs. We have to keep per-table autoincrement +** information in case inserts are down within triggers. Triggers do not +** normally coordinate their activities, but we do need to coordinate the +** loading and saving of autoincrement information. +*/ +struct AutoincInfo { + AutoincInfo *pNext; /* Next info block in a list of them all */ + Table *pTab; /* Table this info block refers to */ + int iDb; /* Index in sqlite3.aDb[] of database holding pTab */ + int regCtr; /* Memory register holding the rowid counter */ +}; + +/* ** Size of the column cache */ #ifndef SQLITE_N_COLCACHE @@ -1992,6 +2009,7 @@ struct Parse { #endif int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRoot; /* Register holding root page number for new objects */ + AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ @@ -2482,6 +2500,13 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int); void sqlite3DropTable(Parse*, SrcList*, int, int); void sqlite3DeleteTable(Table*); +#ifndef SQLITE_OMIT_AUTOINCREMENT + void sqlite3AutoincrementBegin(Parse *pParse); + void sqlite3AutoincrementEnd(Parse *pParse); +#else +# define sqlite3AutoincrementBegin(X) +# define sqlite3AutoincrementEnd(X) +#endif void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); void *sqlite3ArrayAllocate(sqlite3*,void*,int,int,int*,int*,int*); IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); diff --git a/src/tokenize.c b/src/tokenize.c index 6761f8c3c..3fdb38ede 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -15,7 +15,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.161 2009/06/17 01:17:13 drh Exp $ +** $Id: tokenize.c,v 1.162 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" #include <stdlib.h> @@ -518,6 +518,11 @@ abort_parse: sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->apVarExpr); sqlite3DbFree(db, pParse->aAlias); + while( pParse->pAinc ){ + AutoincInfo *p = pParse->pAinc; + pParse->pAinc = p->pNext; + sqlite3DbFree(db, p); + } while( pParse->pZombieTab ){ Table *p = pParse->pZombieTab; pParse->pZombieTab = p->pNextZombie; diff --git a/src/update.c b/src/update.c index 6eb92fb61..12348c91a 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.202 2009/05/28 01:00:55 drh Exp $ +** $Id: update.c,v 1.203 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" @@ -564,6 +564,14 @@ void sqlite3Update( sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0); } + /* Update the sqlite_sequence table by storing the content of the + ** maximum rowid counter values recorded while inserting into + ** autoincrement tables. + */ + if( pParse->nested==0 && pParse->trigStack==0 ){ + sqlite3AutoincrementEnd(pParse); + } + /* ** Return the number of rows that were changed. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not |