1
0
Fork 0
mirror of https://github.com/DBD-SQLite/DBD-SQLite synced 2025-06-07 22:28:47 -04:00

DBD-SQLite: bumped up the bundled library to 3.6.12

This commit is contained in:
Kenichi Ishigaki 2009-03-31 15:31:13 +00:00
parent 735d55875f
commit ebc9d17ee9
54 changed files with 6121 additions and 5266 deletions

View file

@ -12,7 +12,7 @@
** This file contains C code routines that used to generate VDBE code ** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command. ** that implements the ALTER TABLE command.
** **
** $Id: alter.c,v 1.53 2009/02/13 03:43:32 drh Exp $ ** $Id: alter.c,v 1.55 2009/03/24 15:08:10 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -193,7 +193,7 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
*/ */
if( pTab->pSchema!=pTempSchema ){ if( pTab->pSchema!=pTempSchema ){
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
if( pTrig->pSchema==pTempSchema ){ if( pTrig->pSchema==pTempSchema ){
if( !zWhere ){ if( !zWhere ){
zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name); zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name);
@ -232,7 +232,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
/* Drop any table triggers from the internal schema. */ /* Drop any table triggers from the internal schema. */
for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){ for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
assert( iTrigDb==iDb || iTrigDb==1 ); assert( iTrigDb==iDb || iTrigDb==1 );
sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0); sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0);
@ -593,7 +593,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
if( !pNew ) goto exit_begin_add_column; if( !pNew ) goto exit_begin_add_column;
pParse->pNewTable = pNew; pParse->pNewTable = pNew;
pNew->nRef = 1; pNew->nRef = 1;
pNew->db = db; pNew->dbMem = pTab->dbMem;
pNew->nCol = pTab->nCol; pNew->nCol = pTab->nCol;
assert( pNew->nCol>0 ); assert( pNew->nCol>0 );
nAlloc = (((pNew->nCol-1)/8)*8)+8; nAlloc = (((pNew->nCol-1)/8)*8)+8;

View file

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file contains code associated with the ANALYZE command. ** This file contains code associated with the ANALYZE command.
** **
** @(#) $Id: analyze.c,v 1.48 2009/02/13 16:59:53 drh Exp $ ** @(#) $Id: analyze.c,v 1.51 2009/02/28 10:47:42 danielk1977 Exp $
*/ */
#ifndef SQLITE_OMIT_ANALYZE #ifndef SQLITE_OMIT_ANALYZE
#include "sqliteInt.h" #include "sqliteInt.h"
@ -74,8 +74,8 @@ static void openStatTable(
if( !createStat1 ){ if( !createStat1 ){
sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1"); sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1");
} }
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 3);
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb); sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb);
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
sqlite3VdbeChangeP5(v, createStat1); sqlite3VdbeChangeP5(v, createStat1);
} }
@ -117,7 +117,7 @@ static void analyzeOneTable(
/* Establish a read-lock on the table at the shared-cache level. */ /* Establish a read-lock on the table at the shared-cache level. */
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
iIdxCur = pParse->nTab; iIdxCur = pParse->nTab++;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
int regFields; /* Register block for building records */ int regFields; /* Register block for building records */
@ -131,7 +131,6 @@ static void analyzeOneTable(
*/ */
assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) ); assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
nCol = pIdx->nColumn; nCol = pIdx->nColumn;
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nCol+1);
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
(char *)pKey, P4_KEYINFO_HANDOFF); (char *)pKey, P4_KEYINFO_HANDOFF);
VdbeComment((v, "%s", pIdx->zName)); VdbeComment((v, "%s", pIdx->zName));
@ -189,7 +188,7 @@ static void analyzeOneTable(
** The result is a single row of the sqlite_stat1 table. The first ** The result is a single row of the sqlite_stat1 table. The first
** two columns are the names of the table and index. The third column ** two columns are the names of the table and index. The third column
** is a string composed of a list of integer statistics about the ** is a string composed of a list of integer statistics about the
** index. The first integer in the list is the total number of entires ** index. The first integer in the list is the total number of entries
** in the index. There is one additional integer in the list for each ** in the index. There is one additional integer in the list for each
** column of the table. This additional integer is a guess of how many ** column of the table. This additional integer is a guess of how many
** rows of the table the index will select. If D is the count of distinct ** rows of the table the index will select. If D is the count of distinct

View file

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands. ** This file contains code used to implement the ATTACH and DETACH commands.
** **
** $Id: attach.c,v 1.82 2009/02/03 16:51:25 danielk1977 Exp $ ** $Id: attach.c,v 1.83 2009/02/19 14:39:25 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -488,11 +488,11 @@ int sqlite3FixExpr(
Expr *pExpr /* The expression to be fixed to one database */ Expr *pExpr /* The expression to be fixed to one database */
){ ){
while( pExpr ){ while( pExpr ){
if( sqlite3FixSelect(pFix, pExpr->pSelect) ){ if( ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_SpanOnly) ) break;
return 1; if( ExprHasProperty(pExpr, EP_xIsSelect) ){
} if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
if( sqlite3FixExprList(pFix, pExpr->pList) ){ }else{
return 1; if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1;
} }
if( sqlite3FixExpr(pFix, pExpr->pRight) ){ if( sqlite3FixExpr(pFix, pExpr->pRight) ){
return 1; return 1;

View file

@ -12,7 +12,7 @@
** This file contains the implementation of the sqlite3_backup_XXX() ** This file contains the implementation of the sqlite3_backup_XXX()
** API functions and the related features. ** API functions and the related features.
** **
** $Id: backup.c,v 1.12 2009/02/16 17:55:47 shane Exp $ ** $Id: backup.c,v 1.13 2009/03/16 13:19:36 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "btreeInt.h" #include "btreeInt.h"
@ -292,10 +292,10 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
int bCloseTrans = 0; /* True if src db requires unlocking */ int bCloseTrans = 0; /* True if src db requires unlocking */
/* If the source pager is currently in a write-transaction, return /* If the source pager is currently in a write-transaction, return
** SQLITE_LOCKED immediately. ** SQLITE_BUSY immediately.
*/ */
if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
rc = SQLITE_LOCKED; rc = SQLITE_BUSY;
}else{ }else{
rc = SQLITE_OK; rc = SQLITE_OK;
} }

View file

@ -10,7 +10,7 @@
** **
************************************************************************* *************************************************************************
** **
** $Id: btmutex.c,v 1.12 2008/11/17 19:18:55 danielk1977 Exp $ ** $Id: btmutex.c,v 1.13 2009/03/05 04:20:32 shane Exp $
** **
** This file contains code used to implement mutexes on Btree objects. ** This file contains code used to implement mutexes on Btree objects.
** This code really belongs in btree.c. But btree.c is getting too ** This code really belongs in btree.c. But btree.c is getting too
@ -113,7 +113,7 @@ void sqlite3BtreeLeave(Btree *p){
/* /*
** Return true if the BtShared mutex is held on the btree. ** Return true if the BtShared mutex is held on the btree.
** **
** This routine makes no determination one why or another if the ** This routine makes no determination one way or another if the
** database connection mutex is held. ** database connection mutex is held.
** **
** This routine is used only from within assert() statements. ** This routine is used only from within assert() statements.

470
btree.c
View file

@ -9,7 +9,7 @@
** May you share freely, never taking more than you give. ** May you share freely, never taking more than you give.
** **
************************************************************************* *************************************************************************
** $Id: btree.c,v 1.565 2009/02/04 01:49:30 shane Exp $ ** $Id: btree.c,v 1.582 2009/03/30 18:50:05 danielk1977 Exp $
** **
** This file implements a external (disk-based) database using BTrees. ** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information. ** See the header comment on "btreeInt.h" for additional information.
@ -42,6 +42,8 @@ int sqlite3BtreeTrace=0; /* True to enable tracing */
** in shared cache. This variable has file scope during normal builds, ** in shared cache. This variable has file scope during normal builds,
** but the test harness needs to access it so we make it global for ** but the test harness needs to access it so we make it global for
** test builds. ** test builds.
**
** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER.
*/ */
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
BtShared *SQLITE_WSD sqlite3SharedCacheList = 0; BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
@ -68,31 +70,32 @@ int sqlite3_enable_shared_cache(int enable){
/* /*
** Forward declaration ** Forward declaration
*/ */
static int checkReadLocks(Btree*, Pgno, BtCursor*, i64); static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64);
#ifdef SQLITE_OMIT_SHARED_CACHE #ifdef SQLITE_OMIT_SHARED_CACHE
/* /*
** The functions queryTableLock(), lockTable() and unlockAllTables() ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(),
** and clearAllSharedCacheTableLocks()
** manipulate entries in the BtShared.pLock linked list used to store ** manipulate entries in the BtShared.pLock linked list used to store
** shared-cache table level locks. If the library is compiled with the ** shared-cache table level locks. If the library is compiled with the
** shared-cache feature disabled, then there is only ever one user ** shared-cache feature disabled, then there is only ever one user
** of each BtShared structure and so this locking is not necessary. ** of each BtShared structure and so this locking is not necessary.
** So define the lock related functions as no-ops. ** So define the lock related functions as no-ops.
*/ */
#define queryTableLock(a,b,c) SQLITE_OK #define querySharedCacheTableLock(a,b,c) SQLITE_OK
#define lockTable(a,b,c) SQLITE_OK #define setSharedCacheTableLock(a,b,c) SQLITE_OK
#define unlockAllTables(a) #define clearAllSharedCacheTableLocks(a)
#endif #endif
#ifndef SQLITE_OMIT_SHARED_CACHE #ifndef SQLITE_OMIT_SHARED_CACHE
/* /*
** Query to see if btree handle p may obtain a lock of type eLock ** Query to see if btree handle p may obtain a lock of type eLock
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
** SQLITE_OK if the lock may be obtained (by calling lockTable()), or ** SQLITE_OK if the lock may be obtained (by calling
** SQLITE_LOCKED if not. ** setSharedCacheTableLock()), or SQLITE_LOCKED if not.
*/ */
static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
BtLock *pIter; BtLock *pIter;
@ -108,23 +111,26 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
/* If some other connection is holding an exclusive lock, the /* If some other connection is holding an exclusive lock, the
** requested lock may not be obtained. ** requested lock may not be obtained.
*/ */
if( pBt->pExclusive && pBt->pExclusive!=p ){ if( pBt->pWriter!=p && pBt->isExclusive ){
return SQLITE_LOCKED; sqlite3ConnectionBlocked(p->db, pBt->pWriter->db);
return SQLITE_LOCKED_SHAREDCACHE;
} }
/* This (along with lockTable()) is where the ReadUncommitted flag is /* This (along with setSharedCacheTableLock()) is where
** dealt with. If the caller is querying for a read-lock and the flag is ** the ReadUncommitted flag is dealt with.
** set, it is unconditionally granted - even if there are write-locks ** If the caller is querying for a read-lock on any table
** other than the sqlite_master table (table 1) and if the ReadUncommitted
** flag is set, then the lock granted even if there are write-locks
** on the table. If a write-lock is requested, the ReadUncommitted flag ** on the table. If a write-lock is requested, the ReadUncommitted flag
** is not considered. ** is not considered.
** **
** In function lockTable(), if a read-lock is demanded and the ** In function setSharedCacheTableLock(), if a read-lock is demanded and the
** ReadUncommitted flag is set, no entry is added to the locks list ** ReadUncommitted flag is set, no entry is added to the locks list
** (BtShared.pLock). ** (BtShared.pLock).
** **
** To summarize: If the ReadUncommitted flag is set, then read cursors do ** To summarize: If the ReadUncommitted flag is set, then read cursors
** not create or respect table locks. The locking procedure for a ** on non-schema tables do not create or respect table locks. The locking
** write-cursor does not change. ** procedure for a write-cursor does not change.
*/ */
if( if(
0==(p->db->flags&SQLITE_ReadUncommitted) || 0==(p->db->flags&SQLITE_ReadUncommitted) ||
@ -134,7 +140,12 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->pBtree!=p && pIter->iTable==iTab && if( pIter->pBtree!=p && pIter->iTable==iTab &&
(pIter->eLock!=eLock || eLock!=READ_LOCK) ){ (pIter->eLock!=eLock || eLock!=READ_LOCK) ){
return SQLITE_LOCKED; sqlite3ConnectionBlocked(p->db, pIter->pBtree->db);
if( eLock==WRITE_LOCK ){
assert( p==pBt->pWriter );
pBt->isPending = 1;
}
return SQLITE_LOCKED_SHAREDCACHE;
} }
} }
} }
@ -151,7 +162,7 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and ** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and
** SQLITE_NOMEM may also be returned. ** SQLITE_NOMEM may also be returned.
*/ */
static int lockTable(Btree *p, Pgno iTable, u8 eLock){ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
BtLock *pLock = 0; BtLock *pLock = 0;
BtLock *pIter; BtLock *pIter;
@ -165,12 +176,13 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
return SQLITE_OK; return SQLITE_OK;
} }
assert( SQLITE_OK==queryTableLock(p, iTable, eLock) ); assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) );
/* If the read-uncommitted flag is set and a read-lock is requested, /* If the read-uncommitted flag is set and a read-lock is requested on
** return early without adding an entry to the BtShared.pLock list. See ** a non-schema table, then the lock is always granted. Return early
** comment in function queryTableLock() for more info on handling ** without adding an entry to the BtShared.pLock list. See
** the ReadUncommitted flag. ** comment in function querySharedCacheTableLock() for more info
** on handling the ReadUncommitted flag.
*/ */
if( if(
(p->db->flags&SQLITE_ReadUncommitted) && (p->db->flags&SQLITE_ReadUncommitted) &&
@ -217,10 +229,10 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
#ifndef SQLITE_OMIT_SHARED_CACHE #ifndef SQLITE_OMIT_SHARED_CACHE
/* /*
** Release all the table locks (locks obtained via calls to the lockTable() ** Release all the table locks (locks obtained via calls to
** procedure) held by Btree handle p. ** the setSharedCacheTableLock() procedure) held by Btree handle p.
*/ */
static void unlockAllTables(Btree *p){ static void clearAllSharedCacheTableLocks(Btree *p){
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
BtLock **ppIter = &pBt->pLock; BtLock **ppIter = &pBt->pLock;
@ -229,7 +241,7 @@ static void unlockAllTables(Btree *p){
while( *ppIter ){ while( *ppIter ){
BtLock *pLock = *ppIter; BtLock *pLock = *ppIter;
assert( pBt->pExclusive==0 || pBt->pExclusive==pLock->pBtree ); assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree );
if( pLock->pBtree==p ){ if( pLock->pBtree==p ){
*ppIter = pLock->pNext; *ppIter = pLock->pNext;
sqlite3_free(pLock); sqlite3_free(pLock);
@ -238,8 +250,22 @@ static void unlockAllTables(Btree *p){
} }
} }
if( pBt->pExclusive==p ){ assert( pBt->isPending==0 || pBt->pWriter );
pBt->pExclusive = 0; if( pBt->pWriter==p ){
pBt->pWriter = 0;
pBt->isExclusive = 0;
pBt->isPending = 0;
}else if( pBt->nTransaction==2 ){
/* This function is called when connection p is concluding its
** transaction. If there currently exists a writer, and p is not
** that writer, then the number of locks held by connections other
** than the writer must be about to drop to zero. In this case
** set the isPending flag to 0.
**
** If there is not currently a writer, then BtShared.isPending must
** be zero already. So this next line is harmless in that case.
*/
pBt->isPending = 0;
} }
} }
#endif /* SQLITE_OMIT_SHARED_CACHE */ #endif /* SQLITE_OMIT_SHARED_CACHE */
@ -830,7 +856,7 @@ static int defragmentPage(MemPage *pPage){
** **
** If the page contains nBytes of free space but does not contain ** If the page contains nBytes of free space but does not contain
** nBytes of contiguous free space, then this routine automatically ** nBytes of contiguous free space, then this routine automatically
** calls defragementPage() to consolidate all free space before ** calls defragmentPage() to consolidate all free space before
** allocating the new chunk. ** allocating the new chunk.
*/ */
static int allocateSpace(MemPage *pPage, int nByte){ static int allocateSpace(MemPage *pPage, int nByte){
@ -1279,6 +1305,12 @@ static void pageReinit(DbPage *pData){
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pPage->isInit = 0; pPage->isInit = 0;
if( sqlite3PagerPageRefcount(pData)>0 ){ if( sqlite3PagerPageRefcount(pData)>0 ){
/* pPage might not be a btree page; it might be an overflow page
** or ptrmap page or a free page. In those cases, the following
** call to sqlite3BtreeInitPage() will likely return SQLITE_CORRUPT.
** But no harm is done by this. And it is very important that
** sqlite3BtreeInitPage() be called on every btree page so we make
** the call for every page that comes in for re-initing. */
sqlite3BtreeInitPage(pPage); sqlite3BtreeInitPage(pPage);
} }
} }
@ -1310,12 +1342,13 @@ int sqlite3BtreeOpen(
int flags, /* Options */ int flags, /* Options */
int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */ int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */
){ ){
sqlite3_vfs *pVfs; /* The VFS to use for this btree */ sqlite3_vfs *pVfs; /* The VFS to use for this btree */
BtShared *pBt = 0; /* Shared part of btree structure */ BtShared *pBt = 0; /* Shared part of btree structure */
Btree *p; /* Handle to return */ Btree *p; /* Handle to return */
int rc = SQLITE_OK; sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */
u8 nReserve; int rc = SQLITE_OK; /* Result code from this function */
unsigned char zDbHeader[100]; u8 nReserve; /* Byte of unused space on each page */
unsigned char zDbHeader[100]; /* Database header content */
/* Set the variable isMemdb to true for an in-memory database, or /* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database. This symbol is only required if ** false for a file-based database. This symbol is only required if
@ -1361,6 +1394,8 @@ int sqlite3BtreeOpen(
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
sqlite3_mutex_enter(mutexOpen);
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(mutexShared); sqlite3_mutex_enter(mutexShared);
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){ for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
@ -1514,6 +1549,10 @@ btree_open_out:
sqlite3_free(p); sqlite3_free(p);
*ppBtree = 0; *ppBtree = 0;
} }
if( mutexOpen ){
assert( sqlite3_mutex_held(mutexOpen) );
sqlite3_mutex_leave(mutexOpen);
}
return rc; return rc;
} }
@ -1740,6 +1779,12 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
int sqlite3BtreeGetPageSize(Btree *p){ int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize; return p->pBt->pageSize;
} }
/*
** Return the number of bytes of space at the end of every page that
** are intentually left unused. This is the "reserved" space that is
** sometimes used by extensions.
*/
int sqlite3BtreeGetReserve(Btree *p){ int sqlite3BtreeGetReserve(Btree *p){
int n; int n;
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
@ -1774,13 +1819,14 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
#else #else
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
int rc = SQLITE_OK; int rc = SQLITE_OK;
u8 av = autoVacuum ?1:0; u8 av = (u8)autoVacuum;
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
if( pBt->pageSizeFixed && av!=pBt->autoVacuum ){ if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){
rc = SQLITE_READONLY; rc = SQLITE_READONLY;
}else{ }else{
pBt->autoVacuum = av; pBt->autoVacuum = av ?1:0;
pBt->incrVacuum = av==2 ?1:0;
} }
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return rc; return rc;
@ -1965,7 +2011,6 @@ static void unlockBtreeIfUnused(BtShared *pBt){
releasePage(pBt->pPage1); releasePage(pBt->pPage1);
} }
pBt->pPage1 = 0; pBt->pPage1 = 0;
pBt->inStmt = 0;
} }
} }
@ -2047,6 +2092,7 @@ static int newDatabase(BtShared *pBt){
** proceed. ** proceed.
*/ */
int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
sqlite3 *pBlock = 0;
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
int rc = SQLITE_OK; int rc = SQLITE_OK;
@ -2068,25 +2114,27 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
goto trans_begun; goto trans_begun;
} }
#ifndef SQLITE_OMIT_SHARED_CACHE
/* If another database handle has already opened a write transaction /* If another database handle has already opened a write transaction
** on this shared-btree structure and a second write transaction is ** on this shared-btree structure and a second write transaction is
** requested, return SQLITE_BUSY. ** requested, return SQLITE_LOCKED.
*/ */
if( pBt->inTransaction==TRANS_WRITE && wrflag ){ if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){
rc = SQLITE_BUSY; pBlock = pBt->pWriter->db;
goto trans_begun; }else if( wrflag>1 ){
}
#ifndef SQLITE_OMIT_SHARED_CACHE
if( wrflag>1 ){
BtLock *pIter; BtLock *pIter;
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->pBtree!=p ){ if( pIter->pBtree!=p ){
rc = SQLITE_BUSY; pBlock = pIter->pBtree->db;
goto trans_begun; break;
} }
} }
} }
if( pBlock ){
sqlite3ConnectionBlocked(p->db, pBlock);
rc = SQLITE_LOCKED_SHAREDCACHE;
goto trans_begun;
}
#endif #endif
do { do {
@ -2107,9 +2155,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
} }
} }
if( rc==SQLITE_OK ){ if( rc!=SQLITE_OK ){
if( wrflag ) pBt->inStmt = 0;
}else{
unlockBtreeIfUnused(pBt); unlockBtreeIfUnused(pBt);
} }
}while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && }while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
@ -2124,9 +2170,10 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
pBt->inTransaction = p->inTrans; pBt->inTransaction = p->inTrans;
} }
#ifndef SQLITE_OMIT_SHARED_CACHE #ifndef SQLITE_OMIT_SHARED_CACHE
if( wrflag>1 ){ if( wrflag ){
assert( !pBt->pExclusive ); assert( !pBt->pWriter );
pBt->pExclusive = p; pBt->pWriter = p;
pBt->isExclusive = (u8)(wrflag>1);
} }
#endif #endif
} }
@ -2433,6 +2480,18 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
if( nFin==0 ){ if( nFin==0 ){
iLastPg--; iLastPg--;
while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){
if( PTRMAP_ISPAGE(pBt, iLastPg) ){
MemPage *pPg;
int rc = sqlite3BtreeGetPage(pBt, iLastPg, &pPg, 0);
if( rc!=SQLITE_OK ){
return rc;
}
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
if( rc!=SQLITE_OK ){
return rc;
}
}
iLastPg--; iLastPg--;
} }
sqlite3PagerTruncateImage(pBt->pPager, iLastPg); sqlite3PagerTruncateImage(pBt->pPager, iLastPg);
@ -2445,7 +2504,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
** It performs a single unit of work towards an incremental vacuum. ** It performs a single unit of work towards an incremental vacuum.
** **
** If the incremental vacuum is finished after this function has run, ** If the incremental vacuum is finished after this function has run,
** SQLITE_DONE is returned. If it is not finished, but no error occured, ** SQLITE_DONE is returned. If it is not finished, but no error occurred,
** SQLITE_OK is returned. Otherwise an SQLite error code. ** SQLITE_OK is returned. Otherwise an SQLite error code.
*/ */
int sqlite3BtreeIncrVacuum(Btree *p){ int sqlite3BtreeIncrVacuum(Btree *p){
@ -2608,9 +2667,8 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
return rc; return rc;
} }
pBt->inTransaction = TRANS_READ; pBt->inTransaction = TRANS_READ;
pBt->inStmt = 0;
} }
unlockAllTables(p); clearAllSharedCacheTableLocks(p);
/* If the handle has any kind of transaction open, decrement the transaction /* If the handle has any kind of transaction open, decrement the transaction
** count of the shared btree. If the transaction count reaches 0, set ** count of the shared btree. If the transaction count reaches 0, set
@ -2723,7 +2781,7 @@ int sqlite3BtreeRollback(Btree *p){
rc = saveAllCursors(pBt, 0, 0); rc = saveAllCursors(pBt, 0, 0);
#ifndef SQLITE_OMIT_SHARED_CACHE #ifndef SQLITE_OMIT_SHARED_CACHE
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
/* This is a horrible situation. An IO or malloc() error occured whilst /* This is a horrible situation. An IO or malloc() error occurred whilst
** trying to save cursor positions. If this is an automatic rollback (as ** trying to save cursor positions. If this is an automatic rollback (as
** the result of a constraint, malloc() failure or IO error) then ** the result of a constraint, malloc() failure or IO error) then
** the cache may be internally inconsistent (not contain valid trees) so ** the cache may be internally inconsistent (not contain valid trees) so
@ -2734,7 +2792,7 @@ int sqlite3BtreeRollback(Btree *p){
} }
#endif #endif
btreeIntegrity(p); btreeIntegrity(p);
unlockAllTables(p); clearAllSharedCacheTableLocks(p);
if( p->inTrans==TRANS_WRITE ){ if( p->inTrans==TRANS_WRITE ){
int rc2; int rc2;
@ -2765,7 +2823,6 @@ int sqlite3BtreeRollback(Btree *p){
btreeClearHasContent(pBt); btreeClearHasContent(pBt);
p->inTrans = TRANS_NONE; p->inTrans = TRANS_NONE;
pBt->inStmt = 0;
unlockBtreeIfUnused(pBt); unlockBtreeIfUnused(pBt);
btreeIntegrity(p); btreeIntegrity(p);
@ -2774,29 +2831,33 @@ int sqlite3BtreeRollback(Btree *p){
} }
/* /*
** Start a statement subtransaction. The subtransaction can ** Start a statement subtransaction. The subtransaction can can be rolled
** can be rolled back independently of the main transaction. ** back independently of the main transaction. You must start a transaction
** You must start a transaction before starting a subtransaction. ** before starting a subtransaction. The subtransaction is ended automatically
** The subtransaction is ended automatically if the main transaction ** if the main transaction commits or rolls back.
** commits or rolls back.
**
** Only one subtransaction may be active at a time. It is an error to try
** to start a new subtransaction if another subtransaction is already active.
** **
** Statement subtransactions are used around individual SQL statements ** Statement subtransactions are used around individual SQL statements
** that are contained within a BEGIN...COMMIT block. If a constraint ** that are contained within a BEGIN...COMMIT block. If a constraint
** error occurs within the statement, the effect of that one statement ** error occurs within the statement, the effect of that one statement
** can be rolled back without having to rollback the entire transaction. ** can be rolled back without having to rollback the entire transaction.
**
** A statement sub-transaction is implemented as an anonymous savepoint. The
** value passed as the second parameter is the total number of savepoints,
** including the new anonymous savepoint, open on the B-Tree. i.e. if there
** are no active savepoints and no other statement-transactions open,
** iStatement is 1. This anonymous savepoint can be released or rolled back
** using the sqlite3BtreeSavepoint() function.
*/ */
int sqlite3BtreeBeginStmt(Btree *p){ int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
int rc; int rc;
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
pBt->db = p->db; pBt->db = p->db;
assert( p->inTrans==TRANS_WRITE ); assert( p->inTrans==TRANS_WRITE );
assert( !pBt->inStmt );
assert( pBt->readOnly==0 ); assert( pBt->readOnly==0 );
if( NEVER(p->inTrans!=TRANS_WRITE || pBt->inStmt || pBt->readOnly) ){ assert( iStatement>0 );
assert( iStatement>p->db->nSavepoint );
if( NEVER(p->inTrans!=TRANS_WRITE || pBt->readOnly) ){
rc = SQLITE_INTERNAL; rc = SQLITE_INTERNAL;
}else{ }else{
assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->inTransaction==TRANS_WRITE );
@ -2805,55 +2866,7 @@ int sqlite3BtreeBeginStmt(Btree *p){
** SQL statements. It is illegal to open, release or rollback any ** SQL statements. It is illegal to open, release or rollback any
** such savepoints while the statement transaction savepoint is active. ** such savepoints while the statement transaction savepoint is active.
*/ */
rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint+1); rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement);
pBt->inStmt = 1;
}
sqlite3BtreeLeave(p);
return rc;
}
/*
** Commit the statment subtransaction currently in progress. If no
** subtransaction is active, this is a no-op.
*/
int sqlite3BtreeCommitStmt(Btree *p){
int rc;
BtShared *pBt = p->pBt;
sqlite3BtreeEnter(p);
pBt->db = p->db;
assert( pBt->readOnly==0 );
if( pBt->inStmt ){
int iStmtpoint = p->db->nSavepoint;
rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
}else{
rc = SQLITE_OK;
}
pBt->inStmt = 0;
sqlite3BtreeLeave(p);
return rc;
}
/*
** Rollback the active statement subtransaction. If no subtransaction
** is active this routine is a no-op.
**
** All cursors will be invalidated by this operation. Any attempt
** to use a cursor that was open at the beginning of this operation
** will result in an error.
*/
int sqlite3BtreeRollbackStmt(Btree *p){
int rc = SQLITE_OK;
BtShared *pBt = p->pBt;
sqlite3BtreeEnter(p);
pBt->db = p->db;
assert( pBt->readOnly==0 );
if( pBt->inStmt ){
int iStmtpoint = p->db->nSavepoint;
rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_ROLLBACK, iStmtpoint);
if( rc==SQLITE_OK ){
rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
}
pBt->inStmt = 0;
} }
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return rc; return rc;
@ -2875,7 +2888,6 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
int rc = SQLITE_OK; int rc = SQLITE_OK;
if( p && p->inTrans==TRANS_WRITE ){ if( p && p->inTrans==TRANS_WRITE ){
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
assert( pBt->inStmt==0 );
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) );
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
@ -2937,8 +2949,10 @@ static int btreeCursor(
if( NEVER(pBt->readOnly) ){ if( NEVER(pBt->readOnly) ){
return SQLITE_READONLY; return SQLITE_READONLY;
} }
if( checkReadLocks(p, iTable, 0, 0) ){ rc = checkForReadConflicts(p, iTable, 0, 0);
return SQLITE_LOCKED; if( rc!=SQLITE_OK ){
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
return rc;
} }
} }
@ -2976,6 +2990,7 @@ static int btreeCursor(
} }
pBt->pCursor = pCur; pBt->pCursor = pCur;
pCur->eState = CURSOR_INVALID; pCur->eState = CURSOR_INVALID;
pCur->cachedRowid = 0;
return SQLITE_OK; return SQLITE_OK;
@ -2998,11 +3013,48 @@ int sqlite3BtreeCursor(
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return rc; return rc;
} }
int sqlite3BtreeCursorSize(){
/*
** Return the size of a BtCursor object in bytes.
**
** This interfaces is needed so that users of cursors can preallocate
** sufficient storage to hold a cursor. The BtCursor object is opaque
** to users so they cannot do the sizeof() themselves - they must call
** this routine.
*/
int sqlite3BtreeCursorSize(void){
return sizeof(BtCursor); return sizeof(BtCursor);
} }
/*
** Set the cached rowid value of every cursor in the same database file
** as pCur and having the same root page number as pCur. The value is
** set to iRowid.
**
** Only positive rowid values are considered valid for this cache.
** The cache is initialized to zero, indicating an invalid cache.
** A btree will work fine with zero or negative rowids. We just cannot
** cache zero or negative rowids, which means tables that use zero or
** negative rowids might run a little slower. But in practice, zero
** or negative rowids are very uncommon so this should not be a problem.
*/
void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){
BtCursor *p;
for(p=pCur->pBt->pCursor; p; p=p->pNext){
if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid;
}
assert( pCur->cachedRowid==iRowid );
}
/*
** Return the cached rowid for the given cursor. A negative or zero
** return value indicates that the rowid cache is invalid and should be
** ignored. If the rowid cache has never before been set, then a
** zero is returned.
*/
sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){
return pCur->cachedRowid;
}
/* /*
** Close a cursor. The read lock on the database file is released ** Close a cursor. The read lock on the database file is released
@ -3064,6 +3116,8 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
sqlite3_free(pCur->pKey); sqlite3_free(pCur->pKey);
} }
/* /*
** Make sure the BtCursor* given in the argument has a valid ** Make sure the BtCursor* given in the argument has a valid
** BtCursor.info structure. If it is not already valid, call ** BtCursor.info structure. If it is not already valid, call
@ -3501,7 +3555,7 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
** and data to fit on the local page and for there to be no overflow ** and data to fit on the local page and for there to be no overflow
** pages. When that is so, this routine can be used to access the ** pages. When that is so, this routine can be used to access the
** key and data without making a copy. If the key and/or data spills ** key and data without making a copy. If the key and/or data spills
** onto overflow pages, then accessPayload() must be used to reassembly ** onto overflow pages, then accessPayload() must be used to reassemble
** the key/data and copy it into a preallocated buffer. ** the key/data and copy it into a preallocated buffer.
** **
** The pointer returned by this routine looks directly into the cached ** The pointer returned by this routine looks directly into the cached
@ -4391,8 +4445,15 @@ static int allocateBtreePage(
** at the end of the file instead of one. The first allocated page ** at the end of the file instead of one. The first allocated page
** becomes a new pointer-map page, the second is used by the caller. ** becomes a new pointer-map page, the second is used by the caller.
*/ */
MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno)); TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno));
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
rc = sqlite3BtreeGetPage(pBt, *pPgno, &pPg, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
}
if( rc ) return rc;
(*pPgno)++; (*pPgno)++;
if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ (*pPgno)++; } if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ (*pPgno)++; }
} }
@ -5913,9 +5974,9 @@ static int balance(BtCursor *pCur, int isInsert){
** is not in the ReadUncommmitted state, then this routine returns ** is not in the ReadUncommmitted state, then this routine returns
** SQLITE_LOCKED. ** SQLITE_LOCKED.
** **
** As well as cursors with wrFlag==0, cursors with wrFlag==1 and ** As well as cursors with wrFlag==0, cursors with
** isIncrblobHandle==1 are also considered 'read' cursors. Incremental ** isIncrblobHandle==1 are also considered 'read' cursors because
** blob cursors are used for both reading and writing. ** incremental blob cursors are used for both reading and writing.
** **
** When pgnoRoot is the root page of an intkey table, this function is also ** When pgnoRoot is the root page of an intkey table, this function is also
** responsible for invalidating incremental blob cursors when the table row ** responsible for invalidating incremental blob cursors when the table row
@ -5937,11 +5998,11 @@ static int balance(BtCursor *pCur, int isInsert){
** 3) If both pExclude and iRow are set to zero, no incremental blob ** 3) If both pExclude and iRow are set to zero, no incremental blob
** cursors are invalidated. ** cursors are invalidated.
*/ */
static int checkReadLocks( static int checkForReadConflicts(
Btree *pBtree, Btree *pBtree, /* The database file to check */
Pgno pgnoRoot, Pgno pgnoRoot, /* Look for read cursors on this btree */
BtCursor *pExclude, BtCursor *pExclude, /* Ignore this cursor */
i64 iRow i64 iRow /* The rowid that might be changing */
){ ){
BtCursor *p; BtCursor *p;
BtShared *pBt = pBtree->pBt; BtShared *pBt = pBtree->pBt;
@ -5965,9 +6026,10 @@ static int checkReadLocks(
#endif #endif
){ ){
sqlite3 *dbOther = p->pBtree->db; sqlite3 *dbOther = p->pBtree->db;
if( dbOther==0 || assert(dbOther);
(dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){ if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){
return SQLITE_LOCKED; sqlite3ConnectionBlocked(db, dbOther);
return SQLITE_LOCKED_SHAREDCACHE;
} }
} }
} }
@ -6004,8 +6066,11 @@ int sqlite3BtreeInsert(
assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->inTransaction==TRANS_WRITE );
assert( !pBt->readOnly ); assert( !pBt->readOnly );
assert( pCur->wrFlag ); assert( pCur->wrFlag );
if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, nKey) ){ rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey);
return SQLITE_LOCKED; /* The table pCur points to has a read lock */ if( rc ){
/* The table pCur points to has a read lock */
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
return rc;
} }
if( pCur->eState==CURSOR_FAULT ){ if( pCur->eState==CURSOR_FAULT ){
return pCur->skip; return pCur->skip;
@ -6101,8 +6166,11 @@ int sqlite3BtreeDelete(BtCursor *pCur){
return SQLITE_ERROR; /* The cursor is not pointing to anything */ return SQLITE_ERROR; /* The cursor is not pointing to anything */
} }
assert( pCur->wrFlag ); assert( pCur->wrFlag );
if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, pCur->info.nKey) ){ rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey);
return SQLITE_LOCKED; /* The table pCur points to has a read lock */ if( rc!=SQLITE_OK ){
/* The table pCur points to has a read lock */
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
return rc;
} }
/* Restore the current cursor position (a no-op if the cursor is not in /* Restore the current cursor position (a no-op if the cursor is not in
@ -6487,7 +6555,7 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
pBt->db = p->db; pBt->db = p->db;
assert( p->inTrans==TRANS_WRITE ); assert( p->inTrans==TRANS_WRITE );
if( (rc = checkReadLocks(p, iTable, 0, 1))!=SQLITE_OK ){ if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){
/* nothing to do */ /* nothing to do */
}else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
/* nothing to do */ /* nothing to do */
@ -6533,7 +6601,8 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
** occur. ** occur.
*/ */
if( pBt->pCursor ){ if( pBt->pCursor ){
return SQLITE_LOCKED; sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db);
return SQLITE_LOCKED_SHAREDCACHE;
} }
rc = sqlite3BtreeGetPage(pBt, (Pgno)iTable, &pPage, 0); rc = sqlite3BtreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
@ -6655,9 +6724,10 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
/* Reading a meta-data value requires a read-lock on page 1 (and hence /* Reading a meta-data value requires a read-lock on page 1 (and hence
** the sqlite_master table. We grab this lock regardless of whether or ** the sqlite_master table. We grab this lock regardless of whether or
** not the SQLITE_ReadUncommitted flag is set (the table rooted at page ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page
** 1 is treated as a special case by queryTableLock() and lockTable()). ** 1 is treated as a special case by querySharedCacheTableLock()
** and setSharedCacheTableLock()).
*/ */
rc = queryTableLock(p, 1, READ_LOCK); rc = querySharedCacheTableLock(p, 1, READ_LOCK);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return rc; return rc;
@ -6699,7 +6769,7 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
#endif #endif
/* Grab the read-lock on page 1. */ /* Grab the read-lock on page 1. */
rc = lockTable(p, 1, READ_LOCK); rc = setSharedCacheTableLock(p, 1, READ_LOCK);
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return rc; return rc;
} }
@ -6750,6 +6820,75 @@ int sqlite3BtreeFlags(BtCursor *pCur){
return pPage->aData[pPage->hdrOffset]; return pPage->aData[pPage->hdrOffset];
} }
#ifndef SQLITE_OMIT_BTREECOUNT
/*
** The first argument, pCur, is a cursor opened on some b-tree. Count the
** number of entries in the b-tree and write the result to *pnEntry.
**
** SQLITE_OK is returned if the operation is successfully executed.
** Otherwise, if an error is encountered (i.e. an IO error or database
** corruption) an SQLite error code is returned.
*/
int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
i64 nEntry = 0; /* Value to return in *pnEntry */
int rc; /* Return code */
rc = moveToRoot(pCur);
/* Unless an error occurs, the following loop runs one iteration for each
** page in the B-Tree structure (not including overflow pages).
*/
while( rc==SQLITE_OK ){
int iIdx; /* Index of child node in parent */
MemPage *pPage; /* Current page of the b-tree */
/* If this is a leaf page or the tree is not an int-key tree, then
** this page contains countable entries. Increment the entry counter
** accordingly.
*/
pPage = pCur->apPage[pCur->iPage];
if( pPage->leaf || !pPage->intKey ){
nEntry += pPage->nCell;
}
/* pPage is a leaf node. This loop navigates the cursor so that it
** points to the first interior cell that it points to the parent of
** the next page in the tree that has not yet been visited. The
** pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell
** of the page, or to the number of cells in the page if the next page
** to visit is the right-child of its parent.
**
** If all pages in the tree have been visited, return SQLITE_OK to the
** caller.
*/
if( pPage->leaf ){
do {
if( pCur->iPage==0 ){
/* All pages of the b-tree have been visited. Return successfully. */
*pnEntry = nEntry;
return SQLITE_OK;
}
sqlite3BtreeMoveToParent(pCur);
}while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell );
pCur->aiIdx[pCur->iPage]++;
pPage = pCur->apPage[pCur->iPage];
}
/* Descend to the child node of the cell that the cursor currently
** points at. This is the right-child if (iIdx==pPage->nCell).
*/
iIdx = pCur->aiIdx[pCur->iPage];
if( iIdx==pPage->nCell ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
}else{
rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx)));
}
}
/* An error has occurred. Return an error code. */
return rc;
}
#endif
/* /*
** Return the pager associated with a BTree. This routine is used for ** Return the pager associated with a BTree. This routine is used for
@ -6986,7 +7125,9 @@ static int checkTreePage(
sz = info.nData; sz = info.nData;
if( !pPage->intKey ) sz += (int)info.nKey; if( !pPage->intKey ) sz += (int)info.nKey;
assert( sz==info.nPayload ); assert( sz==info.nPayload );
if( sz>info.nLocal ){ if( (sz>info.nLocal)
&& (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
){
int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4); int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]); Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
@ -7248,14 +7389,6 @@ int sqlite3BtreeIsInTrans(Btree *p){
return (p && (p->inTrans==TRANS_WRITE)); return (p && (p->inTrans==TRANS_WRITE));
} }
/*
** Return non-zero if a statement transaction is active.
*/
int sqlite3BtreeIsInStmt(Btree *p){
assert( sqlite3BtreeHoldsMutex(p) );
return ALWAYS(p->pBt) && p->pBt->inStmt;
}
/* /*
** Return non-zero if a read (or write) transaction is active. ** Return non-zero if a read (or write) transaction is active.
*/ */
@ -7303,14 +7436,16 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
} }
/* /*
** Return true if another user of the same shared btree as the argument ** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
** handle holds an exclusive lock on the sqlite_master table. ** btree as the argument handle holds an exclusive lock on the
** sqlite_master table. Otherwise SQLITE_OK.
*/ */
int sqlite3BtreeSchemaLocked(Btree *p){ int sqlite3BtreeSchemaLocked(Btree *p){
int rc; int rc;
assert( sqlite3_mutex_held(p->db->mutex) ); assert( sqlite3_mutex_held(p->db->mutex) );
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
rc = (queryTableLock(p, MASTER_ROOT, READ_LOCK)!=SQLITE_OK); rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE );
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return rc; return rc;
} }
@ -7329,9 +7464,9 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
assert( READ_LOCK+1==WRITE_LOCK ); assert( READ_LOCK+1==WRITE_LOCK );
assert( isWriteLock==0 || isWriteLock==1 ); assert( isWriteLock==0 || isWriteLock==1 );
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
rc = queryTableLock(p, iTab, lockType); rc = querySharedCacheTableLock(p, iTab, lockType);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = lockTable(p, iTab, lockType); rc = setSharedCacheTableLock(p, iTab, lockType);
} }
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
} }
@ -7348,6 +7483,8 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
** to change the length of the data stored. ** to change the length of the data stored.
*/ */
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
int rc;
assert( cursorHoldsMutex(pCsr) ); assert( cursorHoldsMutex(pCsr) );
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
assert(pCsr->isIncrblobHandle); assert(pCsr->isIncrblobHandle);
@ -7368,8 +7505,11 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
} }
assert( !pCsr->pBt->readOnly assert( !pCsr->pBt->readOnly
&& pCsr->pBt->inTransaction==TRANS_WRITE ); && pCsr->pBt->inTransaction==TRANS_WRITE );
if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0) ){ rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0);
return SQLITE_LOCKED; /* The table pCur points to has a read lock */ if( rc!=SQLITE_OK ){
/* The table pCur points to has a read lock */
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
return rc;
} }
if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){ if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){
return SQLITE_ERROR; return SQLITE_ERROR;

13
btree.h
View file

@ -13,7 +13,7 @@
** subsystem. See comments in the source code for a detailed description ** subsystem. See comments in the source code for a detailed description
** of what each interface routine does. ** of what each interface routine does.
** **
** @(#) $Id: btree.h,v 1.108 2009/02/03 16:51:25 danielk1977 Exp $ ** @(#) $Id: btree.h,v 1.111 2009/03/18 10:33:01 danielk1977 Exp $
*/ */
#ifndef _BTREE_H_ #ifndef _BTREE_H_
#define _BTREE_H_ #define _BTREE_H_
@ -91,12 +91,9 @@ int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
int sqlite3BtreeCommitPhaseTwo(Btree*); int sqlite3BtreeCommitPhaseTwo(Btree*);
int sqlite3BtreeCommit(Btree*); int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*); int sqlite3BtreeRollback(Btree*);
int sqlite3BtreeBeginStmt(Btree*); int sqlite3BtreeBeginStmt(Btree*,int);
int sqlite3BtreeCommitStmt(Btree*);
int sqlite3BtreeRollbackStmt(Btree*);
int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeIsInReadTrans(Btree*); int sqlite3BtreeIsInReadTrans(Btree*);
int sqlite3BtreeIsInBackup(Btree*); int sqlite3BtreeIsInBackup(Btree*);
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
@ -165,6 +162,8 @@ const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64);
sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*);
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
struct Pager *sqlite3BtreePager(Btree*); struct Pager *sqlite3BtreePager(Btree*);
@ -173,6 +172,10 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeCacheOverflow(BtCursor *);
void sqlite3BtreeClearCursor(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *);
#ifndef SQLITE_OMIT_BTREECOUNT
int sqlite3BtreeCount(BtCursor *, i64 *);
#endif
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
int sqlite3BtreeCursorInfo(BtCursor*, int*, int); int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
void sqlite3BtreeCursorList(Btree*); void sqlite3BtreeCursorList(Btree*);

View file

@ -9,7 +9,7 @@
** May you share freely, never taking more than you give. ** May you share freely, never taking more than you give.
** **
************************************************************************* *************************************************************************
** $Id: btreeInt.h,v 1.42 2009/02/03 16:51:25 danielk1977 Exp $ ** $Id: btreeInt.h,v 1.46 2009/03/20 14:18:52 danielk1977 Exp $
** **
** This file implements a external (disk-based) database using BTrees. ** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to ** For a detailed discussion of BTrees, refer to
@ -205,11 +205,6 @@
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
/* Round up a number to the next larger multiple of 8. This is used
** to force 8-byte alignment on 64-bit architectures.
*/
#define ROUND8(x) ((x+7)&~7)
/* The following value is the maximum cell size assuming a maximum page /* The following value is the maximum cell size assuming a maximum page
** size give above. ** size give above.
@ -356,13 +351,30 @@ struct Btree {
** may not be modified once it is initially set as long as nRef>0. ** may not be modified once it is initially set as long as nRef>0.
** The pSchema field may be set once under BtShared.mutex and ** The pSchema field may be set once under BtShared.mutex and
** thereafter is unchanged as long as nRef>0. ** thereafter is unchanged as long as nRef>0.
**
** isPending:
**
** If a BtShared client fails to obtain a write-lock on a database
** table (because there exists one or more read-locks on the table),
** the shared-cache enters 'pending-lock' state and isPending is
** set to true.
**
** The shared-cache leaves the 'pending lock' state when either of
** the following occur:
**
** 1) The current writer (BtShared.pWriter) concludes its transaction, OR
** 2) The number of locks held by other connections drops to zero.
**
** while in the 'pending-lock' state, no connection may start a new
** transaction.
**
** This feature is included to help prevent writer-starvation.
*/ */
struct BtShared { struct BtShared {
Pager *pPager; /* The page cache */ Pager *pPager; /* The page cache */
sqlite3 *db; /* Database connection currently using this Btree */ sqlite3 *db; /* Database connection currently using this Btree */
BtCursor *pCursor; /* A list of all open cursors */ BtCursor *pCursor; /* A list of all open cursors */
MemPage *pPage1; /* First page of the database */ MemPage *pPage1; /* First page of the database */
u8 inStmt; /* True if we are in a statement subtransaction */
u8 readOnly; /* True if the underlying file is readonly */ u8 readOnly; /* True if the underlying file is readonly */
u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 pageSizeFixed; /* True if the page size can no longer be changed */
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
@ -385,7 +397,9 @@ struct BtShared {
int nRef; /* Number of references to this structure */ int nRef; /* Number of references to this structure */
BtShared *pNext; /* Next on a list of sharable BtShared structs */ BtShared *pNext; /* Next on a list of sharable BtShared structs */
BtLock *pLock; /* List of locks held on this shared-btree struct */ BtLock *pLock; /* List of locks held on this shared-btree struct */
Btree *pExclusive; /* Btree with an EXCLUSIVE lock on the whole db */ Btree *pWriter; /* Btree with currently open write transaction */
u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
u8 isPending; /* If waiting for read-locks to clear */
#endif #endif
u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
}; };
@ -438,6 +452,7 @@ struct BtCursor {
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
Pgno pgnoRoot; /* The root page of this tree */ Pgno pgnoRoot; /* The root page of this tree */
sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
CellInfo info; /* A parse of the cell we are pointing at */ CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */ u8 wrFlag; /* True if writable */
u8 atLast; /* Cursor pointing to the last entry */ u8 atLast; /* Cursor pointing to the last entry */

190
build.c
View file

@ -22,7 +22,7 @@
** COMMIT ** COMMIT
** ROLLBACK ** ROLLBACK
** **
** $Id: build.c,v 1.518 2009/02/13 03:43:32 drh Exp $ ** $Id: build.c,v 1.527 2009/03/31 03:41:57 shane Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -178,19 +178,6 @@ void sqlite3FinishCoding(Parse *pParse){
codeTableLocks(pParse); codeTableLocks(pParse);
sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto); sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto);
} }
#ifndef SQLITE_OMIT_TRACE
if( !db->init.busy ){
/* Change the P4 argument of the first opcode (which will always be
** an OP_Trace) to be the complete text of the current SQL statement.
*/
VdbeOp *pOp = sqlite3VdbeGetOp(v, 0);
if( pOp && pOp->opcode==OP_Trace ){
sqlite3VdbeChangeP4(v, 0, pParse->zSql,
(int)(pParse->zTail - pParse->zSql));
}
}
#endif /* SQLITE_OMIT_TRACE */
} }
@ -202,8 +189,8 @@ void sqlite3FinishCoding(Parse *pParse){
sqlite3VdbeTrace(v, trace); sqlite3VdbeTrace(v, trace);
#endif #endif
assert( pParse->disableColCache==0 ); /* Disables and re-enables match */ assert( pParse->disableColCache==0 ); /* Disables and re-enables match */
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3, sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
pParse->nTab+3, pParse->explain); pParse->nTab, pParse->explain);
pParse->rc = SQLITE_DONE; pParse->rc = SQLITE_DONE;
pParse->colNamesSet = 0; pParse->colNamesSet = 0;
}else if( pParse->rc==SQLITE_OK ){ }else if( pParse->rc==SQLITE_OK ){
@ -352,7 +339,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
** Reclaim the memory used by an index ** Reclaim the memory used by an index
*/ */
static void freeIndex(Index *p){ static void freeIndex(Index *p){
sqlite3 *db = p->pTable->db; sqlite3 *db = p->pTable->dbMem;
sqlite3DbFree(db, p->zColAff); sqlite3DbFree(db, p->zColAff);
sqlite3DbFree(db, p); sqlite3DbFree(db, p);
} }
@ -480,7 +467,7 @@ void sqlite3CommitInternalChanges(sqlite3 *db){
static void sqliteResetColumnNames(Table *pTable){ static void sqliteResetColumnNames(Table *pTable){
int i; int i;
Column *pCol; Column *pCol;
sqlite3 *db = pTable->db; sqlite3 *db = pTable->dbMem;
assert( pTable!=0 ); assert( pTable!=0 );
if( (pCol = pTable->aCol)!=0 ){ if( (pCol = pTable->aCol)!=0 ){
for(i=0; i<pTable->nCol; i++, pCol++){ for(i=0; i<pTable->nCol; i++, pCol++){
@ -511,7 +498,7 @@ void sqlite3DeleteTable(Table *pTable){
sqlite3 *db; sqlite3 *db;
if( pTable==0 ) return; if( pTable==0 ) return;
db = pTable->db; db = pTable->dbMem;
/* Do not delete the table until the reference count reaches zero. */ /* Do not delete the table until the reference count reaches zero. */
pTable->nRef--; pTable->nRef--;
@ -616,8 +603,11 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
void sqlite3OpenMasterTable(Parse *p, int iDb){ void sqlite3OpenMasterTable(Parse *p, int iDb){
Vdbe *v = sqlite3GetVdbe(p); Vdbe *v = sqlite3GetVdbe(p);
sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb)); sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 5);/* sqlite_master has 5 columns */
sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb); sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */
if( p->nTab==0 ){
p->nTab = 1;
}
} }
/* /*
@ -846,7 +836,7 @@ void sqlite3StartTable(
pTable->iPKey = -1; pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema; pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1; pTable->nRef = 1;
pTable->db = db; pTable->dbMem = db->lookaside.bEnabled ? db : 0;
if( pParse->pNewTable ) sqlite3DeleteTable(pParse->pNewTable); if( pParse->pNewTable ) sqlite3DeleteTable(pParse->pNewTable);
pParse->pNewTable = pTable; pParse->pNewTable = pTable;
@ -901,9 +891,10 @@ void sqlite3StartTable(
** The record created does not contain anything yet. It will be replaced ** The record created does not contain anything yet. It will be replaced
** by the real entry in code generated at sqlite3EndTable(). ** by the real entry in code generated at sqlite3EndTable().
** **
** The rowid for the new entry is left on the top of the stack. ** The rowid for the new entry is left in register pParse->regRowid.
** The rowid value is needed by the code that sqlite3EndTable will ** The root page number of the new table is left in reg pParse->regRoot.
** generate. ** The rowid and root page number values are needed by the code that
** sqlite3EndTable will generate.
*/ */
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
if( isView || isVirtual ){ if( isView || isVirtual ){
@ -1116,12 +1107,14 @@ void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
pCol->zName); pCol->zName);
}else{ }else{
Expr *pCopy; /* A copy of pExpr is used instead of the original, as pExpr contains
** tokens that point to volatile memory. The 'span' of the expression
** is required by pragma table_info.
*/
sqlite3ExprDelete(db, pCol->pDflt); sqlite3ExprDelete(db, pCol->pDflt);
pCol->pDflt = pCopy = sqlite3ExprDup(db, pExpr); pCol->pDflt = sqlite3ExprDup(
if( pCopy ){ db, pExpr, EXPRDUP_REDUCE|EXPRDUP_DISTINCTSPAN
sqlite3TokenCopy(db, &pCopy->span, &pExpr->span); );
}
} }
} }
sqlite3ExprDelete(db, pExpr); sqlite3ExprDelete(db, pExpr);
@ -1217,7 +1210,7 @@ void sqlite3AddCheckConstraint(
** to malloced space and not the (ephemeral) text of the CREATE TABLE ** to malloced space and not the (ephemeral) text of the CREATE TABLE
** statement */ ** statement */
pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck, pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck,
sqlite3ExprDup(db, pCheckExpr)); sqlite3ExprDup(db, pCheckExpr, 0));
} }
#endif #endif
sqlite3ExprDelete(db, pCheckExpr); sqlite3ExprDelete(db, pCheckExpr);
@ -1340,18 +1333,100 @@ static int identLength(const char *z){
} }
/* /*
** Write an identifier onto the end of the given string. Add ** This function is a wrapper around sqlite3GetToken() used by
** quote characters as needed. ** isValidDimension(). This function differs from sqlite3GetToken() in
** that:
**
** * Whitespace is ignored, and
** * The output variable *peToken is set to 0 if the end of the
** nul-terminated input string is reached.
*/ */
static void identPut(char *z, int *pIdx, char *zSignedIdent){ static int getTokenNoSpace(unsigned char *z, int *peToken){
int n = 0;
while( sqlite3Isspace(z[n]) ) n++;
if( !z[n] ){
*peToken = 0;
return 0;
}
return n + sqlite3GetToken(&z[n], peToken);
}
/*
** Parameter z points to a nul-terminated string. Return true if, when
** whitespace is ignored, the contents of this string matches one of
** the following patterns:
**
** ""
** "(number)"
** "(number,number)"
*/
static int isValidDimension(unsigned char *z){
int eToken;
int n = 0;
n += getTokenNoSpace(&z[n], &eToken);
if( eToken ){
if( eToken!=TK_LP ) return 0;
n += getTokenNoSpace(&z[n], &eToken);
if( eToken==TK_PLUS || eToken==TK_MINUS ){
n += getTokenNoSpace(&z[n], &eToken);
}
if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0;
n += getTokenNoSpace(&z[n], &eToken);
if( eToken==TK_COMMA ){
n += getTokenNoSpace(&z[n], &eToken);
if( eToken==TK_PLUS || eToken==TK_MINUS ){
n += getTokenNoSpace(&z[n], &eToken);
}
if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0;
n += getTokenNoSpace(&z[n], &eToken);
}
if( eToken!=TK_RP ) return 0;
getTokenNoSpace(&z[n], &eToken);
}
if( eToken ) return 0;
return 1;
}
/*
** The first parameter is a pointer to an output buffer. The second
** parameter is a pointer to an integer that contains the offset at
** which to write into the output buffer. This function copies the
** nul-terminated string pointed to by the third parameter, zSignedIdent,
** to the specified offset in the buffer and updates *pIdx to refer
** to the first byte after the last byte written before returning.
**
** If the string zSignedIdent consists entirely of alpha-numeric
** characters, does not begin with a digit and is not an SQL keyword,
** then it is copied to the output buffer exactly as it is. Otherwise,
** it is quoted using double-quotes.
*/
static void identPut(char *z, int *pIdx, char *zSignedIdent, int isTypename){
unsigned char *zIdent = (unsigned char*)zSignedIdent; unsigned char *zIdent = (unsigned char*)zSignedIdent;
int i, j, needQuote; int i, j, needQuote;
i = *pIdx; i = *pIdx;
for(j=0; zIdent[j]; j++){ for(j=0; zIdent[j]; j++){
if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break; if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
} }
needQuote = zIdent[j]!=0 || sqlite3Isdigit(zIdent[0]) needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID;
|| sqlite3KeywordCode(zIdent, j)!=TK_ID; if( !needQuote ){
if( isTypename ){
/* If this is a type-name, allow a little more flexibility. In SQLite,
** a type-name is specified as:
**
** ids [ids] [(number [, number])]
**
** where "ids" is either a quoted string or a simple identifier (in the
** above notation, [] means optional). It is a bit tricky to check
** for all cases, but it is good to avoid unnecessarily quoting common
** typenames like VARCHAR(10).
*/
needQuote = !isValidDimension(&zIdent[j]);
}else{
needQuote = zIdent[j];
}
}
if( needQuote ) z[i++] = '"'; if( needQuote ) z[i++] = '"';
for(j=0; zIdent[j]; j++){ for(j=0; zIdent[j]; j++){
z[i++] = zIdent[j]; z[i++] = zIdent[j];
@ -1377,7 +1452,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
n += identLength(pCol->zName); n += identLength(pCol->zName);
z = pCol->zType; z = pCol->zType;
if( z ){ if( z ){
n += (sqlite3Strlen30(z) + 1); n += identLength(z);
} }
} }
n += identLength(p->zName); n += identLength(p->zName);
@ -1398,18 +1473,17 @@ static char *createTableStmt(sqlite3 *db, Table *p){
} }
sqlite3_snprintf(n, zStmt, "CREATE TABLE "); sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
k = sqlite3Strlen30(zStmt); k = sqlite3Strlen30(zStmt);
identPut(zStmt, &k, p->zName); identPut(zStmt, &k, p->zName, 0);
zStmt[k++] = '('; zStmt[k++] = '(';
for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){ for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
sqlite3_snprintf(n-k, &zStmt[k], zSep); sqlite3_snprintf(n-k, &zStmt[k], zSep);
k += sqlite3Strlen30(&zStmt[k]); k += sqlite3Strlen30(&zStmt[k]);
zSep = zSep2; zSep = zSep2;
identPut(zStmt, &k, pCol->zName); identPut(zStmt, &k, pCol->zName, 0);
if( (z = pCol->zType)!=0 ){ if( (z = pCol->zType)!=0 ){
zStmt[k++] = ' '; zStmt[k++] = ' ';
assert( (int)(sqlite3Strlen30(z)+k+1)<=n ); assert( (int)(sqlite3Strlen30(z)+k+1)<=n );
sqlite3_snprintf(n-k, &zStmt[k], "%s", z); identPut(zStmt, &k, z, 1);
k += sqlite3Strlen30(z);
} }
} }
sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd); sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd);
@ -1489,8 +1563,7 @@ void sqlite3EndTable(
} }
/* If not initializing, then create a record for the new table /* If not initializing, then create a record for the new table
** in the SQLITE_MASTER table of the database. The record number ** in the SQLITE_MASTER table of the database.
** for the new table entry should already be on the stack.
** **
** If this is a TEMPORARY table, write the entry into the auxiliary ** If this is a TEMPORARY table, write the entry into the auxiliary
** file instead of into the main database file. ** file instead of into the main database file.
@ -1507,9 +1580,8 @@ void sqlite3EndTable(
sqlite3VdbeAddOp1(v, OP_Close, 0); sqlite3VdbeAddOp1(v, OP_Close, 0);
/* Create the rootpage for the new table and push it onto the stack. /*
** A view has no rootpage, so just push a zero onto the stack for ** Initialize zType for the new view or table.
** views. Initialize zType at the same time.
*/ */
if( p->pSelect==0 ){ if( p->pSelect==0 ){
/* A regular table */ /* A regular table */
@ -1525,7 +1597,7 @@ void sqlite3EndTable(
/* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
** statement to populate the new table. The root-page number for the ** statement to populate the new table. The root-page number for the
** new table is on the top of the vdbe stack. ** new table is in register pParse->regRoot.
** **
** Once the SELECT has been coded by sqlite3Select(), it is in a ** Once the SELECT has been coded by sqlite3Select(), it is in a
** suitable state to query for the column names and types to be used ** suitable state to query for the column names and types to be used
@ -1540,7 +1612,7 @@ void sqlite3EndTable(
SelectDest dest; SelectDest dest;
Table *pSelTab; Table *pSelTab;
assert(pParse->nTab==0); assert(pParse->nTab==1);
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
sqlite3VdbeChangeP5(v, 1); sqlite3VdbeChangeP5(v, 1);
pParse->nTab = 2; pParse->nTab = 2;
@ -1571,9 +1643,7 @@ void sqlite3EndTable(
/* A slot for the record has already been allocated in the /* A slot for the record has already been allocated in the
** SQLITE_MASTER table. We just need to update that slot with all ** SQLITE_MASTER table. We just need to update that slot with all
** the information we've collected. The rowid for the preallocated ** the information we've collected.
** slot is the 2nd item on the stack. The top of the stack is the
** root page for the new table (or a 0 if this is a view).
*/ */
sqlite3NestedParse(pParse, sqlite3NestedParse(pParse,
"UPDATE %Q.%s " "UPDATE %Q.%s "
@ -1701,7 +1771,7 @@ void sqlite3CreateView(
** allocated rather than point to the input string - which means that ** allocated rather than point to the input string - which means that
** they will persist after the current sqlite3_exec() call returns. ** they will persist after the current sqlite3_exec() call returns.
*/ */
p->pSelect = sqlite3SelectDup(db, pSelect); p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
sqlite3SelectDelete(db, pSelect); sqlite3SelectDelete(db, pSelect);
if( db->mallocFailed ){ if( db->mallocFailed ){
return; return;
@ -1783,11 +1853,13 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
** statement that defines the view. ** statement that defines the view.
*/ */
assert( pTable->pSelect ); assert( pTable->pSelect );
pSel = sqlite3SelectDup(db, pTable->pSelect); pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
if( pSel ){ if( pSel ){
u8 enableLookaside = db->lookaside.bEnabled;
n = pParse->nTab; n = pParse->nTab;
sqlite3SrcListAssignCursors(pParse, pSel->pSrc); sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
pTable->nCol = -1; pTable->nCol = -1;
db->lookaside.bEnabled = 0;
#ifndef SQLITE_OMIT_AUTHORIZATION #ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth; xAuth = db->xAuth;
db->xAuth = 0; db->xAuth = 0;
@ -1796,6 +1868,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#else #else
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
#endif #endif
db->lookaside.bEnabled = enableLookaside;
pParse->nTab = n; pParse->nTab = n;
if( pSelTab ){ if( pSelTab ){
assert( pTable->aCol==0 ); assert( pTable->aCol==0 );
@ -1892,8 +1965,8 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
** location iTable. The following code modifies the sqlite_master table to ** location iTable. The following code modifies the sqlite_master table to
** reflect this. ** reflect this.
** **
** The "#%d" in the SQL is a special constant that means whatever value ** The "#NNN" in the SQL is a special constant that means whatever value
** is on the top of the stack. See sqlite3RegisterExpr(). ** is in register NNN. See sqlite3RegisterExpr().
*/ */
sqlite3NestedParse(pParse, sqlite3NestedParse(pParse,
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d", "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
@ -2068,7 +2141,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
** is generated to remove entries from sqlite_master and/or ** is generated to remove entries from sqlite_master and/or
** sqlite_temp_master if required. ** sqlite_temp_master if required.
*/ */
pTrigger = pTab->pTrigger; pTrigger = sqlite3TriggerList(pParse, pTab);
while( pTrigger ){ while( pTrigger ){
assert( pTrigger->pSchema==pTab->pSchema || assert( pTrigger->pSchema==pTab->pSchema ||
pTrigger->pSchema==db->aDb[1].pSchema ); pTrigger->pSchema==db->aDb[1].pSchema );
@ -2277,8 +2350,8 @@ void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){
*/ */
static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
Table *pTab = pIndex->pTable; /* The table that is indexed */ Table *pTab = pIndex->pTable; /* The table that is indexed */
int iTab = pParse->nTab; /* Btree cursor used for pTab */ int iTab = pParse->nTab++; /* Btree cursor used for pTab */
int iIdx = pParse->nTab+1; /* Btree cursor used for pIndex */ int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */
int addr1; /* Address of top of loop */ int addr1; /* Address of top of loop */
int tnum; /* Root page of index */ int tnum; /* Root page of index */
Vdbe *v; /* Generate code into this virtual machine */ Vdbe *v; /* Generate code into this virtual machine */
@ -2773,7 +2846,8 @@ void sqlite3CreateIndex(
/* Clean up before exiting */ /* Clean up before exiting */
exit_create_index: exit_create_index:
if( pIndex ){ if( pIndex ){
freeIndex(pIndex); sqlite3_free(pIndex->zColAff);
sqlite3DbFree(db, pIndex);
} }
sqlite3ExprListDelete(db, pList); sqlite3ExprListDelete(db, pList);
sqlite3SrcListDelete(db, pTblName); sqlite3SrcListDelete(db, pTblName);

View file

@ -13,7 +13,7 @@
** This file contains functions used to access the internal hash tables ** This file contains functions used to access the internal hash tables
** of user defined functions and collation sequences. ** of user defined functions and collation sequences.
** **
** $Id: callback.c,v 1.35 2009/01/31 22:28:49 drh Exp $ ** $Id: callback.c,v 1.37 2009/03/24 15:08:10 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -175,7 +175,7 @@ static CollSeq *findCollSeqEntry(
pColl[0].zName[nName] = 0; pColl[0].zName[nName] = 0;
pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl); pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl);
/* If a malloc() failure occured in sqlite3HashInsert(), it will /* If a malloc() failure occurred in sqlite3HashInsert(), it will
** return the pColl pointer to be deleted (because it wasn't added ** return the pColl pointer to be deleted (because it wasn't added
** to the hash table). ** to the hash table).
*/ */
@ -423,6 +423,7 @@ void sqlite3SchemaFree(void *p){
sqlite3HashInit(&pSchema->tblHash, 0); sqlite3HashInit(&pSchema->tblHash, 0);
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
Table *pTab = sqliteHashData(pElem); Table *pTab = sqliteHashData(pElem);
assert( pTab->dbMem==0 );
sqlite3DeleteTable(pTab); sqlite3DeleteTable(pTab);
} }
sqlite3HashClear(&temp1); sqlite3HashClear(&temp1);

View file

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements. ** in order to generate code for DELETE FROM statements.
** **
** $Id: delete.c,v 1.191 2008/12/23 23:56:22 drh Exp $ ** $Id: delete.c,v 1.198 2009/03/05 03:48:07 shane Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -77,8 +77,8 @@ void sqlite3OpenTable(
v = sqlite3GetVdbe(p); v = sqlite3GetVdbe(p);
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName);
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb); sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32);
VdbeComment((v, "%s", pTab->zName)); VdbeComment((v, "%s", pTab->zName));
} }
@ -99,12 +99,12 @@ void sqlite3MaterializeView(
Select *pDup; Select *pDup;
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
pDup = sqlite3SelectDup(db, pView->pSelect); pDup = sqlite3SelectDup(db, pView->pSelect, 0);
if( pWhere ){ if( pWhere ){
SrcList *pFrom; SrcList *pFrom;
Token viewName; Token viewName;
pWhere = sqlite3ExprDup(db, pWhere); pWhere = sqlite3ExprDup(db, pWhere, 0);
viewName.z = (u8*)pView->zName; viewName.z = (u8*)pView->zName;
viewName.n = (unsigned int)sqlite3Strlen30((const char*)viewName.z); viewName.n = (unsigned int)sqlite3Strlen30((const char*)viewName.z);
pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0); pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0);
@ -174,14 +174,15 @@ Expr *sqlite3LimitWhere(
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
** and the SELECT subtree. */ ** and the SELECT subtree. */
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc); pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
if( pSelectSrc == 0 ) { if( pSelectSrc == 0 ) {
sqlite3ExprListDelete(pParse->db, pEList); sqlite3ExprListDelete(pParse->db, pEList);
goto limit_where_cleanup_2; goto limit_where_cleanup_2;
} }
/* generate the SELECT expression tree. */ /* generate the SELECT expression tree. */
pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,pOrderBy,0,pLimit,pOffset); pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,
pOrderBy,0,pLimit,pOffset);
if( pSelect == 0 ) return 0; if( pSelect == 0 ) return 0;
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
@ -190,7 +191,8 @@ Expr *sqlite3LimitWhere(
pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0); pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
if( pInClause == 0 ) goto limit_where_cleanup_1; if( pInClause == 0 ) goto limit_where_cleanup_1;
pInClause->pSelect = pSelect; pInClause->x.pSelect = pSelect;
pInClause->flags |= EP_xIsSelect;
sqlite3ExprSetHeight(pParse, pInClause); sqlite3ExprSetHeight(pParse, pInClause);
return pInClause; return pInClause;
@ -238,7 +240,7 @@ void sqlite3DeleteFrom(
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to delete from a view */ int isView; /* True if attempting to delete from a view */
int triggers_exist = 0; /* True if any triggers exist */ Trigger *pTrigger; /* List of table triggers, if required */
#endif #endif
int iBeginAfterTrigger = 0; /* Address of after trigger program */ int iBeginAfterTrigger = 0; /* Address of after trigger program */
int iEndAfterTrigger = 0; /* Exit of after trigger program */ int iEndAfterTrigger = 0; /* Exit of after trigger program */
@ -265,10 +267,10 @@ void sqlite3DeleteFrom(
** deleted from is a view ** deleted from is a view
*/ */
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
triggers_exist = sqlite3TriggersExist(pTab, TK_DELETE, 0); pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
isView = pTab->pSelect!=0; isView = pTab->pSelect!=0;
#else #else
# define triggers_exist 0 # define pTrigger 0
# define isView 0 # define isView 0
#endif #endif
#ifdef SQLITE_OMIT_VIEW #ifdef SQLITE_OMIT_VIEW
@ -276,7 +278,7 @@ void sqlite3DeleteFrom(
# define isView 0 # define isView 0
#endif #endif
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
goto delete_from_cleanup; goto delete_from_cleanup;
} }
iDb = sqlite3SchemaToIndex(db, pTab->pSchema); iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@ -287,7 +289,7 @@ void sqlite3DeleteFrom(
if( rcauth==SQLITE_DENY ){ if( rcauth==SQLITE_DENY ){
goto delete_from_cleanup; goto delete_from_cleanup;
} }
assert(!isView || triggers_exist); assert(!isView || pTrigger);
/* If pTab is really a view, make sure it has been initialized. /* If pTab is really a view, make sure it has been initialized.
*/ */
@ -297,7 +299,7 @@ void sqlite3DeleteFrom(
/* Allocate a cursor used to store the old.* data for a trigger. /* Allocate a cursor used to store the old.* data for a trigger.
*/ */
if( triggers_exist ){ if( pTrigger ){
oldIdx = pParse->nTab++; oldIdx = pParse->nTab++;
} }
@ -322,21 +324,21 @@ void sqlite3DeleteFrom(
goto delete_from_cleanup; goto delete_from_cleanup;
} }
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, triggers_exist, iDb); sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb);
if( triggers_exist ){ if( pTrigger ){
int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default); int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
int iGoto = sqlite3VdbeAddOp0(v, OP_Goto); int iGoto = sqlite3VdbeAddOp0(v, OP_Goto);
addr = sqlite3VdbeMakeLabel(v); addr = sqlite3VdbeMakeLabel(v);
iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab, (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
-1, oldIdx, orconf, addr, &old_col_mask, 0); TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto); iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
oldIdx, orconf, addr, &old_col_mask, 0); TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto); iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, iGoto); sqlite3VdbeJumpHere(v, iGoto);
@ -373,7 +375,7 @@ void sqlite3DeleteFrom(
** It is easier just to erase the whole table. Note, however, that ** It is easier just to erase the whole table. Note, however, that
** this means that the row change count will be incorrect. ** this means that the row change count will be incorrect.
*/ */
if( rcauth==SQLITE_OK && pWhere==0 && !triggers_exist && !IsVirtual(pTab) ){ if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) ){
assert( !isView ); assert( !isView );
sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt); sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt);
if( !pParse->nested ){ if( !pParse->nested ){
@ -405,9 +407,8 @@ void sqlite3DeleteFrom(
/* Open the pseudo-table used to store OLD if there are triggers. /* Open the pseudo-table used to store OLD if there are triggers.
*/ */
if( triggers_exist ){ if( pTrigger ){
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
sqlite3VdbeAddOp1(v, OP_OpenPseudo, oldIdx);
} }
/* Delete every item whose key was written to the list during the /* Delete every item whose key was written to the list during the
@ -426,12 +427,12 @@ void sqlite3DeleteFrom(
/* This is the beginning of the delete loop. If a trigger encounters /* This is the beginning of the delete loop. If a trigger encounters
** an IGNORE constraint, it jumps back to here. ** an IGNORE constraint, it jumps back to here.
*/ */
if( triggers_exist ){ if( pTrigger ){
sqlite3VdbeResolveLabel(v, addr); sqlite3VdbeResolveLabel(v, addr);
} }
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
if( triggers_exist ){ if( pTrigger ){
int iData = ++pParse->nMem; /* For storing row data of OLD table */ int iData = ++pParse->nMem; /* For storing row data of OLD table */
/* If the record is no longer present in the table, jump to the /* If the record is no longer present in the table, jump to the
@ -469,7 +470,7 @@ void sqlite3DeleteFrom(
/* If there are row triggers, close all cursors then invoke /* If there are row triggers, close all cursors then invoke
** the AFTER triggers ** the AFTER triggers
*/ */
if( triggers_exist ){ if( pTrigger ){
/* Jump back and run the AFTER triggers */ /* Jump back and run the AFTER triggers */
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
sqlite3VdbeJumpHere(v, iEndAfterTrigger); sqlite3VdbeJumpHere(v, iEndAfterTrigger);

465
expr.c
View file

@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and ** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite. ** for generating VDBE code that evaluates expressions in SQLite.
** **
** $Id: expr.c,v 1.411 2009/02/04 03:59:25 shane Exp $ ** $Id: expr.c,v 1.424 2009/03/25 16:51:43 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -35,7 +35,8 @@
char sqlite3ExprAffinity(Expr *pExpr){ char sqlite3ExprAffinity(Expr *pExpr){
int op = pExpr->op; int op = pExpr->op;
if( op==TK_SELECT ){ if( op==TK_SELECT ){
return sqlite3ExprAffinity(pExpr->pSelect->pEList->a[0].pExpr); assert( pExpr->flags&EP_xIsSelect );
return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
} }
#ifndef SQLITE_OMIT_CAST #ifndef SQLITE_OMIT_CAST
if( op==TK_CAST ){ if( op==TK_CAST ){
@ -155,11 +156,9 @@ static char comparisonAffinity(Expr *pExpr){
aff = sqlite3ExprAffinity(pExpr->pLeft); aff = sqlite3ExprAffinity(pExpr->pLeft);
if( pExpr->pRight ){ if( pExpr->pRight ){
aff = sqlite3CompareAffinity(pExpr->pRight, aff); aff = sqlite3CompareAffinity(pExpr->pRight, aff);
} }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
else if( pExpr->pSelect ){ aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff);
aff = sqlite3CompareAffinity(pExpr->pSelect->pEList->a[0].pExpr, aff); }else if( !aff ){
}
else if( !aff ){
aff = SQLITE_AFF_NONE; aff = SQLITE_AFF_NONE;
} }
return aff; return aff;
@ -345,8 +344,11 @@ static void exprSetHeight(Expr *p){
int nHeight = 0; int nHeight = 0;
heightOfExpr(p->pLeft, &nHeight); heightOfExpr(p->pLeft, &nHeight);
heightOfExpr(p->pRight, &nHeight); heightOfExpr(p->pRight, &nHeight);
heightOfExprList(p->pList, &nHeight); if( ExprHasProperty(p, EP_xIsSelect) ){
heightOfSelect(p->pSelect, &nHeight); heightOfSelect(p->x.pSelect, &nHeight);
}else{
heightOfExprList(p->x.pList, &nHeight);
}
p->nHeight = nHeight + 1; p->nHeight = nHeight + 1;
} }
@ -402,8 +404,24 @@ Expr *sqlite3Expr(
pNew->iAgg = -1; pNew->iAgg = -1;
pNew->span.z = (u8*)""; pNew->span.z = (u8*)"";
if( pToken ){ if( pToken ){
int c;
assert( pToken->dyn==0 ); assert( pToken->dyn==0 );
pNew->span = pNew->token = *pToken; pNew->span = *pToken;
/* The pToken->z value is read-only. But the new expression
** node created here might be passed to sqlite3DequoteExpr() which
** will attempt to modify pNew->token.z. Hence, if the token
** is quoted, make a copy now so that DequoteExpr() will change
** the copy rather than the original text.
*/
if( pToken->n>=2
&& ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){
sqlite3TokenCopy(db, &pNew->token, pToken);
}else{
pNew->token = *pToken;
pNew->flags |= EP_Dequoted;
VVA_ONLY( pNew->vvaFlags |= EVVA_ReadOnlyToken; )
}
}else if( pLeft ){ }else if( pLeft ){
if( pRight ){ if( pRight ){
if( pRight->span.dyn==0 && pLeft->span.dyn==0 ){ if( pRight->span.dyn==0 && pLeft->span.dyn==0 ){
@ -492,7 +510,10 @@ void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
assert( pLeft!=0 ); assert( pLeft!=0 );
if( pExpr ){ if( pExpr ){
pExpr->span.z = pLeft->z; pExpr->span.z = pLeft->z;
pExpr->span.n = pRight->n + (pRight->z - pLeft->z); /* The following assert() may fail when this is called
** via sqlite3PExpr()/sqlite3Expr() from addWhereTerm(). */
/* assert(pRight->z >= pLeft->z); */
pExpr->span.n = pRight->n + (unsigned)(pRight->z - pLeft->z);
} }
} }
@ -506,15 +527,15 @@ Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){
assert( pToken ); assert( pToken );
pNew = sqlite3DbMallocZero(db, sizeof(Expr) ); pNew = sqlite3DbMallocZero(db, sizeof(Expr) );
if( pNew==0 ){ if( pNew==0 ){
sqlite3ExprListDelete(db, pList); /* Avoid leaking memory when malloc fails */ sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */
return 0; return 0;
} }
pNew->op = TK_FUNCTION; pNew->op = TK_FUNCTION;
pNew->pList = pList; pNew->x.pList = pList;
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
assert( pToken->dyn==0 ); assert( pToken->dyn==0 );
pNew->token = *pToken; pNew->span = *pToken;
pNew->span = pNew->token; sqlite3TokenCopy(db, &pNew->token, pToken);
sqlite3ExprSetHeight(pParse, pNew); sqlite3ExprSetHeight(pParse, pNew);
return pNew; return pNew;
} }
@ -607,12 +628,22 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
** Substructure is deleted. ** Substructure is deleted.
*/ */
void sqlite3ExprClear(sqlite3 *db, Expr *p){ void sqlite3ExprClear(sqlite3 *db, Expr *p){
if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z);
if( p->token.dyn ) sqlite3DbFree(db, (char*)p->token.z); if( p->token.dyn ) sqlite3DbFree(db, (char*)p->token.z);
sqlite3ExprDelete(db, p->pLeft); if( !ExprHasAnyProperty(p, EP_TokenOnly|EP_SpanOnly) ){
sqlite3ExprDelete(db, p->pRight); if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z);
sqlite3ExprListDelete(db, p->pList); if( ExprHasProperty(p, EP_Reduced) ){
sqlite3SelectDelete(db, p->pSelect); if( p->pLeft ) sqlite3ExprClear(db, p->pLeft);
if( p->pRight ) sqlite3ExprClear(db, p->pRight);
}else{
sqlite3ExprDelete(db, p->pLeft);
sqlite3ExprDelete(db, p->pRight);
}
if( ExprHasProperty(p, EP_xIsSelect) ){
sqlite3SelectDelete(db, p->x.pSelect);
}else{
sqlite3ExprListDelete(db, p->x.pList);
}
}
} }
/* /*
@ -628,15 +659,195 @@ void sqlite3ExprDelete(sqlite3 *db, Expr *p){
** The Expr.token field might be a string literal that is quoted. ** The Expr.token field might be a string literal that is quoted.
** If so, remove the quotation marks. ** If so, remove the quotation marks.
*/ */
void sqlite3DequoteExpr(sqlite3 *db, Expr *p){ void sqlite3DequoteExpr(Expr *p){
if( ExprHasAnyProperty(p, EP_Dequoted) ){ if( !ExprHasAnyProperty(p, EP_Dequoted) ){
return; ExprSetProperty(p, EP_Dequoted);
assert( (p->vvaFlags & EVVA_ReadOnlyToken)==0 );
sqlite3Dequote((char*)p->token.z);
} }
ExprSetProperty(p, EP_Dequoted); }
if( p->token.dyn==0 ){
sqlite3TokenCopy(db, &p->token, &p->token); /*
** Return the number of bytes allocated for the expression structure
** passed as the first argument. This is always one of EXPR_FULLSIZE,
** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE.
*/
static int exprStructSize(Expr *p){
if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE;
if( ExprHasProperty(p, EP_SpanOnly) ) return EXPR_SPANONLYSIZE;
if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE;
return EXPR_FULLSIZE;
}
/*
** sqlite3ExprDup() has been called to create a copy of expression p with
** the EXPRDUP_XXX flags passed as the second argument. This function
** returns the space required for the copy of the Expr structure only.
** This is always one of EXPR_FULLSIZE, EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE.
*/
static int dupedExprStructSize(Expr *p, int flags){
int nSize;
if( 0==(flags&EXPRDUP_REDUCE) ){
nSize = EXPR_FULLSIZE;
}else if( p->pLeft || p->pRight || p->pColl || p->x.pList ){
nSize = EXPR_REDUCEDSIZE;
}else if( flags&(EXPRDUP_SPAN|EXPRDUP_DISTINCTSPAN) ){
nSize = EXPR_SPANONLYSIZE;
}else{
nSize = EXPR_TOKENONLYSIZE;
} }
sqlite3Dequote((char*)p->token.z); return nSize;
}
/*
** sqlite3ExprDup() has been called to create a copy of expression p with
** the EXPRDUP_XXX passed as the second argument. This function returns
** the space in bytes required to store the copy of the Expr structure
** and the copies of the Expr.token.z and Expr.span.z (if applicable)
** string buffers.
*/
static int dupedExprNodeSize(Expr *p, int flags){
int nByte = dupedExprStructSize(p, flags) + (p->token.z ? p->token.n + 1 : 0);
if( (flags&EXPRDUP_DISTINCTSPAN)
|| (flags&EXPRDUP_SPAN && (p->token.z!=p->span.z || p->token.n!=p->span.n))
){
nByte += p->span.n;
}
return ROUND8(nByte);
}
/*
** Return the number of bytes required to create a duplicate of the
** expression passed as the first argument. The second argument is a
** mask containing EXPRDUP_XXX flags.
**
** The value returned includes space to create a copy of the Expr struct
** itself and the buffer referred to by Expr.token, if any. If the
** EXPRDUP_SPAN flag is set, then space to create a copy of the buffer
** refered to by Expr.span is also included.
**
** If the EXPRDUP_REDUCE flag is set, then the return value includes
** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
** and Expr.pRight variables (but not for any structures pointed to or
** descended from the Expr.x.pList or Expr.x.pSelect variables).
*/
static int dupedExprSize(Expr *p, int flags){
int nByte = 0;
if( p ){
nByte = dupedExprNodeSize(p, flags);
if( flags&EXPRDUP_REDUCE ){
int f = flags&(~(EXPRDUP_SPAN|EXPRDUP_DISTINCTSPAN));
nByte += dupedExprSize(p->pLeft, f) + dupedExprSize(p->pRight, f);
}
}
return nByte;
}
/*
** This function is similar to sqlite3ExprDup(), except that if pzBuffer
** is not NULL then *pzBuffer is assumed to point to a buffer large enough
** to store the copy of expression p, the copies of p->token and p->span
** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
** if any. Before returning, *pzBuffer is set to the first byte passed the
** portion of the buffer copied into by this function.
*/
static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
Expr *pNew = 0; /* Value to return */
if( p ){
const int isRequireDistinctSpan = (flags&EXPRDUP_DISTINCTSPAN);
const int isRequireSpan = (flags&(EXPRDUP_SPAN|EXPRDUP_DISTINCTSPAN));
const int isReduced = (flags&EXPRDUP_REDUCE);
u8 *zAlloc;
assert( pzBuffer==0 || isReduced );
/* Figure out where to write the new Expr structure. */
if( pzBuffer ){
zAlloc = *pzBuffer;
}else{
zAlloc = sqlite3DbMallocRaw(db, dupedExprSize(p, flags));
}
pNew = (Expr *)zAlloc;
if( pNew ){
/* Set nNewSize to the size allocated for the structure pointed to
** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or
** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed
** by the copy of the p->token.z string (if any).
*/
const int nNewSize = dupedExprStructSize(p, flags);
const int nToken = (p->token.z ? p->token.n + 1 : 0);
if( isReduced ){
assert( ExprHasProperty(p, EP_Reduced)==0 );
memcpy(zAlloc, p, nNewSize);
}else{
int nSize = exprStructSize(p);
memcpy(zAlloc, p, nSize);
memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
}
/* Set the EP_Reduced and EP_TokenOnly flags appropriately. */
pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_SpanOnly);
switch( nNewSize ){
case EXPR_REDUCEDSIZE: pNew->flags |= EP_Reduced; break;
case EXPR_TOKENONLYSIZE: pNew->flags |= EP_TokenOnly; break;
case EXPR_SPANONLYSIZE: pNew->flags |= EP_SpanOnly; break;
}
/* Copy the p->token string, if any. */
if( nToken ){
unsigned char *zToken = &zAlloc[nNewSize];
memcpy(zToken, p->token.z, nToken-1);
zToken[nToken-1] = '\0';
pNew->token.dyn = 0;
pNew->token.z = zToken;
}
if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){
/* Fill in the pNew->span token, if required. */
if( isRequireSpan ){
if( isRequireDistinctSpan
|| p->token.z!=p->span.z || p->token.n!=p->span.n
){
pNew->span.z = &zAlloc[nNewSize+nToken];
memcpy((char *)pNew->span.z, p->span.z, p->span.n);
pNew->span.dyn = 0;
}else{
pNew->span.z = pNew->token.z;
pNew->span.n = pNew->token.n;
}
}else{
pNew->span.z = 0;
pNew->span.n = 0;
}
}
if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_SpanOnly)) ){
/* Fill in the pNew->x.pSelect or pNew->x.pList member. */
if( ExprHasProperty(p, EP_xIsSelect) ){
pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, isReduced);
}else{
pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, isReduced);
}
}
/* Fill in pNew->pLeft and pNew->pRight. */
if( ExprHasAnyProperty(pNew, EP_Reduced|EP_TokenOnly|EP_SpanOnly) ){
zAlloc += dupedExprNodeSize(p, flags);
if( ExprHasProperty(pNew, EP_Reduced) ){
pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc);
pNew->pRight = exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc);
}
if( pzBuffer ){
*pzBuffer = zAlloc;
}
}else if( !ExprHasAnyProperty(p, EP_TokenOnly|EP_SpanOnly) ){
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
}
}
}
return pNew;
} }
/* /*
@ -650,27 +861,21 @@ void sqlite3DequoteExpr(sqlite3 *db, Expr *p){
** by subsequent calls to sqlite*ListAppend() routines. ** by subsequent calls to sqlite*ListAppend() routines.
** **
** Any tables that the SrcList might point to are not duplicated. ** Any tables that the SrcList might point to are not duplicated.
**
** The flags parameter contains a combination of the EXPRDUP_XXX flags. If
** the EXPRDUP_SPAN flag is set in the argument parameter, then the
** Expr.span field of the input expression is copied. If EXPRDUP_SPAN is
** clear, then the Expr.span field of the returned expression structure
** is zeroed.
**
** If the EXPRDUP_REDUCE flag is set, then the structure returned is a
** truncated version of the usual Expr structure that will be stored as
** part of the in-memory representation of the database schema.
*/ */
Expr *sqlite3ExprDup(sqlite3 *db, Expr *p){ Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){
Expr *pNew; return exprDup(db, p, flags, 0);
if( p==0 ) return 0;
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
if( pNew==0 ) return 0;
memcpy(pNew, p, sizeof(*pNew));
if( p->token.z!=0 ){
pNew->token.z = (u8*)sqlite3DbStrNDup(db, (char*)p->token.z, p->token.n);
pNew->token.dyn = 1;
}else{
assert( pNew->token.z==0 );
}
pNew->span.z = 0;
pNew->pLeft = sqlite3ExprDup(db, p->pLeft);
pNew->pRight = sqlite3ExprDup(db, p->pRight);
pNew->pList = sqlite3ExprListDup(db, p->pList);
pNew->pSelect = sqlite3SelectDup(db, p->pSelect);
return pNew;
} }
void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){ void sqlite3TokenCopy(sqlite3 *db, Token *pTo, const Token *pFrom){
if( pTo->dyn ) sqlite3DbFree(db, (char*)pTo->z); if( pTo->dyn ) sqlite3DbFree(db, (char*)pTo->z);
if( pFrom->z ){ if( pFrom->z ){
pTo->n = pFrom->n; pTo->n = pFrom->n;
@ -680,7 +885,7 @@ void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){
pTo->z = 0; pTo->z = 0;
} }
} }
ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
ExprList *pNew; ExprList *pNew;
struct ExprList_item *pItem, *pOldItem; struct ExprList_item *pItem, *pOldItem;
int i; int i;
@ -696,17 +901,9 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
} }
pOldItem = p->a; pOldItem = p->a;
for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){ for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){
Expr *pNewExpr, *pOldExpr; Expr *pNewExpr;
pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr = pOldItem->pExpr); Expr *pOldExpr = pOldItem->pExpr;
if( pOldExpr->span.z!=0 && pNewExpr ){ pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr, flags);
/* Always make a copy of the span for top-level expressions in the
** expression list. The logic in SELECT processing that determines
** the names of columns in the result set needs this information */
sqlite3TokenCopy(db, &pNewExpr->span, &pOldExpr->span);
}
assert( pNewExpr==0 || pNewExpr->span.z!=0
|| pOldExpr->span.z==0
|| db->mallocFailed );
pItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pItem->sortOrder = pOldItem->sortOrder; pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0; pItem->done = 0;
@ -724,7 +921,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
*/ */
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
|| !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_SUBQUERY)
SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
SrcList *pNew; SrcList *pNew;
int i; int i;
int nByte; int nByte;
@ -750,8 +947,8 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){
if( pTab ){ if( pTab ){
pTab->nRef++; pTab->nRef++;
} }
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect); pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn); pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
pNewItem->colUsed = pOldItem->colUsed; pNewItem->colUsed = pOldItem->colUsed;
} }
@ -777,21 +974,24 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
} }
return pNew; return pNew;
} }
Select *sqlite3SelectDup(sqlite3 *db, Select *p){ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
Select *pNew; Select *pNew;
if( p==0 ) return 0; if( p==0 ) return 0;
pNew = sqlite3DbMallocRaw(db, sizeof(*p) ); pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
if( pNew==0 ) return 0; if( pNew==0 ) return 0;
pNew->pEList = sqlite3ExprListDup(db, p->pEList); /* Always make a copy of the span for top-level expressions in the
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc); ** expression list. The logic in SELECT processing that determines
pNew->pWhere = sqlite3ExprDup(db, p->pWhere); ** the names of columns in the result set needs this information */
pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy); pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags|EXPRDUP_SPAN);
pNew->pHaving = sqlite3ExprDup(db, p->pHaving); pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy); pNew->pWhere = sqlite3ExprDup(db, p->pWhere, flags);
pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy, flags);
pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
pNew->op = p->op; pNew->op = p->op;
pNew->pPrior = sqlite3SelectDup(db, p->pPrior); pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags);
pNew->pLimit = sqlite3ExprDup(db, p->pLimit); pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
pNew->pOffset = sqlite3ExprDup(db, p->pOffset); pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
pNew->iLimit = 0; pNew->iLimit = 0;
pNew->iOffset = 0; pNew->iOffset = 0;
pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
@ -802,7 +1002,7 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p){
return pNew; return pNew;
} }
#else #else
Select *sqlite3SelectDup(sqlite3 *db, Select *p){ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
assert( p==0 ); assert( p==0 );
return 0; return 0;
} }
@ -1143,7 +1343,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
** If this is the case, it may be possible to use an existing table ** If this is the case, it may be possible to use an existing table
** or index instead of generating an epheremal table. ** or index instead of generating an epheremal table.
*/ */
p = pX->pSelect; p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
if( isCandidateForInOpt(p) ){ if( isCandidateForInOpt(p) ){
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
Index *pIdx; Index *pIdx;
@ -1202,7 +1402,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIdx->nColumn);
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
pKey,P4_KEYINFO_HANDOFF); pKey,P4_KEYINFO_HANDOFF);
VdbeComment((v, "%s", pIdx->zName)); VdbeComment((v, "%s", pIdx->zName));
@ -1222,7 +1421,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
eType = IN_INDEX_EPH; eType = IN_INDEX_EPH;
if( prNotFound ){ if( prNotFound ){
*prNotFound = rMayHaveNull = ++pParse->nMem; *prNotFound = rMayHaveNull = ++pParse->nMem;
}else if( pX->pLeft->iColumn<0 && pX->pSelect==0 ){ }else if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){
eType = IN_INDEX_ROWID; eType = IN_INDEX_ROWID;
} }
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID); sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
@ -1311,7 +1510,7 @@ void sqlite3CodeSubselect(
memset(&keyInfo, 0, sizeof(keyInfo)); memset(&keyInfo, 0, sizeof(keyInfo));
keyInfo.nField = 1; keyInfo.nField = 1;
if( pExpr->pSelect ){ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* Case 1: expr IN (SELECT ...) /* Case 1: expr IN (SELECT ...)
** **
** Generate code to write the results of the select into the temporary ** Generate code to write the results of the select into the temporary
@ -1324,15 +1523,15 @@ void sqlite3CodeSubselect(
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
dest.affinity = (u8)affinity; dest.affinity = (u8)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
if( sqlite3Select(pParse, pExpr->pSelect, &dest) ){ if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
return; return;
} }
pEList = pExpr->pSelect->pEList; pEList = pExpr->x.pSelect->pEList;
if( pEList && pEList->nExpr>0 ){ if( pEList && pEList->nExpr>0 ){
keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
pEList->a[0].pExpr); pEList->a[0].pExpr);
} }
}else if( pExpr->pList ){ }else if( pExpr->x.pList ){
/* Case 2: expr IN (exprlist) /* Case 2: expr IN (exprlist)
** **
** For each expression, build an index key from the evaluation and ** For each expression, build an index key from the evaluation and
@ -1341,7 +1540,7 @@ void sqlite3CodeSubselect(
** a column, use numeric affinity. ** a column, use numeric affinity.
*/ */
int i; int i;
ExprList *pList = pExpr->pList; ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem; struct ExprList_item *pItem;
int r1, r2, r3; int r1, r2, r3;
@ -1401,7 +1600,8 @@ void sqlite3CodeSubselect(
Select *pSel; Select *pSel;
SelectDest dest; SelectDest dest;
pSel = pExpr->pSelect; assert( ExprHasProperty(pExpr, EP_xIsSelect) );
pSel = pExpr->x.pSelect;
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem); sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
if( pExpr->op==TK_SELECT ){ if( pExpr->op==TK_SELECT ){
dest.eDest = SRT_Mem; dest.eDest = SRT_Mem;
@ -1795,7 +1995,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
break; break;
} }
case TK_STRING: { case TK_STRING: {
sqlite3DequoteExpr(db, pExpr); sqlite3DequoteExpr(pExpr);
sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0, sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0,
(char*)pExpr->token.z, pExpr->token.n); (char*)pExpr->token.z, pExpr->token.n);
break; break;
@ -1821,9 +2021,26 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
} }
#endif #endif
case TK_VARIABLE: { case TK_VARIABLE: {
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iTable, target); int iPrior;
if( pExpr->token.n>1 ){ VdbeOp *pOp;
sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n); if( pExpr->token.n<=1
&& (iPrior = sqlite3VdbeCurrentAddr(v)-1)>=0
&& (pOp = sqlite3VdbeGetOp(v, iPrior))->opcode==OP_Variable
&& pOp->p1+pOp->p3==pExpr->iTable
&& pOp->p2+pOp->p3==target
&& pOp->p4.z==0
){
/* If the previous instruction was a copy of the previous unnamed
** parameter into the previous register, then simply increment the
** repeat count on the prior instruction rather than making a new
** instruction.
*/
pOp->p3++;
}else{
sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iTable, target, 1);
if( pExpr->token.n>1 ){
sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n);
}
} }
break; break;
} }
@ -1985,7 +2202,9 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
} }
case TK_CONST_FUNC: case TK_CONST_FUNC:
case TK_FUNCTION: { case TK_FUNCTION: {
ExprList *pList = pExpr->pList; ExprList *pList = (
ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_SpanOnly) ? 0 : pExpr->x.pList
);
int nExpr = pList ? pList->nExpr : 0; int nExpr = pList ? pList->nExpr : 0;
FuncDef *pDef; FuncDef *pDef;
int nId; int nId;
@ -1995,6 +2214,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
u8 enc = ENC(db); u8 enc = ENC(db);
CollSeq *pColl = 0; CollSeq *pColl = 0;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
testcase( op==TK_CONST_FUNC ); testcase( op==TK_CONST_FUNC );
testcase( op==TK_FUNCTION ); testcase( op==TK_FUNCTION );
zId = (char*)pExpr->token.z; zId = (char*)pExpr->token.z;
@ -2162,7 +2382,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
*/ */
case TK_BETWEEN: { case TK_BETWEEN: {
Expr *pLeft = pExpr->pLeft; Expr *pLeft = pExpr->pLeft;
struct ExprList_item *pLItem = pExpr->pList->a; struct ExprList_item *pLItem = pExpr->x.pList->a;
Expr *pRight = pLItem->pExpr; Expr *pRight = pLItem->pExpr;
codeCompareOperands(pParse, pLeft, &r1, &regFree1, codeCompareOperands(pParse, pLeft, &r1, &regFree1,
@ -2222,10 +2442,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
Expr *pX; /* The X expression */ Expr *pX; /* The X expression */
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */ Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
assert(pExpr->pList); assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
assert((pExpr->pList->nExpr % 2) == 0); assert((pExpr->x.pList->nExpr % 2) == 0);
assert(pExpr->pList->nExpr > 0); assert(pExpr->x.pList->nExpr > 0);
pEList = pExpr->pList; pEList = pExpr->x.pList;
aListelem = pEList->a; aListelem = pEList->a;
nExpr = pEList->nExpr; nExpr = pEList->nExpr;
endLabel = sqlite3VdbeMakeLabel(v); endLabel = sqlite3VdbeMakeLabel(v);
@ -2273,15 +2493,15 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
"RAISE() may only be used within a trigger-program"); "RAISE() may only be used within a trigger-program");
return 0; return 0;
} }
if( pExpr->iColumn!=OE_Ignore ){ if( pExpr->affinity!=OE_Ignore ){
assert( pExpr->iColumn==OE_Rollback || assert( pExpr->affinity==OE_Rollback ||
pExpr->iColumn == OE_Abort || pExpr->affinity == OE_Abort ||
pExpr->iColumn == OE_Fail ); pExpr->affinity == OE_Fail );
sqlite3DequoteExpr(db, pExpr); sqlite3DequoteExpr(pExpr);
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, 0, sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0,
(char*)pExpr->token.z, pExpr->token.n); (char*)pExpr->token.z, pExpr->token.n);
} else { } else {
assert( pExpr->iColumn == OE_Ignore ); assert( pExpr->affinity == OE_Ignore );
sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump); sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
VdbeComment((v, "raise(IGNORE)")); VdbeComment((v, "raise(IGNORE)"));
@ -2438,7 +2658,8 @@ static int evalConstExpr(Walker *pWalker, Expr *pExpr){
** Mark them this way to avoid generated unneeded OP_SCopy ** Mark them this way to avoid generated unneeded OP_SCopy
** instructions. ** instructions.
*/ */
ExprList *pList = pExpr->pList; ExprList *pList = pExpr->x.pList;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
if( pList ){ if( pList ){
int i = pList->nExpr; int i = pList->nExpr;
struct ExprList_item *pItem = pList->a; struct ExprList_item *pItem = pList->a;
@ -2614,16 +2835,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
Expr compRight; Expr compRight;
Expr exprX; Expr exprX;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
exprX = *pExpr->pLeft; exprX = *pExpr->pLeft;
exprAnd.op = TK_AND; exprAnd.op = TK_AND;
exprAnd.pLeft = &compLeft; exprAnd.pLeft = &compLeft;
exprAnd.pRight = &compRight; exprAnd.pRight = &compRight;
compLeft.op = TK_GE; compLeft.op = TK_GE;
compLeft.pLeft = &exprX; compLeft.pLeft = &exprX;
compLeft.pRight = pExpr->pList->a[0].pExpr; compLeft.pRight = pExpr->x.pList->a[0].pExpr;
compRight.op = TK_LE; compRight.op = TK_LE;
compRight.pLeft = &exprX; compRight.pLeft = &exprX;
compRight.pRight = pExpr->pList->a[1].pExpr; compRight.pRight = pExpr->x.pList->a[1].pExpr;
exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1); exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1);
testcase( regFree1==0 ); testcase( regFree1==0 );
exprX.op = TK_REGISTER; exprX.op = TK_REGISTER;
@ -2765,16 +2987,17 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
Expr compRight; Expr compRight;
Expr exprX; Expr exprX;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
exprX = *pExpr->pLeft; exprX = *pExpr->pLeft;
exprAnd.op = TK_AND; exprAnd.op = TK_AND;
exprAnd.pLeft = &compLeft; exprAnd.pLeft = &compLeft;
exprAnd.pRight = &compRight; exprAnd.pRight = &compRight;
compLeft.op = TK_GE; compLeft.op = TK_GE;
compLeft.pLeft = &exprX; compLeft.pLeft = &exprX;
compLeft.pRight = pExpr->pList->a[0].pExpr; compLeft.pRight = pExpr->x.pList->a[0].pExpr;
compRight.op = TK_LE; compRight.op = TK_LE;
compRight.pLeft = &exprX; compRight.pLeft = &exprX;
compRight.pRight = pExpr->pList->a[1].pExpr; compRight.pRight = pExpr->x.pList->a[1].pExpr;
exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1); exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1);
testcase( regFree1==0 ); testcase( regFree1==0 );
exprX.op = TK_REGISTER; exprX.op = TK_REGISTER;
@ -2813,22 +3036,25 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
if( pA==0||pB==0 ){ if( pA==0||pB==0 ){
return pB==pA; return pB==pA;
} }
if( pA->op!=pB->op ) return 0; if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 0;
if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0;
if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0;
if( pA->pList ){
if( pB->pList==0 ) return 0;
if( pA->pList->nExpr!=pB->pList->nExpr ) return 0;
for(i=0; i<pA->pList->nExpr; i++){
if( !sqlite3ExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
return 0;
}
}
}else if( pB->pList ){
return 0; return 0;
} }
if( pA->pSelect || pB->pSelect ) return 0; if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 0;
if( pA->op!=pB->op ) return 0;
if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0;
if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0;
if( pA->x.pList && pB->x.pList ){
if( pA->x.pList->nExpr!=pB->x.pList->nExpr ) return 0;
for(i=0; i<pA->x.pList->nExpr; i++){
Expr *pExprA = pA->x.pList->a[i].pExpr;
Expr *pExprB = pB->x.pList->a[i].pExpr;
if( !sqlite3ExprCompare(pExprA, pExprB) ) return 0;
}
}else if( pA->x.pList || pB->x.pList ){
return 0;
}
if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0; if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0;
if( pA->op!=TK_COLUMN && pA->token.z ){ if( pA->op!=TK_COLUMN && pA->token.z ){
if( pB->token.z==0 ) return 0; if( pB->token.z==0 ) return 0;
@ -2976,12 +3202,13 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
u8 enc = ENC(pParse->db); u8 enc = ENC(pParse->db);
i = addAggInfoFunc(pParse->db, pAggInfo); i = addAggInfoFunc(pParse->db, pAggInfo);
if( i>=0 ){ if( i>=0 ){
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pItem = &pAggInfo->aFunc[i]; pItem = &pAggInfo->aFunc[i];
pItem->pExpr = pExpr; pItem->pExpr = pExpr;
pItem->iMem = ++pParse->nMem; pItem->iMem = ++pParse->nMem;
pItem->pFunc = sqlite3FindFunction(pParse->db, pItem->pFunc = sqlite3FindFunction(pParse->db,
(char*)pExpr->token.z, pExpr->token.n, (char*)pExpr->token.z, pExpr->token.n,
pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0); pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
if( pExpr->flags & EP_Distinct ){ if( pExpr->flags & EP_Distinct ){
pItem->iDistinct = pParse->nTab++; pItem->iDistinct = pParse->nTab++;
}else{ }else{

4
fts3.c
View file

@ -6410,7 +6410,7 @@ static void optimizeFunc(sqlite3_context *pContext,
i++; i++;
} }
/* If we managed to succesfully read them all, optimize them. */ /* If we managed to successfully read them all, optimize them. */
if( rc==SQLITE_DONE ){ if( rc==SQLITE_DONE ){
assert( i==nReaders ); assert( i==nReaders );
rc = optimizeInternal(v, readers, nReaders, &writer); rc = optimizeInternal(v, readers, nReaders, &writer);
@ -7001,7 +7001,7 @@ int sqlite3Fts3Init(sqlite3 *db){
); );
} }
/* An error has occured. Delete the hash table and return the error code. */ /* An error has occurred. Delete the hash table and return the error code. */
assert( rc!=SQLITE_OK ); assert( rc!=SQLITE_OK );
if( pHash ){ if( pHash ){
sqlite3Fts3HashClear(pHash); sqlite3Fts3HashClear(pHash);

View file

@ -212,8 +212,6 @@ static int getNextString(
if( ii==0 ){ if( ii==0 ){
memset(p, 0, nByte); memset(p, 0, nByte);
p->pPhrase = (Fts3Phrase *)&p[1]; p->pPhrase = (Fts3Phrase *)&p[1];
p->eType = FTSQUERY_PHRASE;
p->pPhrase->iColumn = pParse->iDefaultCol;
} }
p->pPhrase = (Fts3Phrase *)&p[1]; p->pPhrase = (Fts3Phrase *)&p[1];
p->pPhrase->nToken = ii+1; p->pPhrase->nToken = ii+1;
@ -237,19 +235,25 @@ static int getNextString(
char *zNew; char *zNew;
int nNew = 0; int nNew = 0;
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
nByte += (p->pPhrase->nToken-1) * sizeof(struct PhraseToken); nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken);
p = fts3ReallocOrFree(p, nByte + nTemp); p = fts3ReallocOrFree(p, nByte + nTemp);
if( !p ){ if( !p ){
goto no_mem; goto no_mem;
} }
if( zTemp ){
zNew = &(((char *)p)[nByte]);
memcpy(zNew, zTemp, nTemp);
}else{
memset(p, 0, nByte+nTemp);
}
p->pPhrase = (Fts3Phrase *)&p[1]; p->pPhrase = (Fts3Phrase *)&p[1];
zNew = &(((char *)p)[nByte]);
memcpy(zNew, zTemp, nTemp);
for(jj=0; jj<p->pPhrase->nToken; jj++){ for(jj=0; jj<p->pPhrase->nToken; jj++){
p->pPhrase->aToken[jj].z = &zNew[nNew]; p->pPhrase->aToken[jj].z = &zNew[nNew];
nNew += p->pPhrase->aToken[jj].n; nNew += p->pPhrase->aToken[jj].n;
} }
sqlite3_free(zTemp); sqlite3_free(zTemp);
p->eType = FTSQUERY_PHRASE;
p->pPhrase->iColumn = pParse->iDefaultCol;
rc = SQLITE_OK; rc = SQLITE_OK;
} }

20
func.c
View file

@ -16,7 +16,7 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope. ** All other code has file scope.
** **
** $Id: func.c,v 1.222 2009/02/04 03:59:25 shane Exp $ ** $Id: func.c,v 1.225 2009/03/27 15:26:03 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <stdlib.h> #include <stdlib.h>
@ -1305,9 +1305,9 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
}else{ }else{
pInfo = (struct compareInfo*)&likeInfoNorm; pInfo = (struct compareInfo*)&likeInfoNorm;
} }
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0); sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0);
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0); sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0);
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8, sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY,
(struct compareInfo*)&globInfo, likeFunc, 0,0); (struct compareInfo*)&globInfo, likeFunc, 0,0);
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
setLikeOptFlag(db, "like", setLikeOptFlag(db, "like",
@ -1323,12 +1323,13 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
*/ */
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
FuncDef *pDef; FuncDef *pDef;
if( pExpr->op!=TK_FUNCTION || !pExpr->pList ){ if( pExpr->op!=TK_FUNCTION
return 0; || !pExpr->x.pList
} || pExpr->x.pList->nExpr!=2
if( pExpr->pList->nExpr!=2 ){ ){
return 0; return 0;
} }
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pDef = sqlite3FindFunction(db, (char*)pExpr->token.z, pExpr->token.n, 2, pDef = sqlite3FindFunction(db, (char*)pExpr->token.z, pExpr->token.n, 2,
SQLITE_UTF8, 0); SQLITE_UTF8, 0);
if( pDef==0 || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){ if( pDef==0 || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){
@ -1412,7 +1413,8 @@ void sqlite3RegisterGlobalFunctions(void){
AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
{0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0},
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ), AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize), AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize), AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),

View file

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite. ** to handle INSERT statements in SQLite.
** **
** $Id: insert.c,v 1.256 2008/12/10 21:19:57 drh Exp $ ** $Id: insert.c,v 1.260 2009/02/28 10:47:42 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -168,7 +168,7 @@ static int autoIncBegin(
if( pTab->tabFlags & TF_Autoincrement ){ if( pTab->tabFlags & TF_Autoincrement ){
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
Db *pDb = &pParse->db->aDb[iDb]; Db *pDb = &pParse->db->aDb[iDb];
int iCur = pParse->nTab; int iCur = pParse->nTab++;
int addr; /* Address of the top of the loop */ int addr; /* Address of the top of the loop */
assert( v ); assert( v );
pParse->nMem++; /* Holds name of table */ pParse->nMem++; /* Holds name of table */
@ -217,7 +217,7 @@ static void autoIncEnd(
int memId /* Memory cell holding the maximum rowid */ int memId /* Memory cell holding the maximum rowid */
){ ){
if( pTab->tabFlags & TF_Autoincrement ){ if( pTab->tabFlags & TF_Autoincrement ){
int iCur = pParse->nTab; int iCur = pParse->nTab++;
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
Db *pDb = &pParse->db->aDb[iDb]; Db *pDb = &pParse->db->aDb[iDb];
int j1; int j1;
@ -401,7 +401,8 @@ void sqlite3Insert(
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to insert into a view */ int isView; /* True if attempting to insert into a view */
int triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ Trigger *pTrigger; /* List of triggers on pTab, if required */
int tmask; /* Mask of trigger times */
#endif #endif
db = pParse->db; db = pParse->db;
@ -431,22 +432,24 @@ void sqlite3Insert(
** inserted into is a view ** inserted into is a view
*/ */
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
triggers_exist = sqlite3TriggersExist(pTab, TK_INSERT, 0); pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask);
isView = pTab->pSelect!=0; isView = pTab->pSelect!=0;
#else #else
# define triggers_exist 0 # define pTrigger 0
# define tmask 0
# define isView 0 # define isView 0
#endif #endif
#ifdef SQLITE_OMIT_VIEW #ifdef SQLITE_OMIT_VIEW
# undef isView # undef isView
# define isView 0 # define isView 0
#endif #endif
assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) );
/* Ensure that: /* Ensure that:
* (a) the table is not read-only, * (a) the table is not read-only,
* (b) that if it is a view then ON INSERT triggers exist * (b) that if it is a view then ON INSERT triggers exist
*/ */
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
goto insert_cleanup; goto insert_cleanup;
} }
assert( pTab!=0 ); assert( pTab!=0 );
@ -464,10 +467,10 @@ void sqlite3Insert(
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v==0 ) goto insert_cleanup; if( v==0 ) goto insert_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, iDb); sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb);
/* if there are row triggers, allocate a temp table for new.* references. */ /* if there are row triggers, allocate a temp table for new.* references. */
if( triggers_exist ){ if( pTrigger ){
newIdx = pParse->nTab++; newIdx = pParse->nTab++;
} }
@ -482,7 +485,7 @@ void sqlite3Insert(
** This is the 2nd template. ** This is the 2nd template.
*/ */
if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){
assert( !triggers_exist ); assert( !pTrigger );
assert( pList==0 ); assert( pList==0 );
goto insert_cleanup; goto insert_cleanup;
} }
@ -557,7 +560,7 @@ void sqlite3Insert(
** of the tables being read by the SELECT statement. Also use a ** of the tables being read by the SELECT statement. Also use a
** temp table in the case of row triggers. ** temp table in the case of row triggers.
*/ */
if( triggers_exist || readsTable(v, addrSelect, iDb, pTab) ){ if( pTrigger || readsTable(v, addrSelect, iDb, pTab) ){
useTempTable = 1; useTempTable = 1;
} }
@ -676,9 +679,8 @@ void sqlite3Insert(
/* Open the temp table for FOR EACH ROW triggers /* Open the temp table for FOR EACH ROW triggers
*/ */
if( triggers_exist ){ if( pTrigger ){
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
} }
/* Initialize the count of rows to be inserted /* Initialize the count of rows to be inserted
@ -745,7 +747,7 @@ void sqlite3Insert(
/* Run the BEFORE and INSTEAD OF triggers, if there are any /* Run the BEFORE and INSTEAD OF triggers, if there are any
*/ */
endOfLoop = sqlite3VdbeMakeLabel(v); endOfLoop = sqlite3VdbeMakeLabel(v);
if( triggers_exist & TRIGGER_BEFORE ){ if( tmask & TRIGGER_BEFORE ){
int regTrigRowid; int regTrigRowid;
int regCols; int regCols;
int regRec; int regRec;
@ -813,8 +815,8 @@ void sqlite3Insert(
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol); sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
/* Fire BEFORE or INSTEAD OF triggers */ /* Fire BEFORE or INSTEAD OF triggers */
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab, if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
newIdx, -1, onError, endOfLoop, 0, 0) ){ pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
goto insert_cleanup; goto insert_cleanup;
} }
} }
@ -936,7 +938,7 @@ void sqlite3Insert(
regIns, regIns,
aRegIdx, aRegIdx,
0, 0,
(triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1, (tmask&TRIGGER_AFTER) ? newIdx : -1,
appendFlag appendFlag
); );
} }
@ -948,10 +950,10 @@ void sqlite3Insert(
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
} }
if( triggers_exist ){ if( pTrigger ){
/* Code AFTER triggers */ /* Code AFTER triggers */
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab, if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
newIdx, -1, onError, endOfLoop, 0, 0) ){ pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
goto insert_cleanup; goto insert_cleanup;
} }
} }
@ -1125,7 +1127,6 @@ void sqlite3GenerateConstraintChecks(
if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){ if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){
onError = OE_Abort; onError = OE_Abort;
} }
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|| onError==OE_Ignore || onError==OE_Replace ); || onError==OE_Ignore || onError==OE_Replace );
switch( onError ){ switch( onError ){
@ -1133,22 +1134,24 @@ void sqlite3GenerateConstraintChecks(
case OE_Abort: case OE_Abort:
case OE_Fail: { case OE_Fail: {
char *zMsg; char *zMsg;
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_CONSTRAINT, onError); j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull,
SQLITE_CONSTRAINT, onError, regData+i);
zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL",
pTab->zName, pTab->aCol[i].zName); pTab->zName, pTab->aCol[i].zName);
sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
break; break;
} }
case OE_Ignore: { case OE_Ignore: {
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest);
break; break;
} }
case OE_Replace: { case OE_Replace: {
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i); sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
sqlite3VdbeJumpHere(v, j1);
break; break;
} }
} }
sqlite3VdbeJumpHere(v, j1);
} }
/* Test all CHECK constraints /* Test all CHECK constraints
@ -1530,7 +1533,7 @@ static int xferOptimization(
if( pSelect==0 ){ if( pSelect==0 ){
return 0; /* Must be of the form INSERT INTO ... SELECT ... */ return 0; /* Must be of the form INSERT INTO ... SELECT ... */
} }
if( pDest->pTrigger ){ if( sqlite3TriggerList(pParse, pDest) ){
return 0; /* tab1 must not have triggers */ return 0; /* tab1 must not have triggers */
} }
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE

View file

@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be ** other files are for internal use by SQLite and should not be
** accessed by users of the library. ** accessed by users of the library.
** **
** $Id: legacy.c,v 1.31 2009/01/20 16:53:40 danielk1977 Exp $ ** $Id: legacy.c,v 1.32 2009/03/19 18:51:07 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -101,7 +101,7 @@ int sqlite3_exec(
} }
if( xCallback(pArg, nCol, azVals, azCols) ){ if( xCallback(pArg, nCol, azVals, azCols) ){
rc = SQLITE_ABORT; rc = SQLITE_ABORT;
sqlite3_finalize(pStmt); sqlite3VdbeFinalize((Vdbe *)pStmt);
pStmt = 0; pStmt = 0;
sqlite3Error(db, SQLITE_ABORT, 0); sqlite3Error(db, SQLITE_ABORT, 0);
goto exec_out; goto exec_out;
@ -109,7 +109,7 @@ int sqlite3_exec(
} }
if( rc!=SQLITE_ROW ){ if( rc!=SQLITE_ROW ){
rc = sqlite3_finalize(pStmt); rc = sqlite3VdbeFinalize((Vdbe *)pStmt);
pStmt = 0; pStmt = 0;
if( rc!=SQLITE_SCHEMA ){ if( rc!=SQLITE_SCHEMA ){
nRetry = 0; nRetry = 0;
@ -125,7 +125,7 @@ int sqlite3_exec(
} }
exec_out: exec_out:
if( pStmt ) sqlite3_finalize(pStmt); if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt);
sqlite3DbFree(db, azCols); sqlite3DbFree(db, azCols);
rc = sqlite3ApiExit(db, rc); rc = sqlite3ApiExit(db, rc);

85
main.c
View file

@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be ** other files are for internal use by SQLite and should not be
** accessed by users of the library. ** accessed by users of the library.
** **
** $Id: main.c,v 1.528 2009/02/05 16:31:46 drh Exp $ ** $Id: main.c,v 1.534 2009/03/23 04:33:32 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -404,12 +404,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
sz = 0; sz = 0;
pStart = 0; pStart = 0;
}else if( pBuf==0 ){ }else if( pBuf==0 ){
sz = (sz + 7)&~7; sz = ROUND8(sz);
sqlite3BeginBenignMalloc(); sqlite3BeginBenignMalloc();
pStart = sqlite3Malloc( sz*cnt ); pStart = sqlite3Malloc( sz*cnt );
sqlite3EndBenignMalloc(); sqlite3EndBenignMalloc();
}else{ }else{
sz = sz&~7; sz = ROUNDDOWN8(sz);
pStart = pBuf; pStart = pBuf;
} }
db->lookaside.pStart = pStart; db->lookaside.pStart = pStart;
@ -560,6 +560,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){
sqlite3DbFree(db, pTmp); sqlite3DbFree(db, pTmp);
} }
db->nSavepoint = 0; db->nSavepoint = 0;
db->nStatement = 0;
db->isTransactionSavepoint = 0; db->isTransactionSavepoint = 0;
} }
@ -629,6 +630,12 @@ int sqlite3_close(sqlite3 *db){
} }
} }
sqlite3ResetInternalSchema(db, 0); sqlite3ResetInternalSchema(db, 0);
/* Tell the code in notify.c that the connection no longer holds any
** locks and does not require any further unlock-notify callbacks.
*/
sqlite3ConnectionClosed(db);
assert( db->nDb<=2 ); assert( db->nDb<=2 );
assert( db->aDb==db->aDbStatic ); assert( db->aDb==db->aDbStatic );
for(j=0; j<ArraySize(db->aFunc.a); j++){ for(j=0; j<ArraySize(db->aFunc.a); j++){
@ -1238,15 +1245,15 @@ const char *sqlite3_errmsg(sqlite3 *db){
if( !sqlite3SafetyCheckSickOrOk(db) ){ if( !sqlite3SafetyCheckSickOrOk(db) ){
return sqlite3ErrStr(SQLITE_MISUSE); return sqlite3ErrStr(SQLITE_MISUSE);
} }
if( db->mallocFailed ){
return sqlite3ErrStr(SQLITE_NOMEM);
}
sqlite3_mutex_enter(db->mutex); sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed ); if( db->mallocFailed ){
z = (char*)sqlite3_value_text(db->pErr); z = sqlite3ErrStr(SQLITE_NOMEM);
assert( !db->mallocFailed ); }else{
if( z==0 ){ z = (char*)sqlite3_value_text(db->pErr);
z = sqlite3ErrStr(db->errCode); assert( !db->mallocFailed );
if( z==0 ){
z = sqlite3ErrStr(db->errCode);
}
} }
sqlite3_mutex_leave(db->mutex); sqlite3_mutex_leave(db->mutex);
return z; return z;
@ -1258,46 +1265,42 @@ const char *sqlite3_errmsg(sqlite3 *db){
** error. ** error.
*/ */
const void *sqlite3_errmsg16(sqlite3 *db){ const void *sqlite3_errmsg16(sqlite3 *db){
/* Because all the characters in the string are in the unicode static const u16 outOfMem[] = {
** range 0x00-0xFF, if we pad the big-endian string with a 'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0
** zero byte, we can obtain the little-endian string with
** &big_endian[1].
*/
static const char outOfMemBe[] = {
0, 'o', 0, 'u', 0, 't', 0, ' ',
0, 'o', 0, 'f', 0, ' ',
0, 'm', 0, 'e', 0, 'm', 0, 'o', 0, 'r', 0, 'y', 0, 0, 0
}; };
static const char misuseBe [] = { static const u16 misuse[] = {
0, 'l', 0, 'i', 0, 'b', 0, 'r', 0, 'a', 0, 'r', 0, 'y', 0, ' ', 'l', 'i', 'b', 'r', 'a', 'r', 'y', ' ',
0, 'r', 0, 'o', 0, 'u', 0, 't', 0, 'i', 0, 'n', 0, 'e', 0, ' ', 'r', 'o', 'u', 't', 'i', 'n', 'e', ' ',
0, 'c', 0, 'a', 0, 'l', 0, 'l', 0, 'e', 0, 'd', 0, ' ', 'c', 'a', 'l', 'l', 'e', 'd', ' ',
0, 'o', 0, 'u', 0, 't', 0, ' ', 'o', 'u', 't', ' ',
0, 'o', 0, 'f', 0, ' ', 'o', 'f', ' ',
0, 's', 0, 'e', 0, 'q', 0, 'u', 0, 'e', 0, 'n', 0, 'c', 0, 'e', 0, 0, 0 's', 'e', 'q', 'u', 'e', 'n', 'c', 'e', 0
}; };
const void *z; const void *z;
if( !db ){ if( !db ){
return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); return (void *)outOfMem;
} }
if( !sqlite3SafetyCheckSickOrOk(db) ){ if( !sqlite3SafetyCheckSickOrOk(db) ){
return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); return (void *)misuse;
} }
sqlite3_mutex_enter(db->mutex); sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed ); if( db->mallocFailed ){
z = sqlite3_value_text16(db->pErr); z = (void *)outOfMem;
if( z==0 ){ }else{
sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
SQLITE_UTF8, SQLITE_STATIC);
z = sqlite3_value_text16(db->pErr); z = sqlite3_value_text16(db->pErr);
if( z==0 ){
sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
SQLITE_UTF8, SQLITE_STATIC);
z = sqlite3_value_text16(db->pErr);
}
/* A malloc() may have failed within the call to sqlite3_value_text16()
** above. If this is the case, then the db->mallocFailed flag needs to
** be cleared before returning. Do this directly, instead of via
** sqlite3ApiExit(), to avoid setting the database handle error message.
*/
db->mallocFailed = 0;
} }
/* A malloc() may have failed within the call to sqlite3_value_text16()
** above. If this is the case, then the db->mallocFailed flag needs to
** be cleared before returning. Do this directly, instead of via
** sqlite3ApiExit(), to avoid setting the database handle error message.
*/
db->mallocFailed = 0;
sqlite3_mutex_leave(db->mutex); sqlite3_mutex_leave(db->mutex);
return z; return z;
} }
@ -1940,7 +1943,6 @@ int sqlite3_table_column_metadata(
(void)sqlite3SafetyOn(db); (void)sqlite3SafetyOn(db);
sqlite3BtreeEnterAll(db); sqlite3BtreeEnterAll(db);
rc = sqlite3Init(db, &zErrMsg); rc = sqlite3Init(db, &zErrMsg);
sqlite3BtreeLeaveAll(db);
if( SQLITE_OK!=rc ){ if( SQLITE_OK!=rc ){
goto error_out; goto error_out;
} }
@ -1996,6 +1998,7 @@ int sqlite3_table_column_metadata(
} }
error_out: error_out:
sqlite3BtreeLeaveAll(db);
(void)sqlite3SafetyOff(db); (void)sqlite3SafetyOff(db);
/* Whether the function call succeeded or failed, set the output parameters /* Whether the function call succeeded or failed, set the output parameters

104
malloc.c
View file

@ -12,7 +12,7 @@
** **
** Memory allocation functions used throughout sqlite. ** Memory allocation functions used throughout sqlite.
** **
** $Id: malloc.c,v 1.56 2009/02/17 18:37:29 drh Exp $ ** $Id: malloc.c,v 1.61 2009/03/24 15:08:10 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <stdarg.h> #include <stdarg.h>
@ -121,7 +121,7 @@ int sqlite3MallocInit(void){
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100 if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
&& sqlite3GlobalConfig.nScratch>=0 ){ && sqlite3GlobalConfig.nScratch>=0 ){
int i; int i;
sqlite3GlobalConfig.szScratch = (sqlite3GlobalConfig.szScratch - 4) & ~7; sqlite3GlobalConfig.szScratch = ROUNDDOWN8(sqlite3GlobalConfig.szScratch-4);
mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch) mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch)
[sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch]; [sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch];
for(i=0; i<sqlite3GlobalConfig.nScratch; i++){ mem0.aScratchFree[i] = i; } for(i=0; i<sqlite3GlobalConfig.nScratch; i++){ mem0.aScratchFree[i] = i; }
@ -134,7 +134,7 @@ int sqlite3MallocInit(void){
&& sqlite3GlobalConfig.nPage>=1 ){ && sqlite3GlobalConfig.nPage>=1 ){
int i; int i;
int overhead; int overhead;
int sz = sqlite3GlobalConfig.szPage & ~7; int sz = ROUNDDOWN8(sqlite3GlobalConfig.szPage);
int n = sqlite3GlobalConfig.nPage; int n = sqlite3GlobalConfig.nPage;
overhead = (4*n + sz - 1)/sz; overhead = (4*n + sz - 1)/sz;
sqlite3GlobalConfig.nPage -= overhead; sqlite3GlobalConfig.nPage -= overhead;
@ -407,95 +407,6 @@ void sqlite3ScratchFree(void *p){
} }
} }
/*
** Allocate memory to be used by the page cache. Make use of the
** memory buffer provided by SQLITE_CONFIG_PAGECACHE if there is one
** and that memory is of the right size and is not completely
** consumed. Otherwise, failover to sqlite3Malloc().
*/
#if 0
void *sqlite3PageMalloc(int n){
void *p;
assert( n>0 );
assert( (n & (n-1))==0 );
assert( n>=512 && n<=32768 );
if( sqlite3GlobalConfig.szPage<n ){
goto page_overflow;
}else{
sqlite3_mutex_enter(mem0.mutex);
if( mem0.nPageFree==0 ){
sqlite3_mutex_leave(mem0.mutex);
goto page_overflow;
}else{
int i;
i = mem0.aPageFree[--mem0.nPageFree];
sqlite3_mutex_leave(mem0.mutex);
i *= sqlite3GlobalConfig.szPage;
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
p = (void*)&((char*)sqlite3GlobalConfig.pPage)[i];
}
}
return p;
page_overflow:
if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
n = mallocWithAlarm(n, &p);
if( p ) sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, n);
sqlite3_mutex_leave(mem0.mutex);
}else{
p = sqlite3GlobalConfig.m.xMalloc(n);
}
return p;
}
void sqlite3PageFree(void *p){
if( p ){
if( sqlite3GlobalConfig.pPage==0
|| p<sqlite3GlobalConfig.pPage
|| p>=(void*)mem0.aPageFree ){
/* In this case, the page allocation was obtained from a regular
** call to sqlite3_mem_methods.xMalloc() (a page-cache-memory
** "overflow"). Free the block with sqlite3_mem_methods.xFree().
*/
if( sqlite3GlobalConfig.bMemstat ){
int iSize = sqlite3MallocSize(p);
sqlite3_mutex_enter(mem0.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
sqlite3GlobalConfig.m.xFree(p);
sqlite3_mutex_leave(mem0.mutex);
}else{
sqlite3GlobalConfig.m.xFree(p);
}
}else{
/* The page allocation was allocated from the sqlite3GlobalConfig.pPage
** buffer. In this case all that is add the index of the page in
** the sqlite3GlobalConfig.pPage array to the set of free indexes stored
** in the mem0.aPageFree[] array.
*/
int i;
i = (u8 *)p - (u8 *)sqlite3GlobalConfig.pPage;
i /= sqlite3GlobalConfig.szPage;
assert( i>=0 && i<sqlite3GlobalConfig.nPage );
sqlite3_mutex_enter(mem0.mutex);
assert( mem0.nPageFree<sqlite3GlobalConfig.nPage );
mem0.aPageFree[mem0.nPageFree++] = i;
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
sqlite3_mutex_leave(mem0.mutex);
#if !defined(NDEBUG) && 0
/* Assert that a duplicate was not just inserted into aPageFree[]. */
for(i=0; i<mem0.nPageFree-1; i++){
assert( mem0.aPageFree[i]!=mem0.aPageFree[mem0.nPageFree-1] );
}
#endif
}
}
}
#endif
/* /*
** TRUE if p is a lookaside memory allocation from db ** TRUE if p is a lookaside memory allocation from db
*/ */
@ -515,6 +426,7 @@ int sqlite3MallocSize(void *p){
return sqlite3GlobalConfig.m.xSize(p); return sqlite3GlobalConfig.m.xSize(p);
} }
int sqlite3DbMallocSize(sqlite3 *db, void *p){ int sqlite3DbMallocSize(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
if( p==0 ){ if( p==0 ){
return 0; return 0;
}else if( isLookaside(db, p) ){ }else if( isLookaside(db, p) ){
@ -544,6 +456,7 @@ void sqlite3_free(void *p){
** connection. ** connection.
*/ */
void sqlite3DbFree(sqlite3 *db, void *p){ void sqlite3DbFree(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
if( isLookaside(db, p) ){ if( isLookaside(db, p) ){
LookasideSlot *pBuf = (LookasideSlot*)p; LookasideSlot *pBuf = (LookasideSlot*)p;
pBuf->pNext = db->lookaside.pFree; pBuf->pNext = db->lookaside.pFree;
@ -652,6 +565,7 @@ void *sqlite3DbMallocZero(sqlite3 *db, int n){
*/ */
void *sqlite3DbMallocRaw(sqlite3 *db, int n){ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
void *p; void *p;
assert( db==0 || sqlite3_mutex_held(db->mutex) );
#ifndef SQLITE_OMIT_LOOKASIDE #ifndef SQLITE_OMIT_LOOKASIDE
if( db ){ if( db ){
LookasideSlot *pBuf; LookasideSlot *pBuf;
@ -686,6 +600,8 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
*/ */
void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){ void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
void *pNew = 0; void *pNew = 0;
assert( db!=0 );
assert( sqlite3_mutex_held(db->mutex) );
if( db->mallocFailed==0 ){ if( db->mallocFailed==0 ){
if( p==0 ){ if( p==0 ){
return sqlite3DbMallocRaw(db, n); return sqlite3DbMallocRaw(db, n);
@ -780,10 +696,10 @@ void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
** sqlite3_realloc. ** sqlite3_realloc.
** **
** The returned value is normally a copy of the second argument to this ** The returned value is normally a copy of the second argument to this
** function. However, if a malloc() failure has occured since the previous ** function. However, if a malloc() failure has occurred since the previous
** invocation SQLITE_NOMEM is returned instead. ** invocation SQLITE_NOMEM is returned instead.
** **
** If the first argument, db, is not NULL and a malloc() error has occured, ** If the first argument, db, is not NULL and a malloc() error has occurred,
** then the connection error-code (the value returned by sqlite3_errcode()) ** then the connection error-code (the value returned by sqlite3_errcode())
** is set to SQLITE_NOMEM. ** is set to SQLITE_NOMEM.
*/ */

8
mem1.c
View file

@ -17,7 +17,7 @@
** This file contains implementations of the low-level memory allocation ** This file contains implementations of the low-level memory allocation
** routines specified in the sqlite3_mem_methods object. ** routines specified in the sqlite3_mem_methods object.
** **
** $Id: mem1.c,v 1.29 2008/12/10 21:19:57 drh Exp $ ** $Id: mem1.c,v 1.30 2009/03/23 04:33:33 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -39,7 +39,7 @@
static void *sqlite3MemMalloc(int nByte){ static void *sqlite3MemMalloc(int nByte){
sqlite3_int64 *p; sqlite3_int64 *p;
assert( nByte>0 ); assert( nByte>0 );
nByte = (nByte+7)&~7; nByte = ROUND8(nByte);
p = malloc( nByte+8 ); p = malloc( nByte+8 );
if( p ){ if( p ){
p[0] = nByte; p[0] = nByte;
@ -76,7 +76,7 @@ static void sqlite3MemFree(void *pPrior){
static void *sqlite3MemRealloc(void *pPrior, int nByte){ static void *sqlite3MemRealloc(void *pPrior, int nByte){
sqlite3_int64 *p = (sqlite3_int64*)pPrior; sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 && nByte>0 ); assert( pPrior!=0 && nByte>0 );
nByte = (nByte+7)&~7; nByte = ROUND8(nByte);
p = (sqlite3_int64*)pPrior; p = (sqlite3_int64*)pPrior;
p--; p--;
p = realloc(p, nByte+8 ); p = realloc(p, nByte+8 );
@ -103,7 +103,7 @@ static int sqlite3MemSize(void *pPrior){
** Round up a request size to the next valid allocation size. ** Round up a request size to the next valid allocation size.
*/ */
static int sqlite3MemRoundup(int n){ static int sqlite3MemRoundup(int n){
return (n+7) & ~7; return ROUND8(n);
} }
/* /*

13
mem2.c
View file

@ -19,7 +19,7 @@
** This file contains implementations of the low-level memory allocation ** This file contains implementations of the low-level memory allocation
** routines specified in the sqlite3_mem_methods object. ** routines specified in the sqlite3_mem_methods object.
** **
** $Id: mem2.c,v 1.43 2009/02/05 03:00:06 shane Exp $ ** $Id: mem2.c,v 1.45 2009/03/23 04:33:33 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -128,7 +128,7 @@ static struct {
** Adjust memory usage statistics ** Adjust memory usage statistics
*/ */
static void adjustStats(int iSize, int increment){ static void adjustStats(int iSize, int increment){
int i = ((iSize+7)&~7)/8; int i = ROUND8(iSize)/8;
if( i>NCSIZE-1 ){ if( i>NCSIZE-1 ){
i = NCSIZE - 1; i = NCSIZE - 1;
} }
@ -159,7 +159,7 @@ static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
p = (struct MemBlockHdr*)pAllocation; p = (struct MemBlockHdr*)pAllocation;
p--; p--;
assert( p->iForeGuard==(int)FOREGUARD ); assert( p->iForeGuard==(int)FOREGUARD );
nReserve = (p->iSize+7)&~7; nReserve = ROUND8(p->iSize);
pInt = (int*)pAllocation; pInt = (int*)pAllocation;
pU8 = (u8*)pAllocation; pU8 = (u8*)pAllocation;
assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD ); assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD );
@ -209,7 +209,7 @@ static void sqlite3MemShutdown(void *NotUsed){
** Round up a request size to the next valid allocation size. ** Round up a request size to the next valid allocation size.
*/ */
static int sqlite3MemRoundup(int n){ static int sqlite3MemRoundup(int n){
return (n+7) & ~7; return ROUND8(n);
} }
/* /*
@ -225,7 +225,7 @@ static void *sqlite3MemMalloc(int nByte){
int nReserve; int nReserve;
sqlite3_mutex_enter(mem.mutex); sqlite3_mutex_enter(mem.mutex);
assert( mem.disallow==0 ); assert( mem.disallow==0 );
nReserve = (nByte+7)&~7; nReserve = ROUND8(nByte);
totalSize = nReserve + sizeof(*pHdr) + sizeof(int) + totalSize = nReserve + sizeof(*pHdr) + sizeof(int) +
mem.nBacktrace*sizeof(void*) + mem.nTitle; mem.nBacktrace*sizeof(void*) + mem.nTitle;
p = malloc(totalSize); p = malloc(totalSize);
@ -248,6 +248,7 @@ static void *sqlite3MemMalloc(int nByte){
void *aAddr[40]; void *aAddr[40];
pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1;
memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*));
assert(pBt[0]);
if( mem.xBacktrace ){ if( mem.xBacktrace ){
mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]); mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]);
} }
@ -371,7 +372,7 @@ void sqlite3MemdebugSettitle(const char *zTitle){
if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1; if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1;
memcpy(mem.zTitle, zTitle, n); memcpy(mem.zTitle, zTitle, n);
mem.zTitle[n] = 0; mem.zTitle[n] = 0;
mem.nTitle = (n+7)&~7; mem.nTitle = ROUND8(n);
sqlite3_mutex_leave(mem.mutex); sqlite3_mutex_leave(mem.mutex);
} }

309
notify.c Normal file
View file

@ -0,0 +1,309 @@
/*
** 2009 March 3
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the implementation of the sqlite3_unlock_notify()
** API method and its associated functionality.
**
** $Id: notify.c,v 1.2 2009/03/25 16:51:43 drh Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"
/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
/*
** Public interfaces:
**
** sqlite3ConnectionBlocked()
** sqlite3ConnectionUnlocked()
** sqlite3ConnectionClosed()
** sqlite3_unlock_notify()
*/
#define assertMutexHeld() \
assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) )
/*
** Head of a linked list of all sqlite3 objects created by this process
** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
** is not NULL. This variable may only accessed while the STATIC_MASTER
** mutex is held.
*/
static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
#ifndef NDEBUG
/*
** This function is a complex assert() that verifies the following
** properties of the blocked connections list:
**
** 1) Each entry in the list has a non-NULL value for either
** pUnlockConnection or pBlockingConnection, or both.
**
** 2) All entries in the list that share a common value for
** xUnlockNotify are grouped together.
**
** 3) If the argument db is not NULL, then none of the entries in the
** blocked connections list have pUnlockConnection or pBlockingConnection
** set to db. This is used when closing connection db.
*/
static void checkListProperties(sqlite3 *db){
sqlite3 *p;
for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
int seen = 0;
sqlite3 *p2;
/* Verify property (1) */
assert( p->pUnlockConnection || p->pBlockingConnection );
/* Verify property (2) */
for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){
if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1;
assert( p2->xUnlockNotify==p->xUnlockNotify || !seen );
assert( db==0 || p->pUnlockConnection!=db );
assert( db==0 || p->pBlockingConnection!=db );
}
}
}
#else
# define checkListProperties(x)
#endif
/*
** Remove connection db from the blocked connections list. If connection
** db is not currently a part of the list, this function is a no-op.
*/
static void removeFromBlockedList(sqlite3 *db){
sqlite3 **pp;
assertMutexHeld();
for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){
if( *pp==db ){
*pp = (*pp)->pNextBlocked;
break;
}
}
}
/*
** Add connection db to the blocked connections list. It is assumed
** that it is not already a part of the list.
*/
static void addToBlockedList(sqlite3 *db){
sqlite3 **pp;
assertMutexHeld();
for(
pp=&sqlite3BlockedList;
*pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
pp=&(*pp)->pNextBlocked
);
db->pNextBlocked = *pp;
*pp = db;
}
/*
** Obtain the STATIC_MASTER mutex.
*/
static void enterMutex(){
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
checkListProperties(0);
}
/*
** Release the STATIC_MASTER mutex.
*/
static void leaveMutex(){
assertMutexHeld();
checkListProperties(0);
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}
/*
** Register an unlock-notify callback.
*/
int sqlite3_unlock_notify(
sqlite3 *db,
void (*xNotify)(void **, int),
void *pArg
){
int rc = SQLITE_OK;
sqlite3_mutex_enter(db->mutex);
enterMutex();
if( 0==db->pBlockingConnection ){
/* The blocking transaction has been concluded. Or there never was a
** blocking transaction. In either case, invoke the notify callback
** immediately.
*/
xNotify(&pArg, 1);
}else{
sqlite3 *p;
for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection);
if( p ){
rc = SQLITE_LOCKED; /* Deadlock detected. */
}else{
db->pUnlockConnection = db->pBlockingConnection;
db->xUnlockNotify = xNotify;
db->pUnlockArg = pArg;
removeFromBlockedList(db);
addToBlockedList(db);
}
}
leaveMutex();
assert( !db->mallocFailed );
sqlite3Error(db, rc, (rc?"database is deadlocked":0));
sqlite3_mutex_leave(db->mutex);
return rc;
}
/*
** This function is called while stepping or preparing a statement
** associated with connection db. The operation will return SQLITE_LOCKED
** to the user because it requires a lock that will not be available
** until connection pBlocker concludes its current transaction.
*/
void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
enterMutex();
if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
addToBlockedList(db);
}
db->pBlockingConnection = pBlocker;
leaveMutex();
}
/*
** The transaction opened by database db has just finished. Locks held
** by database connection db have been released.
**
** This function loops through each entry in the blocked connections
** list and does the following:
**
** 1) If the sqlite3.pBlockingConnection member of a list entry is
** set to db, then set pBlockingConnection=0.
**
** 2) If the sqlite3.pUnlockConnection member of a list entry is
** set to db, then invoke the configured unlock-notify callback and
** set pUnlockConnection=0.
**
** 3) If the two steps above mean that pBlockingConnection==0 and
** pUnlockConnection==0, remove the entry from the blocked connections
** list.
*/
void sqlite3ConnectionUnlocked(sqlite3 *db){
void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
int nArg = 0; /* Number of entries in aArg[] */
sqlite3 **pp; /* Iterator variable */
void *aStatic[16];
void **aArg = aStatic;
void **aDyn = 0;
enterMutex(); /* Enter STATIC_MASTER mutex */
/* This loop runs once for each entry in the blocked-connections list. */
for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
sqlite3 *p = *pp;
/* Step 1. */
if( p->pBlockingConnection==db ){
p->pBlockingConnection = 0;
}
/* Step 2. */
if( p->pUnlockConnection==db ){
assert( p->xUnlockNotify );
if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
xUnlockNotify(aArg, nArg);
nArg = 0;
}
sqlite3BeginBenignMalloc();
assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
if( (!aDyn && nArg==(int)ArraySize(aStatic))
|| (aDyn && nArg==(int)(sqlite3DbMallocSize(db, aDyn)/sizeof(void*)))
){
/* The aArg[] array needs to grow. */
void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
if( pNew ){
memcpy(pNew, aArg, nArg*sizeof(void *));
sqlite3_free(aDyn);
aDyn = aArg = pNew;
}else{
/* This occurs when the array of context pointers that need to
** be passed to the unlock-notify callback is larger than the
** aStatic[] array allocated on the stack and the attempt to
** allocate a larger array from the heap has failed.
**
** This is a difficult situation to handle. Returning an error
** code to the caller is insufficient, as even if an error code
** is returned the transaction on connection db will still be
** closed and the unlock-notify callbacks on blocked connections
** will go unissued. This might cause the application to wait
** indefinitely for an unlock-notify callback that will never
** arrive.
**
** Instead, invoke the unlock-notify callback with the context
** array already accumulated. We can then clear the array and
** begin accumulating any further context pointers without
** requiring any dynamic allocation. This is sub-optimal because
** it means that instead of one callback with a large array of
** context pointers the application will receive two or more
** callbacks with smaller arrays of context pointers, which will
** reduce the applications ability to prioritize multiple
** connections. But it is the best that can be done under the
** circumstances.
*/
xUnlockNotify(aArg, nArg);
nArg = 0;
}
}
sqlite3EndBenignMalloc();
aArg[nArg++] = p->pUnlockArg;
xUnlockNotify = p->xUnlockNotify;
p->pUnlockConnection = 0;
p->xUnlockNotify = 0;
p->pUnlockArg = 0;
}
/* Step 3. */
if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
/* Remove connection p from the blocked connections list. */
*pp = p->pNextBlocked;
p->pNextBlocked = 0;
}else{
pp = &p->pNextBlocked;
}
}
if( nArg!=0 ){
xUnlockNotify(aArg, nArg);
}
sqlite3_free(aDyn);
leaveMutex(); /* Leave STATIC_MASTER mutex */
}
/*
** This is called when the database connection passed as an argument is
** being closed. The connection is removed from the blocked list.
*/
void sqlite3ConnectionClosed(sqlite3 *db){
sqlite3ConnectionUnlocked(db);
enterMutex();
removeFromBlockedList(db);
checkListProperties(db);
leaveMutex();
}
#endif

202
opcodes.h
View file

@ -16,7 +16,7 @@
#define OP_RowKey 14 #define OP_RowKey 14
#define OP_IsUnique 15 #define OP_IsUnique 15
#define OP_SetNumColumns 16 #define OP_SetNumColumns 16
#define OP_Eq 73 /* same as TK_EQ */ #define OP_Eq 74 /* same as TK_EQ */
#define OP_VUpdate 17 #define OP_VUpdate 17
#define OP_Expire 18 #define OP_Expire 18
#define OP_NullRow 20 #define OP_NullRow 20
@ -43,103 +43,103 @@
#define OP_IdxDelete 41 #define OP_IdxDelete 41
#define OP_Sort 42 #define OP_Sort 42
#define OP_ResetCount 43 #define OP_ResetCount 43
#define OP_NotNull 71 /* same as TK_NOTNULL */ #define OP_Count 44
#define OP_Ge 77 /* same as TK_GE */ #define OP_NotNull 72 /* same as TK_NOTNULL */
#define OP_Remainder 87 /* same as TK_REM */ #define OP_Ge 78 /* same as TK_GE */
#define OP_Divide 86 /* same as TK_SLASH */ #define OP_Remainder 88 /* same as TK_REM */
#define OP_Integer 44 #define OP_Divide 87 /* same as TK_SLASH */
#define OP_Explain 45 #define OP_Integer 45
#define OP_IncrVacuum 46 #define OP_Explain 46
#define OP_AggStep 47 #define OP_IncrVacuum 47
#define OP_CreateIndex 48 #define OP_AggStep 48
#define OP_NewRowid 49 #define OP_CreateIndex 49
#define OP_And 66 /* same as TK_AND */ #define OP_NewRowid 50
#define OP_ShiftLeft 81 /* same as TK_LSHIFT */ #define OP_And 67 /* same as TK_AND */
#define OP_ShiftLeft 82 /* same as TK_LSHIFT */
#define OP_Real 130 /* same as TK_FLOAT */ #define OP_Real 130 /* same as TK_FLOAT */
#define OP_Return 50 #define OP_Return 51
#define OP_Trace 51 #define OP_Trace 52
#define OP_IfPos 52 #define OP_IfPos 53
#define OP_IdxLT 53 #define OP_IdxLT 54
#define OP_Rewind 54 #define OP_Rewind 55
#define OP_SeekGe 55 #define OP_SeekGe 56
#define OP_Affinity 56 #define OP_Affinity 57
#define OP_Gt 74 /* same as TK_GT */ #define OP_Gt 75 /* same as TK_GT */
#define OP_AddImm 57 #define OP_AddImm 58
#define OP_Subtract 84 /* same as TK_MINUS */ #define OP_Subtract 85 /* same as TK_MINUS */
#define OP_Null 58 #define OP_Null 59
#define OP_VColumn 59 #define OP_VColumn 60
#define OP_Clear 60 #define OP_Clear 61
#define OP_IsNull 70 /* same as TK_ISNULL */ #define OP_IsNull 71 /* same as TK_ISNULL */
#define OP_If 61 #define OP_If 62
#define OP_Permutation 62 #define OP_Permutation 63
#define OP_ToBlob 142 /* same as TK_TO_BLOB */ #define OP_ToBlob 142 /* same as TK_TO_BLOB */
#define OP_RealAffinity 63 #define OP_RealAffinity 64
#define OP_Yield 64 #define OP_HaltIfNull 65
#define OP_AggFinal 67 #define OP_Yield 68
#define OP_IfZero 68 #define OP_AggFinal 69
#define OP_Last 69 #define OP_IfZero 70
#define OP_Rowid 78 #define OP_Last 79
#define OP_Sequence 89 #define OP_Rowid 90
#define OP_NotFound 90 #define OP_Sequence 91
#define OP_SeekGt 91 #define OP_NotFound 92
#define OP_MakeRecord 94 #define OP_SeekGt 95
#define OP_MakeRecord 96
#define OP_ToText 141 /* same as TK_TO_TEXT */ #define OP_ToText 141 /* same as TK_TO_TEXT */
#define OP_BitAnd 79 /* same as TK_BITAND */ #define OP_BitAnd 80 /* same as TK_BITAND */
#define OP_Add 83 /* same as TK_PLUS */ #define OP_Add 84 /* same as TK_PLUS */
#define OP_ResultRow 95 #define OP_ResultRow 97
#define OP_String 96 #define OP_String 98
#define OP_Goto 97 #define OP_Goto 99
#define OP_Noop 98 #define OP_Noop 100
#define OP_VCreate 99 #define OP_VCreate 101
#define OP_RowSetRead 100 #define OP_RowSetRead 102
#define OP_DropTable 101 #define OP_DropTable 103
#define OP_IdxRowid 102 #define OP_IdxRowid 104
#define OP_Insert 103 #define OP_Insert 105
#define OP_Column 104 #define OP_Column 106
#define OP_Not 19 /* same as TK_NOT */ #define OP_Not 19 /* same as TK_NOT */
#define OP_Compare 105 #define OP_Compare 107
#define OP_Le 75 /* same as TK_LE */ #define OP_Le 76 /* same as TK_LE */
#define OP_BitOr 80 /* same as TK_BITOR */ #define OP_BitOr 81 /* same as TK_BITOR */
#define OP_Multiply 85 /* same as TK_STAR */ #define OP_Multiply 86 /* same as TK_STAR */
#define OP_String8 93 /* same as TK_STRING */ #define OP_String8 94 /* same as TK_STRING */
#define OP_VOpen 106 #define OP_VOpen 108
#define OP_CreateTable 107 #define OP_CreateTable 109
#define OP_Found 108 #define OP_Found 110
#define OP_Seek 109 #define OP_Seek 111
#define OP_Close 110 #define OP_Close 112
#define OP_Savepoint 111 #define OP_Savepoint 113
#define OP_Statement 112 #define OP_Statement 114
#define OP_IfNot 113 #define OP_IfNot 115
#define OP_ToInt 144 /* same as TK_TO_INT */ #define OP_ToInt 144 /* same as TK_TO_INT */
#define OP_VBegin 114 #define OP_VBegin 116
#define OP_MemMax 115 #define OP_MemMax 117
#define OP_Next 116 #define OP_Next 118
#define OP_Prev 117 #define OP_Prev 119
#define OP_SeekLe 118 #define OP_SeekLe 120
#define OP_Lt 76 /* same as TK_LT */ #define OP_Lt 77 /* same as TK_LT */
#define OP_Ne 72 /* same as TK_NE */ #define OP_Ne 73 /* same as TK_NE */
#define OP_MustBeInt 119 #define OP_MustBeInt 121
#define OP_ShiftRight 82 /* same as TK_RSHIFT */ #define OP_ShiftRight 83 /* same as TK_RSHIFT */
#define OP_CollSeq 120 #define OP_CollSeq 122
#define OP_Gosub 121 #define OP_Gosub 123
#define OP_ContextPush 122 #define OP_ContextPush 124
#define OP_ParseSchema 123 #define OP_ParseSchema 125
#define OP_Destroy 124 #define OP_Destroy 126
#define OP_IdxGE 125 #define OP_IdxGE 127
#define OP_ReadCookie 126 #define OP_ReadCookie 128
#define OP_BitNot 92 /* same as TK_BITNOT */ #define OP_BitNot 93 /* same as TK_BITNOT */
#define OP_Or 65 /* same as TK_OR */ #define OP_Or 66 /* same as TK_OR */
#define OP_Jump 127 #define OP_Jump 129
#define OP_ToReal 145 /* same as TK_TO_REAL */ #define OP_ToReal 145 /* same as TK_TO_REAL */
#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ #define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/
#define OP_Function 128 #define OP_Function 131
#define OP_Concat 88 /* same as TK_CONCAT */ #define OP_Concat 89 /* same as TK_CONCAT */
#define OP_SCopy 129 #define OP_SCopy 132
#define OP_Int64 131 #define OP_Int64 133
/* The following opcode values are never used */ /* The following opcode values are never used */
#define OP_NotUsed_132 132
#define OP_NotUsed_133 133
#define OP_NotUsed_134 134 #define OP_NotUsed_134 134
#define OP_NotUsed_135 135 #define OP_NotUsed_135 135
#define OP_NotUsed_136 136 #define OP_NotUsed_136 136
@ -164,18 +164,18 @@
/* 8 */ 0x08, 0x00, 0x11, 0x00, 0x00, 0x02, 0x00, 0x11,\ /* 8 */ 0x08, 0x00, 0x11, 0x00, 0x00, 0x02, 0x00, 0x11,\
/* 16 */ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,\ /* 16 */ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,\
/* 24 */ 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x11,\ /* 24 */ 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x11,\
/* 32 */ 0x10, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x08,\ /* 32 */ 0x10, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x08,\
/* 40 */ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,\ /* 40 */ 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01,\
/* 48 */ 0x02, 0x02, 0x04, 0x00, 0x05, 0x11, 0x01, 0x11,\ /* 48 */ 0x00, 0x02, 0x02, 0x04, 0x00, 0x05, 0x11, 0x01,\
/* 56 */ 0x00, 0x04, 0x02, 0x00, 0x00, 0x05, 0x00, 0x04,\ /* 56 */ 0x11, 0x00, 0x04, 0x02, 0x00, 0x00, 0x05, 0x00,\
/* 64 */ 0x04, 0x2c, 0x2c, 0x00, 0x05, 0x01, 0x05, 0x05,\ /* 64 */ 0x04, 0x10, 0x2c, 0x2c, 0x04, 0x00, 0x05, 0x05,\
/* 72 */ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x02, 0x2c,\ /* 72 */ 0x05, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x01,\
/* 80 */ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\ /* 80 */ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\
/* 88 */ 0x2c, 0x02, 0x11, 0x11, 0x04, 0x02, 0x00, 0x00,\ /* 88 */ 0x2c, 0x2c, 0x02, 0x02, 0x11, 0x04, 0x02, 0x11,\
/* 96 */ 0x02, 0x01, 0x00, 0x00, 0x21, 0x00, 0x02, 0x00,\ /* 96 */ 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x21, 0x00,\
/* 104 */ 0x00, 0x00, 0x00, 0x02, 0x11, 0x08, 0x00, 0x00,\ /* 104 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x11, 0x08,\
/* 112 */ 0x00, 0x05, 0x00, 0x0c, 0x01, 0x01, 0x11, 0x05,\ /* 112 */ 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x01, 0x01,\
/* 120 */ 0x00, 0x01, 0x00, 0x00, 0x02, 0x11, 0x02, 0x01,\ /* 120 */ 0x11, 0x05, 0x00, 0x01, 0x00, 0x00, 0x02, 0x11,\
/* 128 */ 0x00, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,\ /* 128 */ 0x02, 0x01, 0x02, 0x00, 0x04, 0x02, 0x00, 0x00,\
/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\ /* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\
/* 144 */ 0x04, 0x04,} /* 144 */ 0x04, 0x04,}

7
os.c
View file

@ -13,7 +13,7 @@
** This file contains OS interface code that is common to all ** This file contains OS interface code that is common to all
** architectures. ** architectures.
** **
** $Id: os.c,v 1.125 2008/12/08 18:19:18 drh Exp $ ** $Id: os.c,v 1.126 2009/03/25 14:24:42 drh Exp $
*/ */
#define _SQLITE_OS_C_ 1 #define _SQLITE_OS_C_ 1
#include "sqliteInt.h" #include "sqliteInt.h"
@ -112,8 +112,11 @@ int sqlite3OsOpen(
int flags, int flags,
int *pFlagsOut int *pFlagsOut
){ ){
int rc;
DO_OS_MALLOC_TEST; DO_OS_MALLOC_TEST;
return pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut); rc = pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut);
assert( rc==SQLITE_OK || pFile->pMethods==0 );
return rc;
} }
int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
return pVfs->xDelete(pVfs, zPath, dirSync); return pVfs->xDelete(pVfs, zPath, dirSync);

View file

@ -17,7 +17,7 @@
** This file should be #included by the os_*.c files only. It is not a ** This file should be #included by the os_*.c files only. It is not a
** general purpose header file. ** general purpose header file.
** **
** $Id: os_common.h,v 1.37 2008/05/29 20:22:37 shane Exp $ ** $Id: os_common.h,v 1.38 2009/02/24 18:40:50 danielk1977 Exp $
*/ */
#ifndef _OS_COMMON_H_ #ifndef _OS_COMMON_H_
#define _OS_COMMON_H_ #define _OS_COMMON_H_
@ -31,15 +31,6 @@
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif #endif
/*
* When testing, this global variable stores the location of the
* pending-byte in the database file.
*/
#ifdef SQLITE_TEST
unsigned int sqlite3_pending_byte = 0x40000000;
#endif
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
int sqlite3OSTrace = 0; int sqlite3OSTrace = 0;
#define OSTRACE1(X) if( sqlite3OSTrace ) sqlite3DebugPrintf(X) #define OSTRACE1(X) if( sqlite3OSTrace ) sqlite3DebugPrintf(X)

View file

@ -43,7 +43,7 @@
** * Definitions of sqlite3_vfs objects for all locking methods ** * Definitions of sqlite3_vfs objects for all locking methods
** plus implementations of sqlite3_os_init() and sqlite3_os_end(). ** plus implementations of sqlite3_os_init() and sqlite3_os_end().
** **
** $Id: os_unix.c,v 1.241 2009/02/09 17:34:07 drh Exp $ ** $Id: os_unix.c,v 1.248 2009/03/30 07:39:35 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#if SQLITE_OS_UNIX /* This file is used on unix only */ #if SQLITE_OS_UNIX /* This file is used on unix only */
@ -1454,11 +1454,12 @@ static int unixUnlock(sqlite3_file *id, int locktype){
if( IS_LOCK_ERROR(rc) ){ if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno; pFile->lastErrno = tErrno;
} }
goto end_unlock; goto end_unlock;
} }
} }
if( locktype==NO_LOCK ){ if( locktype==NO_LOCK ){
struct unixOpenCnt *pOpen; struct unixOpenCnt *pOpen;
int rc2 = SQLITE_OK;
/* Decrement the shared lock counter. Release the lock using an /* Decrement the shared lock counter. Release the lock using an
** OS call only when all threads in this same process have released ** OS call only when all threads in this same process have released
@ -1480,8 +1481,8 @@ static int unixUnlock(sqlite3_file *id, int locktype){
if( IS_LOCK_ERROR(rc) ){ if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno; pFile->lastErrno = tErrno;
} }
pLock->cnt = 1; pLock->locktype = NO_LOCK;
goto end_unlock; pFile->locktype = NO_LOCK;
} }
} }
@ -1489,30 +1490,31 @@ static int unixUnlock(sqlite3_file *id, int locktype){
** count reaches zero, close any other file descriptors whose close ** count reaches zero, close any other file descriptors whose close
** was deferred because of outstanding locks. ** was deferred because of outstanding locks.
*/ */
if( rc==SQLITE_OK ){ pOpen = pFile->pOpen;
pOpen = pFile->pOpen; pOpen->nLock--;
pOpen->nLock--; assert( pOpen->nLock>=0 );
assert( pOpen->nLock>=0 ); if( pOpen->nLock==0 && pOpen->nPending>0 ){
if( pOpen->nLock==0 && pOpen->nPending>0 ){ int i;
int i; for(i=0; i<pOpen->nPending; i++){
for(i=0; i<pOpen->nPending; i++){ /* close pending fds, but if closing fails don't free the array
/* close pending fds, but if closing fails don't free the array ** assign -1 to the successfully closed descriptors and record the
** assign -1 to the successfully closed descriptors and record the ** error. The next attempt to unlock will try again. */
** error. The next attempt to unlock will try again. */ if( pOpen->aPending[i] < 0 ) continue;
if( pOpen->aPending[i] < 0 ) continue; if( close(pOpen->aPending[i]) ){
if( close(pOpen->aPending[i]) ){ pFile->lastErrno = errno;
pFile->lastErrno = errno; rc2 = SQLITE_IOERR_CLOSE;
rc = SQLITE_IOERR_CLOSE; }else{
}else{ pOpen->aPending[i] = -1;
pOpen->aPending[i] = -1;
}
}
if( rc==SQLITE_OK ){
sqlite3_free(pOpen->aPending);
pOpen->nPending = 0;
pOpen->aPending = 0;
} }
} }
if( rc2==SQLITE_OK ){
sqlite3_free(pOpen->aPending);
pOpen->nPending = 0;
pOpen->aPending = 0;
}
}
if( rc==SQLITE_OK ){
rc = rc2;
} }
} }
@ -2824,10 +2826,12 @@ int sqlite3_fullsync_count = 0;
#endif #endif
/* /*
** Use the fdatasync() API only if the HAVE_FDATASYNC macro is defined. ** We do not trust systems to provide a working fdatasync(). Some do.
** Otherwise use fsync() in its place. ** Others do no. To be safe, we will stick with the (slower) fsync().
** If you know that your system does support fdatasync() correctly,
** then simply compile with -Dfdatasync=fdatasync
*/ */
#ifndef HAVE_FDATASYNC #if !defined(fdatasync) && !defined(__linux__)
# define fdatasync fsync # define fdatasync fsync
#endif #endif
@ -2853,6 +2857,19 @@ int sqlite3_fullsync_count = 0;
** You are strongly advised *not* to deploy with SQLITE_NO_SYNC ** You are strongly advised *not* to deploy with SQLITE_NO_SYNC
** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash ** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash
** or power failure will likely corrupt the database file. ** or power failure will likely corrupt the database file.
**
** SQLite sets the dataOnly flag if the size of the file is unchanged.
** The idea behind dataOnly is that it should only write the file content
** to disk, not the inode. We only set dataOnly if the file size is
** unchanged since the file size is part of the inode. However,
** Ted Ts'o tells us that fdatasync() will also write the inode if the
** file size has changed. The only real difference between fdatasync()
** and fsync(), Ted tells us, is that fdatasync() will not flush the
** inode if the mtime or owner or other inode attributes have changed.
** We only care about the file size, not the other file attributes, so
** as far as SQLite is concerned, an fdatasync() is always adequate.
** So, we always use fdatasync() if it is available, regardless of
** the value of the dataOnly flag.
*/ */
static int full_fsync(int fd, int fullSync, int dataOnly){ static int full_fsync(int fd, int fullSync, int dataOnly){
int rc; int rc;
@ -2869,6 +2886,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
UNUSED_PARAMETER(dataOnly); UNUSED_PARAMETER(dataOnly);
#else #else
UNUSED_PARAMETER(fullSync); UNUSED_PARAMETER(fullSync);
UNUSED_PARAMETER(dataOnly);
#endif #endif
/* Record the number of times that we do a normal fsync() and /* Record the number of times that we do a normal fsync() and
@ -2902,16 +2920,12 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
if( rc ) rc = fsync(fd); if( rc ) rc = fsync(fd);
#else #else
if( dataOnly ){ rc = fdatasync(fd);
rc = fdatasync(fd);
#if OS_VXWORKS #if OS_VXWORKS
if( rc==-1 && errno==ENOTSUP ){ if( rc==-1 && errno==ENOTSUP ){
rc = fsync(fd);
}
#endif
}else{
rc = fsync(fd); rc = fsync(fd);
} }
#endif /* OS_VXWORKS */
#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */ #endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */
if( OS_VXWORKS && rc!= -1 ){ if( OS_VXWORKS && rc!= -1 ){
@ -3984,16 +3998,18 @@ static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
sp.tv_sec = microseconds / 1000000; sp.tv_sec = microseconds / 1000000;
sp.tv_nsec = (microseconds % 1000000) * 1000; sp.tv_nsec = (microseconds % 1000000) * 1000;
nanosleep(&sp, NULL); nanosleep(&sp, NULL);
UNUSED_PARAMETER(NotUsed);
return microseconds; return microseconds;
#elif defined(HAVE_USLEEP) && HAVE_USLEEP #elif defined(HAVE_USLEEP) && HAVE_USLEEP
usleep(microseconds); usleep(microseconds);
UNUSED_PARAMETER(NotUsed);
return microseconds; return microseconds;
#else #else
int seconds = (microseconds+999999)/1000000; int seconds = (microseconds+999999)/1000000;
sleep(seconds); sleep(seconds);
UNUSED_PARAMETER(NotUsed);
return seconds*1000000; return seconds*1000000;
#endif #endif
UNUSED_PARAMETER(NotUsed);
} }
/* /*

178
os_win.c
View file

@ -12,7 +12,7 @@
** **
** This file contains code that is specific to windows. ** This file contains code that is specific to windows.
** **
** $Id: os_win.c,v 1.148 2009/02/05 03:16:21 shane Exp $ ** $Id: os_win.c,v 1.153 2009/03/31 03:41:57 shane Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#if SQLITE_OS_WIN /* This file is used for windows only */ #if SQLITE_OS_WIN /* This file is used for windows only */
@ -75,6 +75,7 @@
*/ */
#if SQLITE_OS_WINCE #if SQLITE_OS_WINCE
# define AreFileApisANSI() 1 # define AreFileApisANSI() 1
# define GetDiskFreeSpaceW() 0
#endif #endif
/* /*
@ -101,6 +102,7 @@ struct winFile {
unsigned char locktype; /* Type of lock currently held on this file */ unsigned char locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */
DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD lastErrno; /* The Windows errno from the last I/O error */
DWORD sectorSize; /* Sector size of the device file is on */
#if SQLITE_OS_WINCE #if SQLITE_OS_WINCE
WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hMutex; /* Mutex used to control access to shared lock */
@ -110,6 +112,13 @@ struct winFile {
#endif #endif
}; };
/*
** Forward prototypes.
*/
static int getSectorSize(
sqlite3_vfs *pVfs,
const char *zRelative /* UTF-8 file name */
);
/* /*
** The following variable is (normally) set once and never changes ** The following variable is (normally) set once and never changes
@ -135,7 +144,7 @@ static int sqlite3_os_type = 0;
** **
** Here is an interesting observation: Win95, Win98, and WinME lack ** Here is an interesting observation: Win95, Win98, and WinME lack
** the LockFileEx() API. But we can still statically link against that ** the LockFileEx() API. But we can still statically link against that
** API as long as we don't call it win running Win95/98/ME. A call to ** API as long as we don't call it when running Win95/98/ME. A call to
** this routine is used to determine if the host is Win95/98/ME or ** this routine is used to determine if the host is Win95/98/ME or
** WinNT/2K/XP so that we will know whether or not we can safely call ** WinNT/2K/XP so that we will know whether or not we can safely call
** the LockFileEx() API. ** the LockFileEx() API.
@ -610,6 +619,8 @@ static BOOL winceLockFileEx(
static int winClose(sqlite3_file *id){ static int winClose(sqlite3_file *id){
int rc, cnt = 0; int rc, cnt = 0;
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
assert( id!=0 );
OSTRACE2("CLOSE %d\n", pFile->h); OSTRACE2("CLOSE %d\n", pFile->h);
do{ do{
rc = CloseHandle(pFile->h); rc = CloseHandle(pFile->h);
@ -654,9 +665,10 @@ static int winRead(
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff); LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
LONG lowerBits = (LONG)(offset & 0xffffffff); LONG lowerBits = (LONG)(offset & 0xffffffff);
DWORD rc; DWORD rc;
DWORD got;
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
DWORD error; DWORD error;
DWORD got;
assert( id!=0 ); assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_READ); SimulateIOError(return SQLITE_IOERR_READ);
OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype); OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype);
@ -691,9 +703,10 @@ static int winWrite(
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff); LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
LONG lowerBits = (LONG)(offset & 0xffffffff); LONG lowerBits = (LONG)(offset & 0xffffffff);
DWORD rc; DWORD rc;
DWORD wrote = 0;
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
DWORD error; DWORD error;
DWORD wrote = 0;
assert( id!=0 ); assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_WRITE); SimulateIOError(return SQLITE_IOERR_WRITE);
SimulateDiskfullError(return SQLITE_FULL); SimulateDiskfullError(return SQLITE_FULL);
@ -723,26 +736,26 @@ static int winWrite(
** Truncate an open file to a specified size ** Truncate an open file to a specified size
*/ */
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
DWORD rc;
LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff); LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff);
LONG lowerBits = (LONG)(nByte & 0xffffffff); LONG lowerBits = (LONG)(nByte & 0xffffffff);
DWORD rc;
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
DWORD error = NO_ERROR; DWORD error;
assert( id!=0 );
OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte); OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte);
SimulateIOError(return SQLITE_IOERR_TRUNCATE); SimulateIOError(return SQLITE_IOERR_TRUNCATE);
rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
if( INVALID_SET_FILE_POINTER == rc ){ if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
error = GetLastError(); pFile->lastErrno = error;
return SQLITE_IOERR_TRUNCATE;
} }
if( error == NO_ERROR ){ /* SetEndOfFile will fail if nByte is negative */
/* SetEndOfFile will fail if nByte is negative */ if( !SetEndOfFile(pFile->h) ){
if( SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError();
return SQLITE_OK; return SQLITE_IOERR_TRUNCATE;
}
error = GetLastError();
} }
pFile->lastErrno = error; return SQLITE_OK;
return SQLITE_IOERR_TRUNCATE;
} }
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
@ -760,6 +773,8 @@ int sqlite3_fullsync_count = 0;
static int winSync(sqlite3_file *id, int flags){ static int winSync(sqlite3_file *id, int flags){
#ifndef SQLITE_NO_SYNC #ifndef SQLITE_NO_SYNC
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
assert( id!=0 );
OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype); OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype);
#else #else
UNUSED_PARAMETER(id); UNUSED_PARAMETER(id);
@ -791,9 +806,12 @@ static int winSync(sqlite3_file *id, int flags){
** Determine the current size of a file in bytes ** Determine the current size of a file in bytes
*/ */
static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
DWORD upperBits;
DWORD lowerBits;
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
DWORD upperBits, lowerBits;
DWORD error; DWORD error;
assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_FSTAT); SimulateIOError(return SQLITE_IOERR_FSTAT);
lowerBits = GetFileSize(pFile->h, &upperBits); lowerBits = GetFileSize(pFile->h, &upperBits);
if( (lowerBits == INVALID_FILE_SIZE) if( (lowerBits == INVALID_FILE_SIZE)
@ -897,7 +915,7 @@ static int winLock(sqlite3_file *id, int locktype){
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
DWORD error = NO_ERROR; DWORD error = NO_ERROR;
assert( pFile!=0 ); assert( id!=0 );
OSTRACE5("LOCK %d %d was %d(%d)\n", OSTRACE5("LOCK %d %d was %d(%d)\n",
pFile->h, locktype, pFile->locktype, pFile->sharedLockByte); pFile->h, locktype, pFile->locktype, pFile->sharedLockByte);
@ -1015,7 +1033,8 @@ static int winLock(sqlite3_file *id, int locktype){
static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
int rc; int rc;
winFile *pFile = (winFile*)id; winFile *pFile = (winFile*)id;
assert( pFile!=0 );
assert( id!=0 );
if( pFile->locktype>=RESERVED_LOCK ){ if( pFile->locktype>=RESERVED_LOCK ){
rc = 1; rc = 1;
OSTRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc); OSTRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc);
@ -1100,8 +1119,8 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
** same for both. ** same for both.
*/ */
static int winSectorSize(sqlite3_file *id){ static int winSectorSize(sqlite3_file *id){
UNUSED_PARAMETER(id); assert( id!=0 );
return SQLITE_DEFAULT_SECTOR_SIZE; return (int)(((winFile*)id)->sectorSize);
} }
/* /*
@ -1245,7 +1264,6 @@ static int getLastErrorMsg(int nBuf, char *zBuf){
return 0; return 0;
} }
/* /*
** Open a file. ** Open a file.
*/ */
@ -1269,6 +1287,7 @@ static int winOpen(
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
assert( id!=0 );
UNUSED_PARAMETER(pVfs); UNUSED_PARAMETER(pVfs);
/* If the second argument to this function is NULL, generate a /* If the second argument to this function is NULL, generate a
@ -1348,7 +1367,7 @@ static int winOpen(
if( h==INVALID_HANDLE_VALUE ){ if( h==INVALID_HANDLE_VALUE ){
free(zConverted); free(zConverted);
if( flags & SQLITE_OPEN_READWRITE ){ if( flags & SQLITE_OPEN_READWRITE ){
return winOpen(0, zName, id, return winOpen(pVfs, zName, id,
((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
}else{ }else{
return SQLITE_CANTOPEN; return SQLITE_CANTOPEN;
@ -1365,6 +1384,7 @@ static int winOpen(
pFile->pMethod = &winIoMethod; pFile->pMethod = &winIoMethod;
pFile->h = h; pFile->h = h;
pFile->lastErrno = NO_ERROR; pFile->lastErrno = NO_ERROR;
pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
#if SQLITE_OS_WINCE #if SQLITE_OS_WINCE
if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) == if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
(SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB) (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
@ -1556,6 +1576,73 @@ static int winFullPathname(
#endif #endif
} }
/*
** Get the sector size of the device used to store
** file.
*/
static int getSectorSize(
sqlite3_vfs *pVfs,
const char *zRelative /* UTF-8 file name */
){
DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
char zFullpath[MAX_PATH+1];
int rc;
DWORD dwRet = 0;
/*
** We need to get the full path name of the file
** to get the drive letter to look up the sector
** size.
*/
rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
if( rc == SQLITE_OK )
{
void *zConverted = convertUtf8Filename(zFullpath);
if( zConverted ){
if( isNT() ){
int i;
/* trim path to just drive reference */
WCHAR *p = zConverted;
for(i=0;i<MAX_PATH;i++){
if( p[i] == '\\' ){
i++;
p[i] = '\0';
break;
}
}
dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
NULL,
&bytesPerSector,
NULL,
NULL);
#if SQLITE_OS_WINCE==0
}else{
int i;
/* trim path to just drive reference */
CHAR *p = (CHAR *)zConverted;
for(i=0;i<MAX_PATH;i++){
if( p[i] == '\\' ){
i++;
p[i] = '\0';
break;
}
}
dwRet = GetDiskFreeSpaceA((CHAR*)zConverted,
NULL,
&bytesPerSector,
NULL,
NULL);
#endif
}
free(zConverted);
}
if( !dwRet ){
bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
}
}
return (int) bytesPerSector;
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION #ifndef SQLITE_OMIT_LOAD_EXTENSION
/* /*
** Interfaces for opening a shared library, finding entry points ** Interfaces for opening a shared library, finding entry points
@ -1677,7 +1764,21 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
/* FILETIME structure is a 64-bit value representing the number of /* FILETIME structure is a 64-bit value representing the number of
100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
*/ */
sqlite3_int64 timeW, timeF; sqlite3_int64 timeW; /* Whole days */
sqlite3_int64 timeF; /* Fractional Days */
/* Number of 100-nanosecond intervals in a single day */
static const sqlite3_int64 ntuPerDay =
10000000*(sqlite3_int64)86400;
/* Number of 100-nanosecond intervals in half of a day */
static const sqlite3_int64 ntuPerHalfDay =
10000000*(sqlite3_int64)43200;
/* 2^32 - to avoid use of LL and warnings in gcc */
static const sqlite3_int64 max32BitValue =
(sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296;
#if SQLITE_OS_WINCE #if SQLITE_OS_WINCE
SYSTEMTIME time; SYSTEMTIME time;
GetSystemTime(&time); GetSystemTime(&time);
@ -1689,25 +1790,14 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
GetSystemTimeAsFileTime( &ft ); GetSystemTimeAsFileTime( &ft );
#endif #endif
UNUSED_PARAMETER(pVfs); UNUSED_PARAMETER(pVfs);
#if defined(_MSC_VER) timeW = (((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime;
timeW = (((sqlite3_int64)ft.dwHighDateTime)*4294967296) + ft.dwLowDateTime; timeF = timeW % ntuPerDay; /* fractional days (100-nanoseconds) */
timeF = timeW % 864000000000; /* fractional days (100-nanoseconds) */ timeW = timeW / ntuPerDay; /* whole days */
timeW = timeW / 864000000000; /* whole days */ timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */ timeF = timeF + ntuPerHalfDay; /* add half a day (from 2305813.5) */
timeF = timeF + 432000000000; /* add half a day (from 2305813.5) */ timeW = timeW + (timeF/ntuPerDay); /* add whole day if half day made one */
timeW = timeW + (timeF / 864000000000); /* add whole day if half day made one */ timeF = timeF % ntuPerDay; /* compute new fractional days */
timeF = timeF % 864000000000; /* compute new fractional days */ *prNow = (double)timeW + ((double)timeF / (double)ntuPerDay);
*prNow = (double)timeW + ((double)timeF / (double)864000000000);
#else
timeW = (((sqlite3_int64)ft.dwHighDateTime)*4294967296LL) + ft.dwLowDateTime;
timeF = timeW % 864000000000LL; /* fractional days (100-nanoseconds) */
timeW = timeW / 864000000000LL; /* whole days */
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
timeF = timeF + 432000000000LL; /* add half a day (from 2305813.5) */
timeW = timeW + (timeF / 864000000000LL); /* add whole day if half day made one */
timeF = timeF % 864000000000LL; /* compute new fractional days */
*prNow = (double)timeW + ((double)timeF / (double)864000000000LL);
#endif
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
if( sqlite3_current_time ){ if( sqlite3_current_time ){
*prNow = ((double)sqlite3_current_time + (double)43200) / (double)86400 + (double)2440587; *prNow = ((double)sqlite3_current_time + (double)43200) / (double)86400 + (double)2440587;
@ -1723,7 +1813,7 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
** function, SQLite calls this function with zBuf pointing to ** function, SQLite calls this function with zBuf pointing to
** a buffer of nBuf bytes. The OS layer should populate the ** a buffer of nBuf bytes. The OS layer should populate the
** buffer with a nul-terminated UTF-8 encoded error message ** buffer with a nul-terminated UTF-8 encoded error message
** describing the last IO error to have occured within the calling ** describing the last IO error to have occurred within the calling
** thread. ** thread.
** **
** If the error message is too large for the supplied buffer, ** If the error message is too large for the supplied buffer,

101
pager.c
View file

@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while ** file simultaneously, or one process from reading the database while
** another is writing. ** another is writing.
** **
** @(#) $Id: pager.c,v 1.570 2009/02/17 17:56:30 danielk1977 Exp $ ** @(#) $Id: pager.c,v 1.576 2009/03/31 02:54:40 drh Exp $
*/ */
#ifndef SQLITE_OMIT_DISKIO #ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h" #include "sqliteInt.h"
@ -99,12 +99,6 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */ #define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
#define PAGER_SYNCED 5 #define PAGER_SYNCED 5
/*
** This macro rounds values up so that if the value is an address it
** is guaranteed to be an address that is aligned to an 8-byte boundary.
*/
#define FORCE_ALIGNMENT(X) (((X)+7)&~7)
/* /*
** A macro used for invoking the codec if there is one ** A macro used for invoking the codec if there is one
*/ */
@ -762,7 +756,7 @@ static int writeJournalHdr(Pager *pPager){
** A faster alternative is to write 0xFFFFFFFF to the nRec field. When ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When
** reading the journal this value tells SQLite to assume that the ** reading the journal this value tells SQLite to assume that the
** rest of the journal file contains valid page records. This assumption ** rest of the journal file contains valid page records. This assumption
** is dangerous, as if a failure occured whilst writing to the journal ** is dangerous, as if a failure occurred whilst writing to the journal
** file it may contain some garbage data. There are two scenarios ** file it may contain some garbage data. There are two scenarios
** where this risk can be ignored: ** where this risk can be ignored:
** **
@ -1143,7 +1137,7 @@ static void pager_unlock(Pager *pPager){
/* /*
** This function should be called when an IOERR, CORRUPT or FULL error ** This function should be called when an IOERR, CORRUPT or FULL error
** may have occured. The first argument is a pointer to the pager ** may have occurred. The first argument is a pointer to the pager
** structure, the second the error-code about to be returned by a pager ** structure, the second the error-code about to be returned by a pager
** API function. The value returned is a copy of the second argument ** API function. The value returned is a copy of the second argument
** to this function. ** to this function.
@ -1156,7 +1150,7 @@ static void pager_unlock(Pager *pPager){
** A persistent error indicates that the contents of the pager-cache ** A persistent error indicates that the contents of the pager-cache
** cannot be trusted. This state can be cleared by completely discarding ** cannot be trusted. This state can be cleared by completely discarding
** the contents of the pager-cache. If a transaction was active when ** the contents of the pager-cache. If a transaction was active when
** the persistent error occured, then the rollback journal may need ** the persistent error occurred, then the rollback journal may need
** to be replayed to restore the contents of the database file (as if ** to be replayed to restore the contents of the database file (as if
** it were a hot-journal). ** it were a hot-journal).
*/ */
@ -1505,6 +1499,7 @@ static int pager_playback_one_page(
** Do not attempt to write if database file has never been opened. ** Do not attempt to write if database file has never been opened.
*/ */
pPg = pager_lookup(pPager, pgno); pPg = pager_lookup(pPager, pgno);
assert( pPg || !MEMDB );
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData), PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData),
(isMainJrnl?"main-journal":"sub-journal") (isMainJrnl?"main-journal":"sub-journal")
@ -2054,7 +2049,7 @@ end_playback:
); );
/* If this playback is happening automatically as a result of an IO or /* If this playback is happening automatically as a result of an IO or
** malloc error that occured after the change-counter was updated but ** malloc error that occurred after the change-counter was updated but
** before the transaction was committed, then the change-counter ** before the transaction was committed, then the change-counter
** modification may just have been reverted. If this happens in exclusive ** modification may just have been reverted. If this happens in exclusive
** mode, then subsequent transactions performed by the connection will not ** mode, then subsequent transactions performed by the connection will not
@ -3268,7 +3263,7 @@ int sqlite3PagerOpen(
testcase( rc!=SQLITE_OK ); testcase( rc!=SQLITE_OK );
} }
/* If an error occured in either of the blocks above, free the /* If an error occurred in either of the blocks above, free the
** Pager structure and close the file. ** Pager structure and close the file.
*/ */
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@ -3279,7 +3274,7 @@ int sqlite3PagerOpen(
} }
/* Initialize the PCache object. */ /* Initialize the PCache object. */
nExtra = FORCE_ALIGNMENT(nExtra); nExtra = ROUND8(nExtra);
sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
!memDb?pagerStress:0, (void *)pPager, pPager->pPCache); !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
@ -3335,11 +3330,12 @@ int sqlite3PagerOpen(
** PAGER_SHARED state. It tests if there is a hot journal present in ** PAGER_SHARED state. It tests if there is a hot journal present in
** the file-system for the given pager. A hot journal is one that ** the file-system for the given pager. A hot journal is one that
** needs to be played back. According to this function, a hot-journal ** needs to be played back. According to this function, a hot-journal
** file exists if the following three criteria are met: ** file exists if the following criteria are met:
** **
** * The journal file exists in the file system, and ** * The journal file exists in the file system, and
** * No process holds a RESERVED or greater lock on the database file, and ** * No process holds a RESERVED or greater lock on the database file, and
** * The database file itself is greater than 0 bytes in size. ** * The database file itself is greater than 0 bytes in size, and
** * The first byte of the journal file exists and is not 0x00.
** **
** If the current size of the database file is 0 but a journal file ** If the current size of the database file is 0 but a journal file
** exists, that is probably an old journal left over from a prior ** exists, that is probably an old journal left over from a prior
@ -3347,14 +3343,12 @@ int sqlite3PagerOpen(
** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK ** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK
** is returned. ** is returned.
** **
** This routine does not open the journal file to examine its ** This routine does not check if there is a master journal filename
** content. Hence, the journal might contain the name of a master ** at the end of the file. If there is, and that master journal file
** journal file that has been deleted, and hence not be hot. Or ** does not exist, then the journal file is not really hot. In this
** the header of the journal might be zeroed out. This routine ** case this routine will return a false-positive. The pager_playback()
** does not discover these cases of a non-hot journal - if the ** routine will discover that the journal file is not really hot and
** journal file exists and is not empty this routine assumes it ** will not roll it back.
** is hot. The pager_playback() routine will discover that the
** journal file is not really hot and will no-op.
** **
** If a hot-journal file is found to exist, *pExists is set to 1 and ** If a hot-journal file is found to exist, *pExists is set to 1 and
** SQLITE_OK returned. If no hot-journal file is present, *pExists is ** SQLITE_OK returned. If no hot-journal file is present, *pExists is
@ -3365,29 +3359,52 @@ int sqlite3PagerOpen(
static int hasHotJournal(Pager *pPager, int *pExists){ static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3_vfs * const pVfs = pPager->pVfs; sqlite3_vfs * const pVfs = pPager->pVfs;
int rc; /* Return code */ int rc; /* Return code */
int exists = 0; /* True if a journal file is present */ int exists; /* True if a journal file is present */
int locked = 0; /* True if some process holds a RESERVED lock */
assert( pPager!=0 ); assert( pPager!=0 );
assert( pPager->useJournal ); assert( pPager->useJournal );
assert( isOpen(pPager->fd) ); assert( isOpen(pPager->fd) );
assert( !isOpen(pPager->jfd) );
*pExists = 0; *pExists = 0;
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
if( rc==SQLITE_OK && exists ){ if( rc==SQLITE_OK && exists ){
int locked; /* True if some process holds a RESERVED lock */
rc = sqlite3OsCheckReservedLock(pPager->fd, &locked); rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
if( rc==SQLITE_OK && !locked ){ if( rc==SQLITE_OK && !locked ){
int nPage; int nPage;
/* Check the size of the database file. If it consists of 0 pages,
** then delete the journal file. See the header comment above for
** the reasoning here.
*/
rc = sqlite3PagerPagecount(pPager, &nPage); rc = sqlite3PagerPagecount(pPager, &nPage);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( nPage==0 ){ if( nPage==0 ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0); rc = sqlite3OsDelete(pVfs, pPager->zJournal, 0);
}else{ }else{
*pExists = 1; /* The journal file exists and no other connection has a reserved
** or greater lock on the database file. Now check that there is
** at least one non-zero bytes at the start of the journal file.
** If there is, then we consider this journal to be hot. If not,
** it can be ignored.
*/
int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
if( rc==SQLITE_OK ){
u8 first = 0;
rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
}
sqlite3OsClose(pPager->jfd);
*pExists = (first!=0);
}
} }
} }
} }
} }
return rc; return rc;
} }
@ -3413,10 +3430,13 @@ static int readDbPage(PgHdr *pPg){
if( !isOpen(pPager->fd) ){ if( !isOpen(pPager->fd) ){
assert( pPager->tempFile ); assert( pPager->tempFile );
memset(pPg->pData, 0, pPager->pageSize); memset(pPg->pData, 0, pPager->pageSize);
return SQLITE_IOERR_SHORT_READ; return SQLITE_OK;
} }
iOffset = (pgno-1)*(i64)pPager->pageSize; iOffset = (pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset); rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
}
if( pgno==1 ){ if( pgno==1 ){
u8 *dbFileVers = &((u8*)pPg->pData)[24]; u8 *dbFileVers = &((u8*)pPg->pData)[24];
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
@ -3800,7 +3820,7 @@ int sqlite3PagerAcquire(
}else{ }else{
assert( pPg->pPager==pPager ); assert( pPg->pPager==pPager );
rc = readDbPage(pPg); rc = readDbPage(pPg);
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ if( rc!=SQLITE_OK ){
pagerDropPage(pPg); pagerDropPage(pPg);
return rc; return rc;
} }
@ -3933,7 +3953,7 @@ static int pager_open_journal(Pager *pPager){
sqlite3MemJournalOpen(pPager->jfd); sqlite3MemJournalOpen(pPager->jfd);
}else{ }else{
const int flags = /* VFS flags to open journal file */ const int flags = /* VFS flags to open journal file */
SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_CREATE| SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
(pPager->tempFile ? (pPager->tempFile ?
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
(SQLITE_OPEN_MAIN_JOURNAL) (SQLITE_OPEN_MAIN_JOURNAL)
@ -4135,7 +4155,7 @@ static int pager_write(PgHdr *pPg){
pPager->needSync = 1; pPager->needSync = 1;
} }
/* An error has occured writing to the journal file. The /* An error has occurred writing to the journal file. The
** transaction will be rolled back by the layer above. ** transaction will be rolled back by the layer above.
*/ */
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@ -4851,7 +4871,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE. ** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE.
** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with ** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with
** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes ** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes
** that have occured since the specified savepoint was created. ** that have occurred since the specified savepoint was created.
** **
** The savepoint to rollback or release is identified by parameter ** The savepoint to rollback or release is identified by parameter
** iSavepoint. A value of 0 means to operate on the outermost savepoint ** iSavepoint. A value of 0 means to operate on the outermost savepoint
@ -4997,6 +5017,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
PgHdr *pPgOld; /* The page being overwritten. */ PgHdr *pPgOld; /* The page being overwritten. */
Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
int rc; /* Return code */ int rc; /* Return code */
Pgno origPgno; /* The original page number */
assert( pPg->nRef>0 ); assert( pPg->nRef>0 );
@ -5055,6 +5076,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
} }
origPgno = pPg->pgno;
sqlite3PcacheMove(pPg, pgno); sqlite3PcacheMove(pPg, pgno);
if( pPgOld ){ if( pPgOld ){
sqlite3PcacheDrop(pPgOld); sqlite3PcacheDrop(pPgOld);
@ -5097,6 +5119,19 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
sqlite3PagerUnref(pPgHdr); sqlite3PagerUnref(pPgHdr);
} }
/*
** For an in-memory database, make sure the original page continues
** to exist, in case the transaction needs to roll back. We allocate
** the page now, instead of at rollback, because we can better deal
** with an out-of-memory error now. Ticket #3761.
*/
if( MEMDB ){
DbPage *pNew;
rc = sqlite3PagerAcquire(pPager, origPgno, &pNew, 1);
if( rc!=SQLITE_OK ) return rc;
sqlite3PagerUnref(pNew);
}
return SQLITE_OK; return SQLITE_OK;
} }
#endif #endif

4089
parse.c

File diff suppressed because it is too large Load diff

192
parse.h
View file

@ -13,8 +13,8 @@
#define TK_SAVEPOINT 13 #define TK_SAVEPOINT 13
#define TK_RELEASE 14 #define TK_RELEASE 14
#define TK_TO 15 #define TK_TO 15
#define TK_CREATE 16 #define TK_TABLE 16
#define TK_TABLE 17 #define TK_CREATE 17
#define TK_IF 18 #define TK_IF 18
#define TK_NOT 19 #define TK_NOT 19
#define TK_EXISTS 20 #define TK_EXISTS 20
@ -24,100 +24,100 @@
#define TK_AS 24 #define TK_AS 24
#define TK_COMMA 25 #define TK_COMMA 25
#define TK_ID 26 #define TK_ID 26
#define TK_ABORT 27 #define TK_INDEXED 27
#define TK_AFTER 28 #define TK_ABORT 28
#define TK_ANALYZE 29 #define TK_AFTER 29
#define TK_ASC 30 #define TK_ANALYZE 30
#define TK_ATTACH 31 #define TK_ASC 31
#define TK_BEFORE 32 #define TK_ATTACH 32
#define TK_BY 33 #define TK_BEFORE 33
#define TK_CASCADE 34 #define TK_BY 34
#define TK_CAST 35 #define TK_CASCADE 35
#define TK_COLUMNKW 36 #define TK_CAST 36
#define TK_CONFLICT 37 #define TK_COLUMNKW 37
#define TK_DATABASE 38 #define TK_CONFLICT 38
#define TK_DESC 39 #define TK_DATABASE 39
#define TK_DETACH 40 #define TK_DESC 40
#define TK_EACH 41 #define TK_DETACH 41
#define TK_FAIL 42 #define TK_EACH 42
#define TK_FOR 43 #define TK_FAIL 43
#define TK_IGNORE 44 #define TK_FOR 44
#define TK_INITIALLY 45 #define TK_IGNORE 45
#define TK_INSTEAD 46 #define TK_INITIALLY 46
#define TK_LIKE_KW 47 #define TK_INSTEAD 47
#define TK_MATCH 48 #define TK_LIKE_KW 48
#define TK_KEY 49 #define TK_MATCH 49
#define TK_OF 50 #define TK_KEY 50
#define TK_OFFSET 51 #define TK_OF 51
#define TK_PRAGMA 52 #define TK_OFFSET 52
#define TK_RAISE 53 #define TK_PRAGMA 53
#define TK_REPLACE 54 #define TK_RAISE 54
#define TK_RESTRICT 55 #define TK_REPLACE 55
#define TK_ROW 56 #define TK_RESTRICT 56
#define TK_TRIGGER 57 #define TK_ROW 57
#define TK_VACUUM 58 #define TK_TRIGGER 58
#define TK_VIEW 59 #define TK_VACUUM 59
#define TK_VIRTUAL 60 #define TK_VIEW 60
#define TK_REINDEX 61 #define TK_VIRTUAL 61
#define TK_RENAME 62 #define TK_REINDEX 62
#define TK_CTIME_KW 63 #define TK_RENAME 63
#define TK_ANY 64 #define TK_CTIME_KW 64
#define TK_OR 65 #define TK_ANY 65
#define TK_AND 66 #define TK_OR 66
#define TK_IS 67 #define TK_AND 67
#define TK_BETWEEN 68 #define TK_IS 68
#define TK_IN 69 #define TK_BETWEEN 69
#define TK_ISNULL 70 #define TK_IN 70
#define TK_NOTNULL 71 #define TK_ISNULL 71
#define TK_NE 72 #define TK_NOTNULL 72
#define TK_EQ 73 #define TK_NE 73
#define TK_GT 74 #define TK_EQ 74
#define TK_LE 75 #define TK_GT 75
#define TK_LT 76 #define TK_LE 76
#define TK_GE 77 #define TK_LT 77
#define TK_ESCAPE 78 #define TK_GE 78
#define TK_BITAND 79 #define TK_ESCAPE 79
#define TK_BITOR 80 #define TK_BITAND 80
#define TK_LSHIFT 81 #define TK_BITOR 81
#define TK_RSHIFT 82 #define TK_LSHIFT 82
#define TK_PLUS 83 #define TK_RSHIFT 83
#define TK_MINUS 84 #define TK_PLUS 84
#define TK_STAR 85 #define TK_MINUS 85
#define TK_SLASH 86 #define TK_STAR 86
#define TK_REM 87 #define TK_SLASH 87
#define TK_CONCAT 88 #define TK_REM 88
#define TK_COLLATE 89 #define TK_CONCAT 89
#define TK_UMINUS 90 #define TK_COLLATE 90
#define TK_UPLUS 91 #define TK_UMINUS 91
#define TK_BITNOT 92 #define TK_UPLUS 92
#define TK_STRING 93 #define TK_BITNOT 93
#define TK_JOIN_KW 94 #define TK_STRING 94
#define TK_CONSTRAINT 95 #define TK_JOIN_KW 95
#define TK_DEFAULT 96 #define TK_CONSTRAINT 96
#define TK_NULL 97 #define TK_DEFAULT 97
#define TK_PRIMARY 98 #define TK_NULL 98
#define TK_UNIQUE 99 #define TK_PRIMARY 99
#define TK_CHECK 100 #define TK_UNIQUE 100
#define TK_REFERENCES 101 #define TK_CHECK 101
#define TK_AUTOINCR 102 #define TK_REFERENCES 102
#define TK_ON 103 #define TK_AUTOINCR 103
#define TK_DELETE 104 #define TK_ON 104
#define TK_UPDATE 105 #define TK_DELETE 105
#define TK_INSERT 106 #define TK_UPDATE 106
#define TK_SET 107 #define TK_INSERT 107
#define TK_DEFERRABLE 108 #define TK_SET 108
#define TK_FOREIGN 109 #define TK_DEFERRABLE 109
#define TK_DROP 110 #define TK_FOREIGN 110
#define TK_UNION 111 #define TK_DROP 111
#define TK_ALL 112 #define TK_UNION 112
#define TK_EXCEPT 113 #define TK_ALL 113
#define TK_INTERSECT 114 #define TK_EXCEPT 114
#define TK_SELECT 115 #define TK_INTERSECT 115
#define TK_DISTINCT 116 #define TK_SELECT 116
#define TK_DOT 117 #define TK_DISTINCT 117
#define TK_FROM 118 #define TK_DOT 118
#define TK_JOIN 119 #define TK_FROM 119
#define TK_INDEXED 120 #define TK_JOIN 120
#define TK_USING 121 #define TK_USING 121
#define TK_ORDER 122 #define TK_ORDER 122
#define TK_GROUP 123 #define TK_GROUP 123

View file

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file implements that page cache. ** This file implements that page cache.
** **
** @(#) $Id: pcache.c,v 1.43 2009/01/23 16:45:01 danielk1977 Exp $ ** @(#) $Id: pcache.c,v 1.44 2009/03/31 01:32:18 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -336,11 +336,9 @@ void sqlite3PcacheDrop(PgHdr *p){
** make it so. ** make it so.
*/ */
void sqlite3PcacheMakeDirty(PgHdr *p){ void sqlite3PcacheMakeDirty(PgHdr *p){
PCache *pCache;
p->flags &= ~PGHDR_DONT_WRITE; p->flags &= ~PGHDR_DONT_WRITE;
assert( p->nRef>0 ); assert( p->nRef>0 );
if( 0==(p->flags & PGHDR_DIRTY) ){ if( 0==(p->flags & PGHDR_DIRTY) ){
pCache = p->pCache;
p->flags |= PGHDR_DIRTY; p->flags |= PGHDR_DIRTY;
pcacheAddToDirtyList( p); pcacheAddToDirtyList( p);
} }

View file

@ -16,7 +16,7 @@
** If the default page cache implementation is overriden, then neither of ** If the default page cache implementation is overriden, then neither of
** these two features are available. ** these two features are available.
** **
** @(#) $Id: pcache1.c,v 1.8 2009/01/23 16:45:01 danielk1977 Exp $ ** @(#) $Id: pcache1.c,v 1.10 2009/03/23 04:33:33 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -129,7 +129,7 @@ static SQLITE_WSD struct PCacheGlobal {
*/ */
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
PgFreeslot *p; PgFreeslot *p;
sz &= ~7; sz = ROUNDDOWN8(sz);
pcache1.szSlot = sz; pcache1.szSlot = sz;
pcache1.pStart = pBuf; pcache1.pStart = pBuf;
pcache1.pFree = 0; pcache1.pFree = 0;
@ -516,7 +516,7 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
nPinned = pCache->nPage - pCache->nRecyclable; nPinned = pCache->nPage - pCache->nRecyclable;
if( createFlag==1 && pCache->bPurgeable && ( if( createFlag==1 && pCache->bPurgeable && (
nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage)
|| nPinned>=(pCache->nMax) || nPinned>=(pCache->nMax * 9 / 10)
)){ )){
goto fetch_out; goto fetch_out;
} }

View file

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file contains code used to implement the PRAGMA command. ** This file contains code used to implement the PRAGMA command.
** **
** $Id: pragma.c,v 1.202 2009/01/20 16:53:41 danielk1977 Exp $ ** $Id: pragma.c,v 1.204 2009/02/23 16:52:08 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -172,6 +172,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
{ "empty_result_callbacks", SQLITE_NullCallback }, { "empty_result_callbacks", SQLITE_NullCallback },
{ "legacy_file_format", SQLITE_LegacyFileFmt }, { "legacy_file_format", SQLITE_LegacyFileFmt },
{ "fullfsync", SQLITE_FullFSync }, { "fullfsync", SQLITE_FullFSync },
{ "reverse_unordered_selects", SQLITE_ReverseOrder },
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
{ "sql_trace", SQLITE_SqlTrace }, { "sql_trace", SQLITE_SqlTrace },
{ "vdbe_listing", SQLITE_VdbeListing }, { "vdbe_listing", SQLITE_VdbeListing },
@ -831,7 +832,6 @@ void sqlite3Pragma(
sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC); sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC);
sqlite3ViewGetColumnNames(pParse, pTab); sqlite3ViewGetColumnNames(pParse, pTab);
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
const Token *pDflt;
if( IsHiddenColumn(pCol) ){ if( IsHiddenColumn(pCol) ){
nHidden++; nHidden++;
continue; continue;
@ -842,9 +842,9 @@ void sqlite3Pragma(
pCol->zType ? pCol->zType : "", 0); pCol->zType ? pCol->zType : "", 0);
sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4); sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4);
if( pCol->pDflt ){ if( pCol->pDflt ){
pDflt = &pCol->pDflt->span; const Token *p = &pCol->pDflt->span;
assert( pDflt->z ); assert( p->z );
sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pDflt->z, pDflt->n); sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)p->z, p->n);
}else{ }else{
sqlite3VdbeAddOp2(v, OP_Null, 0, 5); sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
} }

View file

@ -13,7 +13,7 @@
** interface, and routines that contribute to loading the database schema ** interface, and routines that contribute to loading the database schema
** from disk. ** from disk.
** **
** $Id: prepare.c,v 1.105 2009/01/20 16:53:41 danielk1977 Exp $ ** $Id: prepare.c,v 1.114 2009/03/24 15:08:10 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -77,21 +77,17 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
*/ */
char *zErr; char *zErr;
int rc; int rc;
u8 lookasideEnabled;
assert( db->init.busy ); assert( db->init.busy );
db->init.iDb = iDb; db->init.iDb = iDb;
db->init.newTnum = atoi(argv[1]); db->init.newTnum = atoi(argv[1]);
lookasideEnabled = db->lookaside.bEnabled;
db->lookaside.bEnabled = 0;
rc = sqlite3_exec(db, argv[2], 0, 0, &zErr); rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
db->init.iDb = 0; db->init.iDb = 0;
db->lookaside.bEnabled = lookasideEnabled;
assert( rc!=SQLITE_OK || zErr==0 ); assert( rc!=SQLITE_OK || zErr==0 );
if( SQLITE_OK!=rc ){ if( SQLITE_OK!=rc ){
pData->rc = rc; pData->rc = rc;
if( rc==SQLITE_NOMEM ){ if( rc==SQLITE_NOMEM ){
db->mallocFailed = 1; db->mallocFailed = 1;
}else if( rc!=SQLITE_INTERRUPT ){ }else if( rc!=SQLITE_INTERRUPT && (rc&0xff)!=SQLITE_LOCKED ){
corruptSchema(pData, argv[0], zErr); corruptSchema(pData, argv[0], zErr);
} }
sqlite3DbFree(db, zErr); sqlite3DbFree(db, zErr);
@ -351,10 +347,10 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
} }
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
/* Black magic: If the SQLITE_RecoveryMode flag is set, then consider /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider
** the schema loaded, even if errors occured. In this situation the ** the schema loaded, even if errors occurred. In this situation the
** current sqlite3_prepare() operation will fail, but the following one ** current sqlite3_prepare() operation will fail, but the following one
** will attempt to compile the supplied statement against whatever subset ** will attempt to compile the supplied statement against whatever subset
** of the schema was loaded before the error occured. The primary ** of the schema was loaded before the error occurred. The primary
** purpose of this is to allow access to the sqlite_master table ** purpose of this is to allow access to the sqlite_master table
** even when its contents have been corrupted. ** even when its contents have been corrupted.
*/ */
@ -540,18 +536,40 @@ static int sqlite3Prepare(
assert( !db->mallocFailed ); assert( !db->mallocFailed );
assert( sqlite3_mutex_held(db->mutex) ); assert( sqlite3_mutex_held(db->mutex) );
/* If any attached database schemas are locked, do not proceed with /* Check to verify that it is possible to get a read lock on all
** compilation. Instead return SQLITE_LOCKED immediately. ** database schemas. The inability to get a read lock indicates that
** some other database connection is holding a write-lock, which in
** turn means that the other connection has made uncommitted changes
** to the schema.
**
** Were we to proceed and prepare the statement against the uncommitted
** schema changes and if those schema changes are subsequently rolled
** back and different changes are made in their place, then when this
** prepared statement goes to run the schema cookie would fail to detect
** the schema change. Disaster would follow.
**
** This thread is currently holding mutexes on all Btrees (because
** of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it
** is not possible for another thread to start a new schema change
** while this routine is running. Hence, we do not need to hold
** locks on the schema, we just need to make sure nobody else is
** holding them.
**
** Note that setting READ_UNCOMMITTED overrides most lock detection,
** but it does *not* override schema lock detection, so this all still
** works even if READ_UNCOMMITTED is set.
*/ */
for(i=0; i<db->nDb; i++) { for(i=0; i<db->nDb; i++) {
Btree *pBt = db->aDb[i].pBt; Btree *pBt = db->aDb[i].pBt;
if( pBt ){ if( pBt ){
assert( sqlite3BtreeHoldsMutex(pBt) );
rc = sqlite3BtreeSchemaLocked(pBt); rc = sqlite3BtreeSchemaLocked(pBt);
if( rc ){ if( rc ){
const char *zDb = db->aDb[i].zName; const char *zDb = db->aDb[i].zName;
sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb); sqlite3Error(db, rc, "database schema is locked: %s", zDb);
(void)sqlite3SafetyOff(db); (void)sqlite3SafetyOff(db);
return sqlite3ApiExit(db, SQLITE_LOCKED); testcase( db->flags & SQLITE_ReadUncommitted );
return sqlite3ApiExit(db, rc);
} }
} }
} }
@ -621,11 +639,13 @@ static int sqlite3Prepare(
rc = SQLITE_MISUSE; rc = SQLITE_MISUSE;
} }
if( saveSqlFlag ){ assert( db->init.busy==0 || saveSqlFlag==0 );
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail - zSql)); if( db->init.busy==0 ){
Vdbe *pVdbe = sParse.pVdbe;
sqlite3VdbeSetSql(pVdbe, zSql, (int)(sParse.zTail-zSql), saveSqlFlag);
} }
if( rc!=SQLITE_OK || db->mallocFailed ){ if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe); sqlite3VdbeFinalize(sParse.pVdbe);
assert(!(*ppStmt)); assert(!(*ppStmt));
}else{ }else{
*ppStmt = (sqlite3_stmt*)sParse.pVdbe; *ppStmt = (sqlite3_stmt*)sParse.pVdbe;
@ -664,8 +684,11 @@ static int sqlite3LockAndPrepare(
/* /*
** Rerun the compilation of a statement after a schema change. ** Rerun the compilation of a statement after a schema change.
** Return true if the statement was recompiled successfully. **
** Return false if there is an error of some kind. ** If the statement is successfully recompiled, return SQLITE_OK. Otherwise,
** if the statement cannot be recompiled because another connection has
** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error
** occurs, return SQLITE_SCHEMA.
*/ */
int sqlite3Reprepare(Vdbe *p){ int sqlite3Reprepare(Vdbe *p){
int rc; int rc;
@ -684,7 +707,7 @@ int sqlite3Reprepare(Vdbe *p){
db->mallocFailed = 1; db->mallocFailed = 1;
} }
assert( pNew==0 ); assert( pNew==0 );
return 0; return (rc==SQLITE_LOCKED) ? SQLITE_LOCKED : SQLITE_SCHEMA;
}else{ }else{
assert( pNew!=0 ); assert( pNew!=0 );
} }
@ -692,7 +715,7 @@ int sqlite3Reprepare(Vdbe *p){
sqlite3TransferBindings(pNew, (sqlite3_stmt*)p); sqlite3TransferBindings(pNew, (sqlite3_stmt*)p);
sqlite3VdbeResetStepResult((Vdbe*)pNew); sqlite3VdbeResetStepResult((Vdbe*)pNew);
sqlite3VdbeFinalize((Vdbe*)pNew); sqlite3VdbeFinalize((Vdbe*)pNew);
return 1; return SQLITE_OK;
} }

View file

@ -14,7 +14,7 @@
** resolve all identifiers by associating them with a particular ** resolve all identifiers by associating them with a particular
** table and column. ** table and column.
** **
** $Id: resolve.c,v 1.15 2008/12/10 19:26:24 drh Exp $ ** $Id: resolve.c,v 1.20 2009/03/05 04:23:47 shane Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <stdlib.h> #include <stdlib.h>
@ -63,8 +63,9 @@ static void resolveAlias(
assert( pOrig!=0 ); assert( pOrig!=0 );
assert( pOrig->flags & EP_Resolved ); assert( pOrig->flags & EP_Resolved );
db = pParse->db; db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig); pDup = sqlite3ExprDup(db, pOrig, 0);
if( pDup==0 ) return; if( pDup==0 ) return;
sqlite3TokenCopy(db, &pDup->token, &pOrig->token);
if( pDup->op!=TK_COLUMN && zType[0]!='G' ){ if( pDup->op!=TK_COLUMN && zType[0]!='G' ){
pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0); pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
if( pDup==0 ) return; if( pDup==0 ) return;
@ -129,6 +130,7 @@ static int lookupName(
NameContext *pTopNC = pNC; /* First namecontext in the list */ NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */ Schema *pSchema = 0; /* Schema of the expression */
assert( pNC ); /* the name context cannot be NULL. */
assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */ assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */
/* Dequote and zero-terminate the names */ /* Dequote and zero-terminate the names */
@ -282,8 +284,8 @@ static int lookupName(
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
Expr *pOrig; Expr *pOrig;
assert( pExpr->pLeft==0 && pExpr->pRight==0 ); assert( pExpr->pLeft==0 && pExpr->pRight==0 );
assert( pExpr->pList==0 ); assert( pExpr->x.pList==0 );
assert( pExpr->pSelect==0 ); assert( pExpr->x.pSelect==0 );
pOrig = pEList->a[j].pExpr; pOrig = pEList->a[j].pExpr;
if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){ if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
@ -474,8 +476,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
*/ */
case TK_CONST_FUNC: case TK_CONST_FUNC:
case TK_FUNCTION: { case TK_FUNCTION: {
ExprList *pList = pExpr->pList; /* The argument list */ ExprList *pList = pExpr->x.pList; /* The argument list */
int n = pList ? pList->nExpr : 0; /* Number of arguments */ int n = pList ? pList->nExpr : 0; /* Number of arguments */
int no_such_func = 0; /* True if no such function exists */ int no_such_func = 0; /* True if no such function exists */
int wrong_num_args = 0; /* True if wrong number of arguments */ int wrong_num_args = 0; /* True if wrong number of arguments */
int is_agg = 0; /* True if is an aggregate function */ int is_agg = 0; /* True if is an aggregate function */
@ -485,6 +487,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
FuncDef *pDef; /* Information about the function */ FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */ u8 enc = ENC(pParse->db); /* The database encoding */
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
zId = (char*)pExpr->token.z; zId = (char*)pExpr->token.z;
nId = pExpr->token.n; nId = pExpr->token.n;
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0); pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
@ -541,14 +544,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
case TK_EXISTS: case TK_EXISTS:
#endif #endif
case TK_IN: { case TK_IN: {
if( pExpr->pSelect ){ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
int nRef = pNC->nRef; int nRef = pNC->nRef;
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
if( pNC->isCheck ){ if( pNC->isCheck ){
sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints"); sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
} }
#endif #endif
sqlite3WalkSelect(pWalker, pExpr->pSelect); sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
assert( pNC->nRef>=nRef ); assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){ if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect); ExprSetProperty(pExpr, EP_VarSelect);
@ -736,7 +739,7 @@ static int resolveCompoundOrderBy(
}else{ }else{
iCol = resolveAsName(pParse, pEList, pE); iCol = resolveAsName(pParse, pEList, pE);
if( iCol==0 ){ if( iCol==0 ){
pDup = sqlite3ExprDup(db, pE); pDup = sqlite3ExprDup(db, pE, 0);
if( !db->mallocFailed ){ if( !db->mallocFailed ){
assert(pDup); assert(pDup);
iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup);

262
select.c
View file

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite. ** to handle SELECT statements in SQLite.
** **
** $Id: select.c,v 1.499 2009/02/09 13:19:28 drh Exp $ ** $Id: select.c,v 1.506 2009/03/31 03:41:57 shane Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -800,8 +800,7 @@ static void generateSortTail(
iTab = pOrderBy->iECursor; iTab = pOrderBy->iECursor;
if( eDest==SRT_Output || eDest==SRT_Coroutine ){ if( eDest==SRT_Output || eDest==SRT_Coroutine ){
pseudoTab = pParse->nTab++; pseudoTab = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nColumn); sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output, nColumn);
sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output);
} }
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
codeOffset(v, p, addrContinue); codeOffset(v, p, addrContinue);
@ -987,8 +986,9 @@ static const char *columnType(
** statement. ** statement.
*/ */
NameContext sNC; NameContext sNC;
Select *pS = pExpr->pSelect; Select *pS = pExpr->x.pSelect;
Expr *p = pS->pEList->a[0].pExpr; Expr *p = pS->pEList->a[0].pExpr;
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
sNC.pSrcList = pS->pSrc; sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC; sNC.pNext = pNC;
sNC.pParse = pNC->pParse; sNC.pParse = pNC->pParse;
@ -1229,7 +1229,7 @@ static int selectColumnsFromExprList(
** The column list has only names, not types or collations. This ** The column list has only names, not types or collations. This
** routine goes through and adds the types and collations. ** routine goes through and adds the types and collations.
** **
** This routine requires that all indentifiers in the SELECT ** This routine requires that all identifiers in the SELECT
** statement be resolved. ** statement be resolved.
*/ */
static void selectAddColumnTypeAndCollation( static void selectAddColumnTypeAndCollation(
@ -1284,7 +1284,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
if( pTab==0 ){ if( pTab==0 ){
return 0; return 0;
} }
pTab->db = db; pTab->dbMem = db->lookaside.bEnabled ? db : 0;
pTab->nRef = 1; pTab->nRef = 1;
pTab->zName = 0; pTab->zName = 0;
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
@ -2141,7 +2141,7 @@ static int multiSelectOrderBy(
/* Reattach the ORDER BY clause to the query. /* Reattach the ORDER BY clause to the query.
*/ */
p->pOrderBy = pOrderBy; p->pOrderBy = pOrderBy;
pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy); pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0);
/* Allocate a range of temporary registers and the KeyInfo needed /* Allocate a range of temporary registers and the KeyInfo needed
** for the logic that removes duplicate result rows when the ** for the logic that removes duplicate result rows when the
@ -2392,23 +2392,26 @@ static void substExpr(
}else{ }else{
Expr *pNew; Expr *pNew;
assert( pEList!=0 && pExpr->iColumn<pEList->nExpr ); assert( pEList!=0 && pExpr->iColumn<pEList->nExpr );
assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 ); assert( pExpr->pLeft==0 && pExpr->pRight==0 );
pNew = pEList->a[pExpr->iColumn].pExpr; pNew = pEList->a[pExpr->iColumn].pExpr;
assert( pNew!=0 ); assert( pNew!=0 );
pExpr->op = pNew->op; pExpr->op = pNew->op;
assert( pExpr->pLeft==0 ); assert( pExpr->pLeft==0 );
pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft); pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft, 0);
assert( pExpr->pRight==0 ); assert( pExpr->pRight==0 );
pExpr->pRight = sqlite3ExprDup(db, pNew->pRight); pExpr->pRight = sqlite3ExprDup(db, pNew->pRight, 0);
assert( pExpr->pList==0 );
pExpr->pList = sqlite3ExprListDup(db, pNew->pList);
pExpr->iTable = pNew->iTable; pExpr->iTable = pNew->iTable;
pExpr->pTab = pNew->pTab; pExpr->pTab = pNew->pTab;
pExpr->iColumn = pNew->iColumn; pExpr->iColumn = pNew->iColumn;
pExpr->iAgg = pNew->iAgg; pExpr->iAgg = pNew->iAgg;
sqlite3TokenCopy(db, &pExpr->token, &pNew->token); sqlite3TokenCopy(db, &pExpr->token, &pNew->token);
sqlite3TokenCopy(db, &pExpr->span, &pNew->span); sqlite3TokenCopy(db, &pExpr->span, &pNew->span);
pExpr->pSelect = sqlite3SelectDup(db, pNew->pSelect); assert( pExpr->x.pList==0 && pExpr->x.pSelect==0 );
if( ExprHasProperty(pNew, EP_xIsSelect) ){
pExpr->x.pSelect = sqlite3SelectDup(db, pNew->x.pSelect, 0);
}else{
pExpr->x.pList = sqlite3ExprListDup(db, pNew->x.pList, 0);
}
pExpr->flags = pNew->flags; pExpr->flags = pNew->flags;
pExpr->pAggInfo = pNew->pAggInfo; pExpr->pAggInfo = pNew->pAggInfo;
pNew->pAggInfo = 0; pNew->pAggInfo = 0;
@ -2416,8 +2419,11 @@ static void substExpr(
}else{ }else{
substExpr(db, pExpr->pLeft, iTable, pEList); substExpr(db, pExpr->pLeft, iTable, pEList);
substExpr(db, pExpr->pRight, iTable, pEList); substExpr(db, pExpr->pRight, iTable, pEList);
substSelect(db, pExpr->pSelect, iTable, pEList); if( ExprHasProperty(pExpr, EP_xIsSelect) ){
substExprList(db, pExpr->pList, iTable, pEList); substSelect(db, pExpr->x.pSelect, iTable, pEList);
}else{
substExprList(db, pExpr->x.pList, iTable, pEList);
}
} }
} }
static void substExprList( static void substExprList(
@ -2729,7 +2735,7 @@ static int flattenSubquery(
p->pSrc = 0; p->pSrc = 0;
p->pPrior = 0; p->pPrior = 0;
p->pLimit = 0; p->pLimit = 0;
pNew = sqlite3SelectDup(db, p); pNew = sqlite3SelectDup(db, p, 0);
p->pLimit = pLimit; p->pLimit = pLimit;
p->pOrderBy = pOrderBy; p->pOrderBy = pOrderBy;
p->pSrc = pSrc; p->pSrc = pSrc;
@ -2873,7 +2879,7 @@ static int flattenSubquery(
substExprList(db, pParent->pOrderBy, iParent, pSub->pEList); substExprList(db, pParent->pOrderBy, iParent, pSub->pEList);
} }
if( pSub->pWhere ){ if( pSub->pWhere ){
pWhere = sqlite3ExprDup(db, pSub->pWhere); pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
}else{ }else{
pWhere = 0; pWhere = 0;
} }
@ -2883,9 +2889,9 @@ static int flattenSubquery(
pParent->pWhere = pWhere; pParent->pWhere = pWhere;
substExpr(db, pParent->pHaving, iParent, pSub->pEList); substExpr(db, pParent->pHaving, iParent, pSub->pEList);
pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving, pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
sqlite3ExprDup(db, pSub->pHaving)); sqlite3ExprDup(db, pSub->pHaving, 0));
assert( pParent->pGroupBy==0 ); assert( pParent->pGroupBy==0 );
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy); pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
}else{ }else{
substExpr(db, pParent->pWhere, iParent, pSub->pEList); substExpr(db, pParent->pWhere, iParent, pSub->pEList);
pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere); pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
@ -2934,7 +2940,8 @@ static u8 minMaxQuery(Select *p){
if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL; if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
pExpr = pEList->a[0].pExpr; pExpr = pEList->a[0].pExpr;
pEList = pExpr->pList; if( ExprHasProperty(pExpr, EP_xIsSelect) ) return 0;
pEList = pExpr->x.pList;
if( pExpr->op!=TK_AGG_FUNCTION || pEList==0 || pEList->nExpr!=1 ) return 0; if( pExpr->op!=TK_AGG_FUNCTION || pEList==0 || pEList->nExpr!=1 ) return 0;
if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL; if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
if( pExpr->token.n!=3 ) return WHERE_ORDERBY_NORMAL; if( pExpr->token.n!=3 ) return WHERE_ORDERBY_NORMAL;
@ -2946,6 +2953,40 @@ static u8 minMaxQuery(Select *p){
return WHERE_ORDERBY_NORMAL; return WHERE_ORDERBY_NORMAL;
} }
/*
** The select statement passed as the first argument is an aggregate query.
** The second argment is the associated aggregate-info object. This
** function tests if the SELECT is of the form:
**
** SELECT count(*) FROM <tbl>
**
** where table is a database table, not a sub-select or view. If the query
** does match this pattern, then a pointer to the Table object representing
** <tbl> is returned. Otherwise, 0 is returned.
*/
static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
Table *pTab;
Expr *pExpr;
assert( !p->pGroupBy );
if( p->pWhere || p->pEList->nExpr!=1
|| p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect
){
return 0;
}
pTab = p->pSrc->a[0].pTab;
pExpr = p->pEList->a[0].pExpr;
assert( pTab && !pTab->pSelect && pExpr );
if( IsVirtual(pTab) ) return 0;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0;
if( pExpr->flags&EP_Distinct ) return 0;
return pTab;
}
/* /*
** If the source-list item passed as an argument was augmented with an ** If the source-list item passed as an argument was augmented with an
** INDEXED BY clause, then try to locate the specified index. If there ** INDEXED BY clause, then try to locate the specified index. If there
@ -3039,7 +3080,7 @@ static int selectExpander(Walker *pWalker, Select *p){
sqlite3WalkSelect(pWalker, pSel); sqlite3WalkSelect(pWalker, pSel);
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
if( pTab==0 ) return WRC_Abort; if( pTab==0 ) return WRC_Abort;
pTab->db = db; pTab->dbMem = db->lookaside.bEnabled ? db : 0;
pTab->nRef = 1; pTab->nRef = 1;
pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
while( pSel->pPrior ){ pSel = pSel->pPrior; } while( pSel->pPrior ){ pSel = pSel->pPrior; }
@ -3065,7 +3106,7 @@ static int selectExpander(Walker *pWalker, Select *p){
** in the inner view. ** in the inner view.
*/ */
if( pFrom->pSelect==0 ){ if( pFrom->pSelect==0 ){
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect); pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
sqlite3WalkSelect(pWalker, pFrom->pSelect); sqlite3WalkSelect(pWalker, pFrom->pSelect);
} }
} }
@ -3364,12 +3405,13 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem);
if( pFunc->iDistinct>=0 ){ if( pFunc->iDistinct>=0 ){
Expr *pE = pFunc->pExpr; Expr *pE = pFunc->pExpr;
if( pE->pList==0 || pE->pList->nExpr!=1 ){ assert( !ExprHasProperty(pE, EP_xIsSelect) );
if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){
sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one "
"argument"); "argument");
pFunc->iDistinct = -1; pFunc->iDistinct = -1;
}else{ }else{
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->pList); KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
(char*)pKeyInfo, P4_KEYINFO_HANDOFF); (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
} }
@ -3386,7 +3428,8 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
int i; int i;
struct AggInfo_func *pF; struct AggInfo_func *pF;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
ExprList *pList = pF->pExpr->pList; ExprList *pList = pF->pExpr->x.pList;
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
sqlite3VdbeAddOp4(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0, 0, sqlite3VdbeAddOp4(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0, 0,
(void*)pF->pFunc, P4_FUNCDEF); (void*)pF->pFunc, P4_FUNCDEF);
} }
@ -3407,7 +3450,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
int nArg; int nArg;
int addrNext = 0; int addrNext = 0;
int regAgg; int regAgg;
ExprList *pList = pF->pExpr->pList; ExprList *pList = pF->pExpr->x.pList;
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
if( pList ){ if( pList ){
nArg = pList->nExpr; nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg); regAgg = sqlite3GetTempRange(pParse, nArg);
@ -3657,7 +3701,7 @@ int sqlite3Select(
** GROUP BY might use an index, DISTINCT never does. ** GROUP BY might use an index, DISTINCT never does.
*/ */
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct && !p->pGroupBy ){ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct && !p->pGroupBy ){
p->pGroupBy = sqlite3ExprListDup(db, p->pEList); p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
pGroupBy = p->pGroupBy; pGroupBy = p->pGroupBy;
p->selFlags &= ~SF_Distinct; p->selFlags &= ~SF_Distinct;
isDistinct = 0; isDistinct = 0;
@ -3780,7 +3824,8 @@ int sqlite3Select(
} }
sAggInfo.nAccumulator = sAggInfo.nColumn; sAggInfo.nAccumulator = sAggInfo.nColumn;
for(i=0; i<sAggInfo.nFunc; i++){ for(i=0; i<sAggInfo.nFunc; i++){
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList); assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
} }
if( db->mallocFailed ) goto select_end; if( db->mallocFailed ) goto select_end;
@ -3987,68 +4032,127 @@ int sqlite3Select(
} /* endif pGroupBy */ } /* endif pGroupBy */
else { else {
ExprList *pMinMax = 0;
ExprList *pDel = 0; ExprList *pDel = 0;
u8 flag; #ifndef SQLITE_OMIT_BTREECOUNT
Table *pTab;
if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
/* If isSimpleCount() returns a pointer to a Table structure, then
** the SQL statement is of the form:
**
** SELECT count(*) FROM <tbl>
**
** where the Table structure returned represents table <tbl>.
**
** This statement is so common that it is optimized specially. The
** OP_Count instruction is executed either on the intkey table that
** contains the data for table <tbl> or on one of its indexes. It
** is better to execute the op on an index, as indexes are almost
** always spread across less pages than their corresponding tables.
*/
const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */
Index *pIdx; /* Iterator variable */
KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */
Index *pBest = 0; /* Best index found so far */
int iRoot = pTab->tnum; /* Root page of scanned b-tree */
/* Check if the query is of one of the following forms: sqlite3CodeVerifySchema(pParse, iDb);
** sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
** SELECT min(x) FROM ...
** SELECT max(x) FROM ... /* Search for the index that has the least amount of columns. If
** ** there is such an index, and it has less columns than the table
** If it is, then ask the code in where.c to attempt to sort results ** does, then we can assume that it consumes less space on disk and
** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause. ** will therefore be cheaper to scan to determine the query result.
** If where.c is able to produce results sorted in this order, then ** In this case set iRoot to the root page number of the index b-tree
** add vdbe code to break out of the processing loop after the ** and pKeyInfo to the KeyInfo structure required to navigate the
** first iteration (since the first iteration of the loop is ** index.
** guaranteed to operate on the row with the minimum or maximum **
** value of x, the only row required). ** In practice the KeyInfo structure will not be used. It is only
** ** passed to keep OP_OpenRead happy.
** A special flag must be passed to sqlite3WhereBegin() to slightly */
** modify behaviour as follows: for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
** if( !pBest || pIdx->nColumn<pBest->nColumn ){
** + If the query is a "SELECT min(x)", then the loop coded by pBest = pIdx;
** where.c should not iterate over any values with a NULL value }
** for x.
**
** + The optimizer code in where.c (the thing that decides which
** index or indices to use) should place a different priority on
** satisfying the 'ORDER BY' clause than it does in other cases.
** Refer to code and comments in where.c for details.
*/
flag = minMaxQuery(p);
if( flag ){
pDel = pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->pList);
if( pMinMax && !db->mallocFailed ){
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
pMinMax->a[0].pExpr->op = TK_COLUMN;
} }
if( pBest && pBest->nColumn<pTab->nCol ){
iRoot = pBest->tnum;
pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest);
}
/* Open a read-only cursor, execute the OP_Count, close the cursor. */
sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb);
if( pKeyInfo ){
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF);
}
sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
}else
#endif /* SQLITE_OMIT_BTREECOUNT */
{
/* Check if the query is of one of the following forms:
**
** SELECT min(x) FROM ...
** SELECT max(x) FROM ...
**
** If it is, then ask the code in where.c to attempt to sort results
** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause.
** If where.c is able to produce results sorted in this order, then
** add vdbe code to break out of the processing loop after the
** first iteration (since the first iteration of the loop is
** guaranteed to operate on the row with the minimum or maximum
** value of x, the only row required).
**
** A special flag must be passed to sqlite3WhereBegin() to slightly
** modify behaviour as follows:
**
** + If the query is a "SELECT min(x)", then the loop coded by
** where.c should not iterate over any values with a NULL value
** for x.
**
** + The optimizer code in where.c (the thing that decides which
** index or indices to use) should place a different priority on
** satisfying the 'ORDER BY' clause than it does in other cases.
** Refer to code and comments in where.c for details.
*/
ExprList *pMinMax = 0;
u8 flag = minMaxQuery(p);
if( flag ){
assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) );
pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0);
pDel = pMinMax;
if( pMinMax && !db->mallocFailed ){
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
pMinMax->a[0].pExpr->op = TK_COLUMN;
}
}
/* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0);
if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDel);
goto select_end;
}
updateAccumulator(pParse, &sAggInfo);
if( !pMinMax && flag ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
VdbeComment((v, "%s() by index",
(flag==WHERE_ORDERBY_MIN?"min":"max")));
}
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, &sAggInfo);
} }
/* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0);
if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDel);
goto select_end;
}
updateAccumulator(pParse, &sAggInfo);
if( !pMinMax && flag ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
VdbeComment((v, "%s() by index",(flag==WHERE_ORDERBY_MIN?"min":"max")));
}
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, &sAggInfo);
pOrderBy = 0; pOrderBy = 0;
if( pHaving ){ if( pHaving ){
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL); sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
} }
selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1, selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
pDest, addrEnd, addrEnd); pDest, addrEnd, addrEnd);
sqlite3ExprListDelete(db, pDel); sqlite3ExprListDelete(db, pDel);
} }
sqlite3VdbeResolveLabel(v, addrEnd); sqlite3VdbeResolveLabel(v, addrEnd);

932
shell.c
View file

@ -12,7 +12,7 @@
** This file contains code to implement the "sqlite" command line ** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases. ** utility for accessing SQLite databases.
** **
** $Id: shell.c,v 1.201 2009/02/04 22:46:47 drh Exp $ ** $Id: shell.c,v 1.207 2009/03/16 10:59:44 drh Exp $
*/ */
#if defined(_WIN32) || defined(WIN32) #if defined(_WIN32) || defined(WIN32)
/* This needs to come before any includes for MSVC compiler */ /* This needs to come before any includes for MSVC compiler */
@ -120,6 +120,861 @@ static void endTimer(void){
*/ */
#define UNUSED_PARAMETER(x) (void)(x) #define UNUSED_PARAMETER(x) (void)(x)
/**************************************************************************
***************************************************************************
** Begin genfkey logic.
*/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined SQLITE_OMIT_SUBQUERY
#define GENFKEY_ERROR 1
#define GENFKEY_DROPTRIGGER 2
#define GENFKEY_CREATETRIGGER 3
static int genfkey_create_triggers(sqlite3 *, const char *, void *,
int (*)(void *, int, const char *)
);
struct GenfkeyCb {
void *pCtx;
int eType;
int (*xData)(void *, int, const char *);
};
typedef struct GenfkeyCb GenfkeyCb;
/* The code in this file defines a sqlite3 virtual-table module that
** provides a read-only view of the current database schema. There is one
** row in the schema table for each column in the database schema.
*/
#define SCHEMA \
"CREATE TABLE x(" \
"database," /* Name of database (i.e. main, temp etc.) */ \
"tablename," /* Name of table */ \
"cid," /* Column number (from left-to-right, 0 upward) */ \
"name," /* Column name */ \
"type," /* Specified type (i.e. VARCHAR(32)) */ \
"not_null," /* Boolean. True if NOT NULL was specified */ \
"dflt_value," /* Default value for this column */ \
"pk" /* True if this column is part of the primary key */ \
")"
#define SCHEMA2 \
"CREATE TABLE x(" \
"database," /* Name of database (i.e. main, temp etc.) */ \
"from_tbl," /* Name of table */ \
"fkid," \
"seq," \
"to_tbl," \
"from_col," \
"to_col," \
"on_update," \
"on_delete," \
"match" \
")"
#define SCHEMA3 \
"CREATE TABLE x(" \
"database," /* Name of database (i.e. main, temp etc.) */ \
"tablename," /* Name of table */ \
"seq," \
"name," \
"isunique" \
")"
#define SCHEMA4 \
"CREATE TABLE x(" \
"database," /* Name of database (i.e. main, temp etc.) */ \
"indexname," /* Name of table */ \
"seqno," \
"cid," \
"name" \
")"
#define SCHEMA5 \
"CREATE TABLE x(" \
"database," /* Name of database (i.e. main, temp etc.) */ \
"triggername," /* Name of trigger */ \
"dummy" /* Unused */ \
")"
typedef struct SchemaTable SchemaTable;
struct SchemaTable {
const char *zName;
const char *zObject;
const char *zPragma;
const char *zSchema;
} aSchemaTable[] = {
{ "table_info", "table", "PRAGMA %Q.table_info(%Q)", SCHEMA },
{ "foreign_key_list", "table", "PRAGMA %Q.foreign_key_list(%Q)", SCHEMA2 },
{ "index_list", "table", "PRAGMA %Q.index_list(%Q)", SCHEMA3 },
{ "index_info", "index", "PRAGMA %Q.index_info(%Q)", SCHEMA4 },
{ "trigger_list", "trigger", "SELECT 1", SCHEMA5 },
{ 0, 0, 0, 0 }
};
typedef struct schema_vtab schema_vtab;
typedef struct schema_cursor schema_cursor;
/* A schema table object */
struct schema_vtab {
sqlite3_vtab base;
sqlite3 *db;
SchemaTable *pType;
};
/* A schema table cursor object */
struct schema_cursor {
sqlite3_vtab_cursor base;
sqlite3_stmt *pDbList;
sqlite3_stmt *pTableList;
sqlite3_stmt *pColumnList;
int rowid;
};
/*
** Table destructor for the schema module.
*/
static int schemaDestroy(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return 0;
}
/*
** Table constructor for the schema module.
*/
static int schemaCreate(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
int rc = SQLITE_NOMEM;
schema_vtab *pVtab;
SchemaTable *pType = &aSchemaTable[0];
UNUSED_PARAMETER(pzErr);
if( argc>3 ){
int i;
pType = 0;
for(i=0; aSchemaTable[i].zName; i++){
if( 0==strcmp(argv[3], aSchemaTable[i].zName) ){
pType = &aSchemaTable[i];
}
}
if( !pType ){
return SQLITE_ERROR;
}
}
pVtab = sqlite3_malloc(sizeof(schema_vtab));
if( pVtab ){
memset(pVtab, 0, sizeof(schema_vtab));
pVtab->db = (sqlite3 *)pAux;
pVtab->pType = pType;
rc = sqlite3_declare_vtab(db, pType->zSchema);
}
*ppVtab = (sqlite3_vtab *)pVtab;
return rc;
}
/*
** Open a new cursor on the schema table.
*/
static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
int rc = SQLITE_NOMEM;
schema_cursor *pCur;
UNUSED_PARAMETER(pVTab);
pCur = sqlite3_malloc(sizeof(schema_cursor));
if( pCur ){
memset(pCur, 0, sizeof(schema_cursor));
*ppCursor = (sqlite3_vtab_cursor *)pCur;
rc = SQLITE_OK;
}
return rc;
}
/*
** Close a schema table cursor.
*/
static int schemaClose(sqlite3_vtab_cursor *cur){
schema_cursor *pCur = (schema_cursor *)cur;
sqlite3_finalize(pCur->pDbList);
sqlite3_finalize(pCur->pTableList);
sqlite3_finalize(pCur->pColumnList);
sqlite3_free(pCur);
return SQLITE_OK;
}
static void columnToResult(sqlite3_context *ctx, sqlite3_stmt *pStmt, int iCol){
switch( sqlite3_column_type(pStmt, iCol) ){
case SQLITE_NULL:
sqlite3_result_null(ctx);
break;
case SQLITE_INTEGER:
sqlite3_result_int64(ctx, sqlite3_column_int64(pStmt, iCol));
break;
case SQLITE_FLOAT:
sqlite3_result_double(ctx, sqlite3_column_double(pStmt, iCol));
break;
case SQLITE_TEXT: {
const char *z = (const char *)sqlite3_column_text(pStmt, iCol);
sqlite3_result_text(ctx, z, -1, SQLITE_TRANSIENT);
break;
}
}
}
/*
** Retrieve a column of data.
*/
static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
schema_cursor *pCur = (schema_cursor *)cur;
switch( i ){
case 0:
columnToResult(ctx, pCur->pDbList, 1);
break;
case 1:
columnToResult(ctx, pCur->pTableList, 0);
break;
default:
columnToResult(ctx, pCur->pColumnList, i-2);
break;
}
return SQLITE_OK;
}
/*
** Retrieve the current rowid.
*/
static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
schema_cursor *pCur = (schema_cursor *)cur;
*pRowid = pCur->rowid;
return SQLITE_OK;
}
static int finalize(sqlite3_stmt **ppStmt){
int rc = sqlite3_finalize(*ppStmt);
*ppStmt = 0;
return rc;
}
static int schemaEof(sqlite3_vtab_cursor *cur){
schema_cursor *pCur = (schema_cursor *)cur;
return (pCur->pDbList ? 0 : 1);
}
/*
** Advance the cursor to the next row.
*/
static int schemaNext(sqlite3_vtab_cursor *cur){
int rc = SQLITE_OK;
schema_cursor *pCur = (schema_cursor *)cur;
schema_vtab *pVtab = (schema_vtab *)(cur->pVtab);
char *zSql = 0;
while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){
if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit;
while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){
if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit;
assert(pCur->pDbList);
while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){
rc = finalize(&pCur->pDbList);
goto next_exit;
}
/* Set zSql to the SQL to pull the list of tables from the
** sqlite_master (or sqlite_temp_master) table of the database
** identfied by the row pointed to by the SQL statement pCur->pDbList
** (iterating through a "PRAGMA database_list;" statement).
*/
if( sqlite3_column_int(pCur->pDbList, 0)==1 ){
zSql = sqlite3_mprintf(
"SELECT name FROM sqlite_temp_master WHERE type=%Q",
pVtab->pType->zObject
);
}else{
sqlite3_stmt *pDbList = pCur->pDbList;
zSql = sqlite3_mprintf(
"SELECT name FROM %Q.sqlite_master WHERE type=%Q",
sqlite3_column_text(pDbList, 1), pVtab->pType->zObject
);
}
if( !zSql ){
rc = SQLITE_NOMEM;
goto next_exit;
}
rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0);
sqlite3_free(zSql);
if( rc!=SQLITE_OK ) goto next_exit;
}
/* Set zSql to the SQL to the table_info pragma for the table currently
** identified by the rows pointed to by statements pCur->pDbList and
** pCur->pTableList.
*/
zSql = sqlite3_mprintf(pVtab->pType->zPragma,
sqlite3_column_text(pCur->pDbList, 1),
sqlite3_column_text(pCur->pTableList, 0)
);
if( !zSql ){
rc = SQLITE_NOMEM;
goto next_exit;
}
rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0);
sqlite3_free(zSql);
if( rc!=SQLITE_OK ) goto next_exit;
}
pCur->rowid++;
next_exit:
/* TODO: Handle rc */
return rc;
}
/*
** Reset a schema table cursor.
*/
static int schemaFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
int rc;
schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab);
schema_cursor *pCur = (schema_cursor *)pVtabCursor;
UNUSED_PARAMETER(idxNum);
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
pCur->rowid = 0;
finalize(&pCur->pTableList);
finalize(&pCur->pColumnList);
finalize(&pCur->pDbList);
rc = sqlite3_prepare(pVtab->db,"SELECT 0, 'main'", -1, &pCur->pDbList, 0);
return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc);
}
/*
** Analyse the WHERE condition.
*/
static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
UNUSED_PARAMETER(tab);
UNUSED_PARAMETER(pIdxInfo);
return SQLITE_OK;
}
/*
** A virtual table module that merely echos method calls into TCL
** variables.
*/
static sqlite3_module schemaModule = {
0, /* iVersion */
schemaCreate,
schemaCreate,
schemaBestIndex,
schemaDestroy,
schemaDestroy,
schemaOpen, /* xOpen - open a cursor */
schemaClose, /* xClose - close a cursor */
schemaFilter, /* xFilter - configure scan constraints */
schemaNext, /* xNext - advance a cursor */
schemaEof, /* xEof */
schemaColumn, /* xColumn - read data */
schemaRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
};
/*
** Extension load function.
*/
static int installSchemaModule(sqlite3 *db, sqlite3 *sdb){
sqlite3_create_module(db, "schema", &schemaModule, (void *)sdb);
return 0;
}
/*
** sj(zValue, zJoin)
**
** The following block contains the implementation of an aggregate
** function that returns a string. Each time the function is stepped,
** it appends data to an internal buffer. When the aggregate is finalized,
** the contents of the buffer are returned.
**
** The first time the aggregate is stepped the buffer is set to a copy
** of the first argument. The second time and subsequent times it is
** stepped a copy of the second argument is appended to the buffer, then
** a copy of the first.
**
** Example:
**
** INSERT INTO t1(a) VALUES('1');
** INSERT INTO t1(a) VALUES('2');
** INSERT INTO t1(a) VALUES('3');
** SELECT sj(a, ', ') FROM t1;
**
** => "1, 2, 3"
**
*/
struct StrBuffer {
char *zBuf;
};
typedef struct StrBuffer StrBuffer;
static void joinFinalize(sqlite3_context *context){
StrBuffer *p;
p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer));
sqlite3_result_text(context, p->zBuf, -1, SQLITE_TRANSIENT);
sqlite3_free(p->zBuf);
}
static void joinStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
StrBuffer *p;
UNUSED_PARAMETER(argc);
p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer));
if( p->zBuf==0 ){
p->zBuf = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
}else{
char *zTmp = p->zBuf;
p->zBuf = sqlite3_mprintf("%s%s%s",
zTmp, sqlite3_value_text(argv[1]), sqlite3_value_text(argv[0])
);
sqlite3_free(zTmp);
}
}
/*
** dq(zString)
**
** This scalar function accepts a single argument and interprets it as
** a text value. The return value is the argument enclosed in double
** quotes. If any double quote characters are present in the argument,
** these are escaped.
**
** dq('the raven "Nevermore."') == '"the raven ""Nevermore."""'
*/
static void doublequote(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int ii;
char *zOut;
char *zCsr;
const char *zIn = (const char *)sqlite3_value_text(argv[0]);
int nIn = sqlite3_value_bytes(argv[0]);
UNUSED_PARAMETER(argc);
zOut = sqlite3_malloc(nIn*2+3);
zCsr = zOut;
*zCsr++ = '"';
for(ii=0; ii<nIn; ii++){
*zCsr++ = zIn[ii];
if( zIn[ii]=='"' ){
*zCsr++ = '"';
}
}
*zCsr++ = '"';
*zCsr++ = '\0';
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
sqlite3_free(zOut);
}
/*
** multireplace(zString, zSearch1, zReplace1, ...)
*/
static void multireplace(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int i = 0;
char *zOut = 0;
int nOut = 0;
int nMalloc = 0;
const char *zIn = (const char *)sqlite3_value_text(argv[0]);
int nIn = sqlite3_value_bytes(argv[0]);
while( i<nIn ){
const char *zCopy = &zIn[i];
int nCopy = 1;
int nReplace = 1;
int j;
for(j=1; j<(argc-1); j+=2){
const char *z = (const char *)sqlite3_value_text(argv[j]);
int n = sqlite3_value_bytes(argv[j]);
if( n<=(nIn-i) && 0==strncmp(z, zCopy, n) ){
zCopy = (const char *)sqlite3_value_text(argv[j+1]);
nCopy = sqlite3_value_bytes(argv[j+1]);
nReplace = n;
break;
}
}
if( (nOut+nCopy)>nMalloc ){
nMalloc = 16 + (nOut+nCopy)*2;
zOut = (char *)sqlite3_realloc(zOut, nMalloc);
}
assert( nMalloc>=(nOut+nCopy) );
memcpy(&zOut[nOut], zCopy, nCopy);
i += nReplace;
nOut += nCopy;
}
sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT);
sqlite3_free(zOut);
}
/*
** A callback for sqlite3_exec() invokes the callback specified by the
** GenfkeyCb structure pointed to by the void* passed as the first argument.
*/
static int invokeCallback(void *p, int nArg, char **azArg, char **azCol){
GenfkeyCb *pCb = (GenfkeyCb *)p;
UNUSED_PARAMETER(nArg);
UNUSED_PARAMETER(azCol);
return pCb->xData(pCb->pCtx, pCb->eType, azArg[0]);
}
int detectSchemaProblem(
sqlite3 *db, /* Database connection */
const char *zMessage, /* English language error message */
const char *zSql, /* SQL statement to run */
GenfkeyCb *pCb
){
sqlite3_stmt *pStmt;
int rc;
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
return rc;
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){
char *zDel;
int iFk = sqlite3_column_int(pStmt, 0);
const char *zTab = (const char *)sqlite3_column_text(pStmt, 1);
zDel = sqlite3_mprintf("Error in table %s: %s", zTab, zMessage);
rc = pCb->xData(pCb->pCtx, pCb->eType, zDel);
sqlite3_free(zDel);
if( rc!=SQLITE_OK ) return rc;
zDel = sqlite3_mprintf(
"DELETE FROM temp.fkey WHERE from_tbl = %Q AND fkid = %d"
, zTab, iFk
);
sqlite3_exec(db, zDel, 0, 0, 0);
sqlite3_free(zDel);
}
sqlite3_finalize(pStmt);
return SQLITE_OK;
}
/*
** Create and populate temporary table "fkey".
*/
static int populateTempTable(sqlite3 *db, GenfkeyCb *pCallback){
int rc;
rc = sqlite3_exec(db,
"CREATE VIRTUAL TABLE temp.v_fkey USING schema(foreign_key_list);"
"CREATE VIRTUAL TABLE temp.v_col USING schema(table_info);"
"CREATE VIRTUAL TABLE temp.v_idxlist USING schema(index_list);"
"CREATE VIRTUAL TABLE temp.v_idxinfo USING schema(index_info);"
"CREATE VIRTUAL TABLE temp.v_triggers USING schema(trigger_list);"
"CREATE TABLE temp.fkey AS "
"SELECT from_tbl, to_tbl, fkid, from_col, to_col, on_update, on_delete "
"FROM temp.v_fkey WHERE database = 'main';"
, 0, 0, 0
);
if( rc!=SQLITE_OK ) return rc;
rc = detectSchemaProblem(db, "foreign key columns do not exist",
"SELECT fkid, from_tbl "
"FROM temp.fkey "
"WHERE to_col IS NOT NULL AND NOT EXISTS (SELECT 1 "
"FROM temp.v_col WHERE tablename=to_tbl AND name==to_col"
")", pCallback
);
if( rc!=SQLITE_OK ) return rc;
/* At this point the temp.fkey table is mostly populated. If any foreign
** keys were specified so that they implicitly refer to they primary
** key of the parent table, the "to_col" values of the temp.fkey rows
** are still set to NULL.
**
** This is easily fixed for single column primary keys, but not for
** composites. With a composite primary key, there is no way to reliably
** query sqlite for the order in which the columns that make up the
** composite key were declared i.e. there is no way to tell if the
** schema actually contains "PRIMARY KEY(a, b)" or "PRIMARY KEY(b, a)".
** Therefore, this case is not handled. The following function call
** detects instances of this case.
*/
rc = detectSchemaProblem(db, "implicit mapping to composite primary key",
"SELECT fkid, from_tbl "
"FROM temp.fkey "
"WHERE to_col IS NULL "
"GROUP BY fkid, from_tbl HAVING count(*) > 1", pCallback
);
if( rc!=SQLITE_OK ) return rc;
/* Detect attempts to implicitly map to the primary key of a table
** that has no primary key column.
*/
rc = detectSchemaProblem(db, "implicit mapping to non-existant primary key",
"SELECT fkid, from_tbl "
"FROM temp.fkey "
"WHERE to_col IS NULL AND NOT EXISTS "
"(SELECT 1 FROM temp.v_col WHERE pk AND tablename = temp.fkey.to_tbl)"
, pCallback
);
if( rc!=SQLITE_OK ) return rc;
/* Fix all the implicit primary key mappings in the temp.fkey table. */
rc = sqlite3_exec(db,
"UPDATE temp.fkey SET to_col = "
"(SELECT name FROM temp.v_col WHERE pk AND tablename=temp.fkey.to_tbl)"
" WHERE to_col IS NULL;"
, 0, 0, 0
);
if( rc!=SQLITE_OK ) return rc;
/* Now check that all all parent keys are either primary keys or
** subject to a unique constraint.
*/
rc = sqlite3_exec(db,
"CREATE TABLE temp.idx2 AS SELECT "
"il.tablename AS tablename,"
"ii.indexname AS indexname,"
"ii.name AS col "
"FROM temp.v_idxlist AS il, temp.v_idxinfo AS ii "
"WHERE il.isunique AND il.database='main' AND ii.indexname = il.name;"
"INSERT INTO temp.idx2 "
"SELECT tablename, 'pk', name FROM temp.v_col WHERE pk;"
"CREATE TABLE temp.idx AS SELECT "
"tablename, indexname, sj(dq(col),',') AS cols "
"FROM (SELECT * FROM temp.idx2 ORDER BY col) "
"GROUP BY tablename, indexname;"
"CREATE TABLE temp.fkey2 AS SELECT "
"fkid, from_tbl, to_tbl, sj(dq(to_col),',') AS cols "
"FROM (SELECT * FROM temp.fkey ORDER BY to_col) "
"GROUP BY fkid, from_tbl;"
"CREATE TABLE temp.triggers AS SELECT "
"triggername FROM temp.v_triggers WHERE database='main' AND "
"triggername LIKE 'genfkey%';"
, 0, 0, 0
);
if( rc!=SQLITE_OK ) return rc;
rc = detectSchemaProblem(db, "foreign key is not unique",
"SELECT fkid, from_tbl "
"FROM temp.fkey2 "
"WHERE NOT EXISTS (SELECT 1 "
"FROM temp.idx WHERE tablename=to_tbl AND fkey2.cols==idx.cols"
")", pCallback
);
if( rc!=SQLITE_OK ) return rc;
return rc;
}
#define GENFKEY_ERROR 1
#define GENFKEY_DROPTRIGGER 2
#define GENFKEY_CREATETRIGGER 3
static int genfkey_create_triggers(
sqlite3 *sdb, /* Connection to read schema from */
const char *zDb, /* Name of db to read ("main", "temp") */
void *pCtx, /* Context pointer to pass to xData */
int (*xData)(void *, int, const char *)
){
const char *zSql =
"SELECT multireplace('"
"-- Triggers for foreign key mapping:\n"
"--\n"
"-- /from_readable/ REFERENCES /to_readable/\n"
"-- on delete /on_delete/\n"
"-- on update /on_update/\n"
"--\n"
/* The "BEFORE INSERT ON <referencing>" trigger. This trigger's job is to
** throw an exception if the user tries to insert a row into the
** referencing table for which there is no corresponding row in
** the referenced table.
*/
"CREATE TRIGGER /name/_insert_referencing BEFORE INSERT ON /tbl/ WHEN \n"
" /key_notnull/ AND NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n"
"BEGIN\n"
" SELECT RAISE(ABORT, ''constraint failed'');\n"
"END;\n"
/* The "BEFORE UPDATE ON <referencing>" trigger. This trigger's job
** is to throw an exception if the user tries to update a row in the
** referencing table causing it to correspond to no row in the
** referenced table.
*/
"CREATE TRIGGER /name/_update_referencing BEFORE\n"
" UPDATE OF /rkey_list/ ON /tbl/ WHEN \n"
" /key_notnull/ AND \n"
" NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n"
"BEGIN\n"
" SELECT RAISE(ABORT, ''constraint failed'');\n"
"END;\n"
/* The "BEFORE DELETE ON <referenced>" trigger. This trigger's job
** is to detect when a row is deleted from the referenced table to
** which rows in the referencing table correspond. The action taken
** depends on the value of the 'ON DELETE' clause.
*/
"CREATE TRIGGER /name/_delete_referenced BEFORE DELETE ON /ref/ WHEN\n"
" EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n"
"BEGIN\n"
" /delete_action/\n"
"END;\n"
/* The "BEFORE DELETE ON <referenced>" trigger. This trigger's job
** is to detect when the key columns of a row in the referenced table
** to which one or more rows in the referencing table correspond are
** updated. The action taken depends on the value of the 'ON UPDATE'
** clause.
*/
"CREATE TRIGGER /name/_update_referenced AFTER\n"
" UPDATE OF /fkey_list/ ON /ref/ WHEN \n"
" EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n"
"BEGIN\n"
" /update_action/\n"
"END;\n"
"'"
/* These are used in the SQL comment written above each set of triggers */
", '/from_readable/', from_tbl || '(' || sj(from_col, ', ') || ')'"
", '/to_readable/', to_tbl || '(' || sj(to_col, ', ') || ')'"
", '/on_delete/', on_delete"
", '/on_update/', on_update"
", '/name/', 'genfkey' || min(rowid)"
", '/tbl/', dq(from_tbl)"
", '/ref/', dq(to_tbl)"
", '/key_notnull/', sj('new.' || dq(from_col) || ' IS NOT NULL', ' AND ')"
", '/fkey_list/', sj(to_col, ', ')"
", '/rkey_list/', sj(from_col, ', ')"
", '/cond1/', sj(multireplace('new./from/ == /to/'"
", '/from/', dq(from_col)"
", '/to/', dq(to_col)"
"), ' AND ')"
", '/cond2/', sj(multireplace('old./to/ == /from/'"
", '/from/', dq(from_col)"
", '/to/', dq(to_col)"
"), ' AND ')"
", '/update_action/', CASE on_update "
"WHEN 'SET NULL' THEN "
"multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' "
", '/setlist/', sj(from_col||' = NULL',', ')"
", '/tbl/', dq(from_tbl)"
", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')"
")"
"WHEN 'CASCADE' THEN "
"multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' "
", '/setlist/', sj(dq(from_col)||' = new.'||dq(to_col),', ')"
", '/tbl/', dq(from_tbl)"
", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')"
")"
"ELSE "
" 'SELECT RAISE(ABORT, ''constraint failed'');'"
"END "
", '/delete_action/', CASE on_delete "
"WHEN 'SET NULL' THEN "
"multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' "
", '/setlist/', sj(from_col||' = NULL',', ')"
", '/tbl/', dq(from_tbl)"
", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')"
")"
"WHEN 'CASCADE' THEN "
"multireplace('DELETE FROM /tbl/ WHERE /where/;' "
", '/tbl/', dq(from_tbl)"
", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')"
")"
"ELSE "
" 'SELECT RAISE(ABORT, ''constraint failed'');'"
"END "
") FROM temp.fkey "
"GROUP BY from_tbl, fkid"
;
int rc;
const int enc = SQLITE_UTF8;
sqlite3 *db = 0;
GenfkeyCb cb;
cb.xData = xData;
cb.pCtx = pCtx;
UNUSED_PARAMETER(zDb);
/* Open the working database handle. */
rc = sqlite3_open(":memory:", &db);
if( rc!=SQLITE_OK ) goto genfkey_exit;
/* Create the special scalar and aggregate functions used by this program. */
sqlite3_create_function(db, "dq", 1, enc, 0, doublequote, 0, 0);
sqlite3_create_function(db, "multireplace", -1, enc, db, multireplace, 0, 0);
sqlite3_create_function(db, "sj", 2, enc, 0, 0, joinStep, joinFinalize);
/* Install the "schema" virtual table module */
installSchemaModule(db, sdb);
/* Create and populate a temp table with the information required to
** build the foreign key triggers. See function populateTempTable()
** for details.
*/
cb.eType = GENFKEY_ERROR;
rc = populateTempTable(db, &cb);
if( rc!=SQLITE_OK ) goto genfkey_exit;
/* Unless the --no-drop option was specified, generate DROP TRIGGER
** statements to drop any triggers in the database generated by a
** previous run of this program.
*/
cb.eType = GENFKEY_DROPTRIGGER;
rc = sqlite3_exec(db,
"SELECT 'DROP TRIGGER main.' || dq(triggername) || ';' FROM triggers"
,invokeCallback, (void *)&cb, 0
);
if( rc!=SQLITE_OK ) goto genfkey_exit;
/* Run the main query to create the trigger definitions. */
cb.eType = GENFKEY_CREATETRIGGER;
rc = sqlite3_exec(db, zSql, invokeCallback, (void *)&cb, 0);
if( rc!=SQLITE_OK ) goto genfkey_exit;
genfkey_exit:
sqlite3_close(db);
return rc;
}
#endif
/* End genfkey logic. */
/*************************************************************************/
/*************************************************************************/
/* /*
** If the following flag is set, then command execution stops ** If the following flag is set, then command execution stops
** at an error if we are not interactive. ** at an error if we are not interactive.
@ -926,6 +1781,62 @@ static int run_schema_dump_query(
return rc; return rc;
} }
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY)
struct GenfkeyCmd {
sqlite3 *db; /* Database handle */
struct callback_data *pCb; /* Callback data */
int isIgnoreErrors; /* True for --ignore-errors */
int isExec; /* True for --exec */
int isNoDrop; /* True for --no-drop */
int nErr; /* Number of errors seen so far */
};
typedef struct GenfkeyCmd GenfkeyCmd;
static int genfkeyParseArgs(GenfkeyCmd *p, char **azArg, int nArg){
int ii;
memset(p, 0, sizeof(GenfkeyCmd));
for(ii=0; ii<nArg; ii++){
int n = strlen30(azArg[ii]);
if( n>2 && n<10 && 0==strncmp(azArg[ii], "--no-drop", n) ){
p->isNoDrop = 1;
}else if( n>2 && n<16 && 0==strncmp(azArg[ii], "--ignore-errors", n) ){
p->isIgnoreErrors = 1;
}else if( n>2 && n<7 && 0==strncmp(azArg[ii], "--exec", n) ){
p->isExec = 1;
}else{
fprintf(stderr, "unknown option: %s\n", azArg[ii]);
return -1;
}
}
return SQLITE_OK;
}
static int genfkeyCmdCb(void *pCtx, int eType, const char *z){
GenfkeyCmd *p = (GenfkeyCmd *)pCtx;
if( eType==GENFKEY_ERROR && !p->isIgnoreErrors ){
p->nErr++;
fprintf(stderr, "%s\n", z);
}
if( p->nErr==0 && (
(eType==GENFKEY_CREATETRIGGER)
|| (eType==GENFKEY_DROPTRIGGER && !p->isNoDrop)
)){
if( p->isExec ){
sqlite3_exec(p->db, z, 0, 0, 0);
}else{
char *zCol = "sql";
callback((void *)p->pCb, 1, (char **)&z, (char **)&zCol);
}
}
return SQLITE_OK;
}
#endif
/* /*
** Text of a help message ** Text of a help message
*/ */
@ -937,6 +1848,14 @@ static char zHelp[] =
".echo ON|OFF Turn command echo on or off\n" ".echo ON|OFF Turn command echo on or off\n"
".exit Exit this program\n" ".exit Exit this program\n"
".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n" ".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n"
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY)
".genfkey ?OPTIONS? Options are:\n"
" --no-drop: Do not drop old fkey triggers.\n"
" --ignore-errors: Ignore tables with fkey errors\n"
" --exec: Execute generated SQL immediately\n"
" See file tool/genfkey.README in the source \n"
" distribution for further information.\n"
#endif
".header(s) ON|OFF Turn display of headers on or off\n" ".header(s) ON|OFF Turn display of headers on or off\n"
".help Show this message\n" ".help Show this message\n"
".import FILE TABLE Import data from FILE into TABLE\n" ".import FILE TABLE Import data from FILE into TABLE\n"
@ -1240,6 +2159,17 @@ static int do_meta_command(char *zLine, struct callback_data *p){
} }
}else }else
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY)
if( c=='g' && strncmp(azArg[0], "genfkey", n)==0 ){
GenfkeyCmd cmd;
if( 0==genfkeyParseArgs(&cmd, &azArg[1], nArg-1) ){
cmd.db = p->db;
cmd.pCb = p;
genfkey_create_triggers(p->db, "main", (void *)&cmd, genfkeyCmdCb);
}
}else
#endif
if( c=='h' && (strncmp(azArg[0], "header", n)==0 || if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){ strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
p->showHeader = booleanValue(azArg[1]); p->showHeader = booleanValue(azArg[1]);

2085
sqlite3.h

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** Internal interface definitions for SQLite.
** **
** @(#) $Id: sqliteInt.h,v 1.833 2009/02/05 16:53:43 drh Exp $ ** @(#) $Id: sqliteInt.h,v 1.848 2009/03/25 16:51:43 drh Exp $
*/ */
#ifndef _SQLITEINT_H_ #ifndef _SQLITEINT_H_
#define _SQLITEINT_H_ #define _SQLITEINT_H_
@ -444,6 +444,17 @@ extern const int sqlite3one;
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
/*
** Round up a number to the next larger multiple of 8. This is used
** to force 8-byte alignment on 64-bit architectures.
*/
#define ROUND8(x) (((x)+7)&~7)
/*
** Round down to the nearest multiple of 8
*/
#define ROUNDDOWN8(x) ((x)&~7)
/* /*
** An instance of the following structure is used to store the busy-handler ** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle. ** callback for a given sqlite handle.
@ -675,10 +686,17 @@ struct Schema {
** lookaside malloc subsystem. Each available memory allocation in ** lookaside malloc subsystem. Each available memory allocation in
** the lookaside subsystem is stored on a linked list of LookasideSlot ** the lookaside subsystem is stored on a linked list of LookasideSlot
** objects. ** objects.
**
** Lookaside allocations are only allowed for objects that are associated
** with a particular database connection. Hence, schema information cannot
** be stored in lookaside because in shared cache mode the schema information
** is shared by multiple database connections. Therefore, while parsing
** schema information, the Lookaside.bEnabled flag is cleared so that
** lookaside allocations are not used to construct the schema objects.
*/ */
struct Lookaside { struct Lookaside {
u16 sz; /* Size of each buffer in bytes */ u16 sz; /* Size of each buffer in bytes */
u8 bEnabled; /* True if use lookaside. False to ignore it */ u8 bEnabled; /* False to disable new lookaside allocations */
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
int nOut; /* Number of buffers currently checked out */ int nOut; /* Number of buffers currently checked out */
int mxOut; /* Highwater mark for nOut */ int mxOut; /* Highwater mark for nOut */
@ -807,7 +825,19 @@ struct sqlite3 {
#endif #endif
Savepoint *pSavepoint; /* List of active savepoints */ Savepoint *pSavepoint; /* List of active savepoints */
int nSavepoint; /* Number of non-transaction savepoints */ int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
/* The following variables are all protected by the STATIC_MASTER
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
*/
sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */
sqlite3 *pUnlockConnection; /* Connection to watch for unlock */
void *pUnlockArg; /* Argument to xUnlockNotify */
void (*xUnlockNotify)(void **, int); /* Unlock notify callback */
sqlite3 *pNextBlocked; /* Next in list of all blocked connections */
#endif
}; };
/* /*
@ -847,6 +877,7 @@ struct sqlite3 {
#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ #define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */
#define SQLITE_Vtab 0x00100000 /* There exists a virtual table */ #define SQLITE_Vtab 0x00100000 /* There exists a virtual table */
#define SQLITE_CommitBusy 0x00200000 /* In the process of committing */ #define SQLITE_CommitBusy 0x00200000 /* In the process of committing */
#define SQLITE_ReverseOrder 0x00400000 /* Reverse unordered SELECTs */
/* /*
** Possible values for the sqlite.magic field. ** Possible values for the sqlite.magic field.
@ -886,6 +917,7 @@ struct FuncDef {
#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ #define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ #define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ #define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */
#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */
/* /*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@ -1080,7 +1112,7 @@ struct CollSeq {
** of a SELECT statement. ** of a SELECT statement.
*/ */
struct Table { struct Table {
sqlite3 *db; /* Associated database connection. Might be NULL. */ sqlite3 *dbMem; /* DB connection used for lookaside allocations. */
char *zName; /* Name of the table or view */ char *zName; /* Name of the table or view */
int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */
int nCol; /* Number of columns in this table */ int nCol; /* Number of columns in this table */
@ -1091,7 +1123,6 @@ struct Table {
u16 nRef; /* Number of pointers to this Table */ u16 nRef; /* Number of pointers to this Table */
u8 tabFlags; /* Mask of TF_* values */ u8 tabFlags; /* Mask of TF_* values */
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
Trigger *pTrigger; /* List of SQL triggers on this table */
FKey *pFKey; /* Linked list of all foreign keys in this table */ FKey *pFKey; /* Linked list of all foreign keys in this table */
char *zColAff; /* String defining the affinity of each column */ char *zColAff; /* String defining the affinity of each column */
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
@ -1106,6 +1137,7 @@ struct Table {
int nModuleArg; /* Number of arguments to the module */ int nModuleArg; /* Number of arguments to the module */
char **azModuleArg; /* Text of all module args. [0] is module name */ char **azModuleArg; /* Text of all module args. [0] is module name */
#endif #endif
Trigger *pTrigger; /* List of triggers stored in pSchema */
Schema *pSchema; /* Schema that contains this table */ Schema *pSchema; /* Schema that contains this table */
Table *pNextZombie; /* Next on the Parse.pZombieTab list */ Table *pNextZombie; /* Next on the Parse.pZombieTab list */
}; };
@ -1363,19 +1395,27 @@ struct AggInfo {
** Each node of an expression in the parse tree is an instance ** Each node of an expression in the parse tree is an instance
** of this structure. ** of this structure.
** **
** Expr.op is the opcode. The integer parser token codes are reused ** Expr.op is the opcode. The integer parser token codes are reused
** as opcodes here. For example, the parser defines TK_GE to be an integer ** as opcodes here. For example, the parser defines TK_GE to be an integer
** code representing the ">=" operator. This same integer code is reused ** code representing the ">=" operator. This same integer code is reused
** to represent the greater-than-or-equal-to operator in the expression ** to represent the greater-than-or-equal-to operator in the expression
** tree. ** tree.
** **
** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list ** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
** of argument if the expression is a function. ** or TK_STRING), then Expr.token contains the text of the SQL literal. If
** the expression is a variable (TK_VARIABLE), then Expr.token contains the
** variable name. Finally, if the expression is an SQL function (TK_FUNCTION),
** then Expr.token contains the name of the function.
** **
** Expr.token is the operator token for this node. For some expressions ** Expr.pRight and Expr.pLeft are the left and right subexpressions of a
** that have subexpressions, Expr.token can be the complete text that gave ** binary operator. Either or both may be NULL.
** rise to the Expr. In the latter case, the token is marked as being **
** a compound token. ** Expr.x.pList is a list of arguments if the expression is an SQL function,
** a CASE expression or an IN expression of the form "<lhs> IN (<y>, <z>...)".
** Expr.x.pSelect is used if the expression is a sub-select or an expression of
** the form "<lhs> IN (SELECT ...)". If the EP_xIsSelect bit is set in the
** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is
** valid.
** **
** An expression of the form ID or ID.ID refers to a column in a table. ** An expression of the form ID or ID.ID refers to a column in a table.
** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is ** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is
@ -1385,10 +1425,9 @@ struct AggInfo {
** value is also stored in the Expr.iAgg column in the aggregate so that ** value is also stored in the Expr.iAgg column in the aggregate so that
** it can be accessed after all aggregates are computed. ** it can be accessed after all aggregates are computed.
** **
** If the expression is a function, the Expr.iTable is an integer code ** If the expression is an unbound variable marker (a question mark
** representing which function. If the expression is an unbound variable ** character '?' in the original SQL) then the Expr.iTable holds the index
** marker (a question mark character '?' in the original SQL) then the ** number for that variable.
** Expr.iTable holds the index number for that variable.
** **
** If the expression is a subquery then Expr.iColumn holds an integer ** If the expression is a subquery then Expr.iColumn holds an integer
** register number containing the result of the subquery. If the ** register number containing the result of the subquery. If the
@ -1396,32 +1435,63 @@ struct AggInfo {
** gives a different answer at different times during statement processing ** gives a different answer at different times during statement processing
** then iTable is the address of a subroutine that computes the subquery. ** then iTable is the address of a subroutine that computes the subquery.
** **
** The Expr.pSelect field points to a SELECT statement. The SELECT might
** be the right operand of an IN operator. Or, if a scalar SELECT appears
** in an expression the opcode is TK_SELECT and Expr.pSelect is the only
** operand.
**
** If the Expr is of type OP_Column, and the table it is selecting from ** If the Expr is of type OP_Column, and the table it is selecting from
** is a disk table or the "old.*" pseudo-table, then pTab points to the ** is a disk table or the "old.*" pseudo-table, then pTab points to the
** corresponding table definition. ** corresponding table definition.
**
** ALLOCATION NOTES:
**
** Expr structures may be stored as part of the in-memory database schema,
** for example as part of trigger, view or table definitions. In this case,
** the amount of memory consumed by complex expressions may be significant.
** For this reason, less than sizeof(Expr) bytes may be allocated for some
** Expr structs stored as part of the in-memory database schema.
**
** If the EP_Reduced flag is set in Expr.flags, then only EXPR_REDUCEDSIZE
** bytes of space are allocated for the expression structure. This is enough
** space to store all fields up to and including the "Token span;" field.
**
** If the EP_TokenOnly flag is set in Expr.flags, then only EXPR_TOKENONLYSIZE
** bytes of space are allocated for the expression structure. This is enough
** space to store all fields up to and including the "Token token;" field.
*/ */
struct Expr { struct Expr {
u8 op; /* Operation performed by this node */ u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */ char affinity; /* The affinity of the column or 0 if not a column */
u16 flags; /* Various flags. See below */ VVA_ONLY(u8 vvaFlags;) /* Flags used for VV&A only. EVVA_* below. */
CollSeq *pColl; /* The collation type of the column or 0 */ u16 flags; /* Various flags. EP_* See below */
Expr *pLeft, *pRight; /* Left and right subnodes */
ExprList *pList; /* A list of expressions used as function arguments
** or in "<expr> IN (<expr-list)" */
Token token; /* An operand token */ Token token; /* An operand token */
/* If the EP_TokenOnly flag is set in the Expr.flags mask, then no
** space is allocated for the fields below this point. An attempt to
** access them will result in a segfault or malfunction.
*********************************************************************/
Token span; /* Complete text of the expression */ Token span; /* Complete text of the expression */
/* If the EP_SpanOnly flag is set in the Expr.flags mask, then no
** space is allocated for the fields below this point. An attempt to
** access them will result in a segfault or malfunction.
*********************************************************************/
Expr *pLeft; /* Left subnode */
Expr *pRight; /* Right subnode */
union {
ExprList *pList; /* Function arguments or in "<expr> IN (<expr-list)" */
Select *pSelect; /* Used for sub-selects and "<expr> IN (<select>)" */
} x;
CollSeq *pColl; /* The collation type of the column or 0 */
/* If the EP_Reduced flag is set in the Expr.flags mask, then no
** space is allocated for the fields below this point. An attempt to
** access them will result in a segfault or malfunction.
*********************************************************************/
int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the
** iColumn-th field of the iTable-th table. */ ** iColumn-th field of the iTable-th table. */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
int iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ int iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
int iRightJoinTable; /* If EP_FromJoin, the right table of the join */ int iRightJoinTable; /* If EP_FromJoin, the right table of the join */
Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */
Table *pTab; /* Table for TK_COLUMN expressions. */ Table *pTab; /* Table for TK_COLUMN expressions. */
#if SQLITE_MAX_EXPR_DEPTH>0 #if SQLITE_MAX_EXPR_DEPTH>0
int nHeight; /* Height of the tree headed by this node */ int nHeight; /* Height of the tree headed by this node */
@ -1443,6 +1513,21 @@ struct Expr {
#define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */ #define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */
#define EP_FixedDest 0x0400 /* Result needed in a specific register */ #define EP_FixedDest 0x0400 /* Result needed in a specific register */
#define EP_IntValue 0x0800 /* Integer value contained in iTable */ #define EP_IntValue 0x0800 /* Integer value contained in iTable */
#define EP_xIsSelect 0x1000 /* x.pSelect is valid (otherwise x.pList is) */
#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
#define EP_SpanOnly 0x8000 /* Expr struct is EXPR_SPANONLYSIZE bytes only */
/*
** The following are the meanings of bits in the Expr.vvaFlags field.
** This information is only used when SQLite is compiled with
** SQLITE_DEBUG defined.
*/
#ifndef NDEBUG
#define EVVA_ReadOnlyToken 0x01 /* Expr.token.z is read-only */
#endif
/* /*
** These macros can be used to test, set, or clear bits in the ** These macros can be used to test, set, or clear bits in the
** Expr.flags field. ** Expr.flags field.
@ -1452,6 +1537,24 @@ struct Expr {
#define ExprSetProperty(E,P) (E)->flags|=(P) #define ExprSetProperty(E,P) (E)->flags|=(P)
#define ExprClearProperty(E,P) (E)->flags&=~(P) #define ExprClearProperty(E,P) (E)->flags&=~(P)
/*
** Macros to determine the number of bytes required by a normal Expr
** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
** and an Expr struct with the EP_TokenOnly flag set.
*/
#define EXPR_FULLSIZE sizeof(Expr)
#define EXPR_REDUCEDSIZE offsetof(Expr,iTable)
#define EXPR_TOKENONLYSIZE offsetof(Expr,span)
#define EXPR_SPANONLYSIZE offsetof(Expr,pLeft)
/*
** Flags passed to the sqlite3ExprDup() function. See the header comment
** above sqlite3ExprDup() for details.
*/
#define EXPRDUP_REDUCE 0x0001
#define EXPRDUP_SPAN 0x0002
#define EXPRDUP_DISTINCTSPAN 0x0004
/* /*
** A list of expressions. Each expression may optionally have a ** A list of expressions. Each expression may optionally have a
** name. An expr/name combination can be used in several ways, such ** name. An expr/name combination can be used in several ways, such
@ -2237,7 +2340,7 @@ void sqlite3SetString(char **, sqlite3*, const char*, ...);
void sqlite3ErrorMsg(Parse*, const char*, ...); void sqlite3ErrorMsg(Parse*, const char*, ...);
void sqlite3ErrorClear(Parse*); void sqlite3ErrorClear(Parse*);
void sqlite3Dequote(char*); void sqlite3Dequote(char*);
void sqlite3DequoteExpr(sqlite3*, Expr*); void sqlite3DequoteExpr(Expr*);
int sqlite3KeywordCode(const unsigned char*, int); int sqlite3KeywordCode(const unsigned char*, int);
int sqlite3RunParser(Parse*, const char*, char **); int sqlite3RunParser(Parse*, const char*, char **);
void sqlite3FinishCoding(Parse*); void sqlite3FinishCoding(Parse*);
@ -2379,12 +2482,12 @@ void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
void sqlite3BeginWriteOperation(Parse*, int, int); void sqlite3BeginWriteOperation(Parse*, int, int);
Expr *sqlite3ExprDup(sqlite3*,Expr*); Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
void sqlite3TokenCopy(sqlite3*,Token*, Token*); void sqlite3TokenCopy(sqlite3*,Token*,const Token*);
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*); ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*); SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
IdList *sqlite3IdListDup(sqlite3*,IdList*); IdList *sqlite3IdListDup(sqlite3*,IdList*);
Select *sqlite3SelectDup(sqlite3*,Select*); Select *sqlite3SelectDup(sqlite3*,Select*,int);
void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*); void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int); FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
void sqlite3RegisterBuiltinFunctions(sqlite3*); void sqlite3RegisterBuiltinFunctions(sqlite3*);
@ -2411,9 +2514,10 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*);
void sqlite3DropTrigger(Parse*, SrcList*, int); void sqlite3DropTrigger(Parse*, SrcList*, int);
void sqlite3DropTriggerPtr(Parse*, Trigger*); void sqlite3DropTriggerPtr(Parse*, Trigger*);
int sqlite3TriggersExist(Table*, int, ExprList*); Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask);
int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, Trigger *sqlite3TriggerList(Parse *, Table *);
int, int, u32*, u32*); int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
int, int, int, int, u32*, u32*);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
@ -2428,7 +2532,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
# define sqlite3DeleteTrigger(A,B) # define sqlite3DeleteTrigger(A,B)
# define sqlite3DropTriggerPtr(A,B) # define sqlite3DropTriggerPtr(A,B)
# define sqlite3UnlinkAndDeleteTrigger(A,B,C) # define sqlite3UnlinkAndDeleteTrigger(A,B,C)
# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K) 0 # define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K,L) 0
#endif #endif
int sqlite3JoinType(Parse*, Token*, Token*, Token*); int sqlite3JoinType(Parse*, Token*, Token*, Token*);
@ -2686,6 +2790,17 @@ int sqlite3IsMemJournal(sqlite3_file *);
u32 sqlite3Get4byte(const u8*); u32 sqlite3Get4byte(const u8*);
void sqlite3Put4byte(u8*, u32); void sqlite3Put4byte(u8*, u32);
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
void sqlite3ConnectionBlocked(sqlite3 *, sqlite3 *);
void sqlite3ConnectionUnlocked(sqlite3 *db);
void sqlite3ConnectionClosed(sqlite3 *db);
#else
#define sqlite3ConnectionBlocked(x,y)
#define sqlite3ConnectionUnlocked(x)
#define sqlite3ConnectionClosed(x)
#endif
#ifdef SQLITE_SSE #ifdef SQLITE_SSE
#include "sseInt.h" #include "sseInt.h"
#endif #endif

View file

@ -15,7 +15,7 @@
** individual tokens and sends those tokens one-by-one over to the ** individual tokens and sends those tokens one-by-one over to the
** parser for analysis. ** parser for analysis.
** **
** $Id: tokenize.c,v 1.153 2009/01/20 16:53:41 danielk1977 Exp $ ** $Id: tokenize.c,v 1.155 2009/03/31 03:41:57 shane Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <stdlib.h> #include <stdlib.h>
@ -381,14 +381,17 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
** error message. ** error message.
*/ */
int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
int nErr = 0; int nErr = 0; /* Number of errors encountered */
int i; int i; /* Loop counter */
void *pEngine; void *pEngine; /* The LEMON-generated LALR(1) parser */
int tokenType; int tokenType; /* type of the next token */
int lastTokenParsed = -1; int lastTokenParsed = -1; /* type of the previous token */
sqlite3 *db = pParse->db; u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */
int mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; 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 ){ if( db->activeVdbeCnt==0 ){
db->u1.isInterrupted = 0; db->u1.isInterrupted = 0;
} }
@ -408,6 +411,8 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
assert( pParse->nVarExpr==0 ); assert( pParse->nVarExpr==0 );
assert( pParse->nVarExprAlloc==0 ); assert( pParse->nVarExprAlloc==0 );
assert( pParse->apVarExpr==0 ); assert( pParse->apVarExpr==0 );
enableLookaside = db->lookaside.bEnabled;
if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
while( !db->mallocFailed && zSql[i]!=0 ){ while( !db->mallocFailed && zSql[i]!=0 ){
assert( i>=0 ); assert( i>=0 );
pParse->sLastToken.z = (u8*)&zSql[i]; pParse->sLastToken.z = (u8*)&zSql[i];
@ -462,6 +467,7 @@ abort_parse:
); );
#endif /* YYDEBUG */ #endif /* YYDEBUG */
sqlite3ParserFree(pEngine, sqlite3_free); sqlite3ParserFree(pEngine, sqlite3_free);
db->lookaside.bEnabled = enableLookaside;
if( db->mallocFailed ){ if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM; pParse->rc = SQLITE_NOMEM;
} }

150
trigger.c
View file

@ -10,7 +10,7 @@
************************************************************************* *************************************************************************
** **
** **
** $Id: trigger.c,v 1.133 2008/12/26 07:56:39 danielk1977 Exp $ ** $Id: trigger.c,v 1.135 2009/02/28 10:47:42 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -33,6 +33,30 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
} }
} }
/*
** Given table pTab, return a list of all the triggers attached to
** the table. The list is connected by Trigger.pNext pointers.
*/
Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
Schema * const pTmpSchema = pParse->db->aDb[1].pSchema;
Trigger *pList = 0; /* List of triggers to return */
if( pTmpSchema!=pTab->pSchema ){
HashElem *p;
for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
Trigger *pTrig = (Trigger *)sqliteHashData(p);
if( pTrig->pTabSchema==pTab->pSchema
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
){
pTrig->pNext = (pList ? pList : pTab->pTrigger);
pList = pTrig;
}
}
}
return (pList ? pList : pTab->pTrigger);
}
/* /*
** This is called by the parser when it sees a CREATE TRIGGER statement ** This is called by the parser when it sees a CREATE TRIGGER statement
** up to the point of the BEGIN before the trigger actions. A Trigger ** up to the point of the BEGIN before the trigger actions. A Trigger
@ -182,7 +206,7 @@ void sqlite3BeginTrigger(
pTrigger->pTabSchema = pTab->pSchema; pTrigger->pTabSchema = pTab->pSchema;
pTrigger->op = (u8)op; pTrigger->op = (u8)op;
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
pTrigger->pWhen = sqlite3ExprDup(db, pWhen); pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
pTrigger->pColumns = sqlite3IdListDup(db, pColumns); pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
sqlite3TokenCopy(db, &pTrigger->nameToken,pName); sqlite3TokenCopy(db, &pTrigger->nameToken,pName);
assert( pParse->pNewTrigger==0 ); assert( pParse->pNewTrigger==0 );
@ -209,14 +233,16 @@ void sqlite3FinishTrigger(
TriggerStep *pStepList, /* The triggered program */ TriggerStep *pStepList, /* The triggered program */
Token *pAll /* Token that describes the complete CREATE TRIGGER */ Token *pAll /* Token that describes the complete CREATE TRIGGER */
){ ){
Trigger *pTrig = 0; /* The trigger whose construction is finishing up */ Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */
sqlite3 *db = pParse->db; /* The database */ char *zName; /* Name of trigger */
sqlite3 *db = pParse->db; /* The database */
DbFixer sFix; DbFixer sFix;
int iDb; /* Database containing the trigger */ int iDb; /* Database containing the trigger */
pTrig = pParse->pNewTrigger; pTrig = pParse->pNewTrigger;
pParse->pNewTrigger = 0; pParse->pNewTrigger = 0;
if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup; if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
zName = pTrig->name;
iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
pTrig->step_list = pStepList; pTrig->step_list = pStepList;
while( pStepList ){ while( pStepList ){
@ -242,32 +268,29 @@ void sqlite3FinishTrigger(
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
sqlite3NestedParse(pParse, sqlite3NestedParse(pParse,
"INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrig->name, db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName,
pTrig->table, z); pTrig->table, z);
sqlite3DbFree(db, z); sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb); sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf( sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf(
db, "type='trigger' AND name='%q'", pTrig->name), P4_DYNAMIC db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC
); );
} }
if( db->init.busy ){ if( db->init.busy ){
int n; Trigger *pLink = pTrig;
Table *pTab; Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
Trigger *pDel; pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig);
pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash, if( pTrig ){
pTrig->name, sqlite3Strlen30(pTrig->name), pTrig);
if( pDel ){
assert( pDel==pTrig );
db->mallocFailed = 1; db->mallocFailed = 1;
goto triggerfinish_cleanup; }else if( pLink->pSchema==pLink->pTabSchema ){
Table *pTab;
int n = sqlite3Strlen30(pLink->table) + 1;
pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table, n);
assert( pTab!=0 );
pLink->pNext = pTab->pTrigger;
pTab->pTrigger = pLink;
} }
n = sqlite3Strlen30(pTrig->table) + 1;
pTab = sqlite3HashFind(&pTrig->pTabSchema->tblHash, pTrig->table, n);
assert( pTab!=0 );
pTrig->pNext = pTab->pTrigger;
pTab->pTrigger = pTrig;
pTrig = 0;
} }
triggerfinish_cleanup: triggerfinish_cleanup:
@ -292,17 +315,17 @@ static void sqlitePersistTriggerStep(sqlite3 *db, TriggerStep *p){
p->target.dyn = 1; p->target.dyn = 1;
} }
if( p->pSelect ){ if( p->pSelect ){
Select *pNew = sqlite3SelectDup(db, p->pSelect); Select *pNew = sqlite3SelectDup(db, p->pSelect, 1);
sqlite3SelectDelete(db, p->pSelect); sqlite3SelectDelete(db, p->pSelect);
p->pSelect = pNew; p->pSelect = pNew;
} }
if( p->pWhere ){ if( p->pWhere ){
Expr *pNew = sqlite3ExprDup(db, p->pWhere); Expr *pNew = sqlite3ExprDup(db, p->pWhere, EXPRDUP_REDUCE);
sqlite3ExprDelete(db, p->pWhere); sqlite3ExprDelete(db, p->pWhere);
p->pWhere = pNew; p->pWhere = pNew;
} }
if( p->pExprList ){ if( p->pExprList ){
ExprList *pNew = sqlite3ExprListDup(db, p->pExprList); ExprList *pNew = sqlite3ExprListDup(db, p->pExprList, 1);
sqlite3ExprListDelete(db, p->pExprList); sqlite3ExprListDelete(db, p->pExprList);
p->pExprList = pNew; p->pExprList = pNew;
} }
@ -546,6 +569,9 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
sqlite3ChangeCookie(pParse, iDb); sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_Close, 0, 0); sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0); sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0);
if( pParse->nMem<3 ){
pParse->nMem = 3;
}
} }
} }
@ -553,25 +579,15 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
** Remove a trigger from the hash tables of the sqlite* pointer. ** Remove a trigger from the hash tables of the sqlite* pointer.
*/ */
void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
Hash *pHash = &(db->aDb[iDb].pSchema->trigHash);
Trigger *pTrigger; Trigger *pTrigger;
int nName = sqlite3Strlen30(zName); pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0);
pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash),
zName, nName, 0);
if( pTrigger ){ if( pTrigger ){
Table *pTable = tableOfTrigger(pTrigger); if( pTrigger->pSchema==pTrigger->pTabSchema ){
assert( pTable!=0 ); Table *pTab = tableOfTrigger(pTrigger);
if( pTable->pTrigger == pTrigger ){ Trigger **pp;
pTable->pTrigger = pTrigger->pNext; for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
}else{ *pp = (*pp)->pNext;
Trigger *cc = pTable->pTrigger;
while( cc ){
if( cc->pNext == pTrigger ){
cc->pNext = cc->pNext->pNext;
break;
}
cc = cc->pNext;
}
assert(cc);
} }
sqlite3DeleteTrigger(db, pTrigger); sqlite3DeleteTrigger(db, pTrigger);
db->flags |= SQLITE_InternChanges; db->flags |= SQLITE_InternChanges;
@ -597,30 +613,31 @@ static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
} }
/* /*
** Return a bit vector to indicate what kind of triggers exist for operation ** Return a list of all triggers on table pTab if there exists at least
** "op" on table pTab. If pChanges is not NULL then it is a list of columns ** one trigger that must be fired when an operation of type 'op' is
** that are being updated. Triggers only match if the ON clause of the ** performed on the table, and, if that operation is an UPDATE, if at
** trigger definition overlaps the set of columns being updated. ** least one of the columns in pChanges is being modified.
**
** The returned bit vector is some combination of TRIGGER_BEFORE and
** TRIGGER_AFTER.
*/ */
int sqlite3TriggersExist( Trigger *sqlite3TriggersExist(
Parse *pParse, /* Parse context */
Table *pTab, /* The table the contains the triggers */ Table *pTab, /* The table the contains the triggers */
int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
ExprList *pChanges /* Columns that change in an UPDATE statement */ ExprList *pChanges, /* Columns that change in an UPDATE statement */
int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
){ ){
Trigger *pTrigger;
int mask = 0; int mask = 0;
Trigger *pList = sqlite3TriggerList(pParse, pTab);
pTrigger = IsVirtual(pTab) ? 0 : pTab->pTrigger; Trigger *p;
while( pTrigger ){ assert( pList==0 || IsVirtual(pTab)==0 );
if( pTrigger->op==op && checkColumnOverLap(pTrigger->pColumns, pChanges) ){ for(p=pList; p; p=p->pNext){
mask |= pTrigger->tr_tm; if( p->op==op && checkColumnOverLap(p->pColumns, pChanges) ){
mask |= p->tr_tm;
} }
pTrigger = pTrigger->pNext;
} }
return mask; if( pMask ){
*pMask = mask;
}
return (mask ? pList : 0);
} }
/* /*
@ -677,7 +694,7 @@ static int codeTriggerProgram(
pParse->trigStack->orconf = orconf; pParse->trigStack->orconf = orconf;
switch( pTriggerStep->op ){ switch( pTriggerStep->op ){
case TK_SELECT: { case TK_SELECT: {
Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect); Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0);
if( ss ){ if( ss ){
SelectDest dest; SelectDest dest;
@ -692,8 +709,8 @@ static int codeTriggerProgram(
pSrc = targetSrcList(pParse, pTriggerStep); pSrc = targetSrcList(pParse, pTriggerStep);
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
sqlite3Update(pParse, pSrc, sqlite3Update(pParse, pSrc,
sqlite3ExprListDup(db, pTriggerStep->pExprList), sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
sqlite3ExprDup(db, pTriggerStep->pWhere), orconf); sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf);
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break; break;
} }
@ -702,8 +719,8 @@ static int codeTriggerProgram(
pSrc = targetSrcList(pParse, pTriggerStep); pSrc = targetSrcList(pParse, pTriggerStep);
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
sqlite3Insert(pParse, pSrc, sqlite3Insert(pParse, pSrc,
sqlite3ExprListDup(db, pTriggerStep->pExprList), sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
sqlite3SelectDup(db, pTriggerStep->pSelect), sqlite3SelectDup(db, pTriggerStep->pSelect, 0),
sqlite3IdListDup(db, pTriggerStep->pIdList), orconf); sqlite3IdListDup(db, pTriggerStep->pIdList), orconf);
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break; break;
@ -713,7 +730,7 @@ static int codeTriggerProgram(
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
pSrc = targetSrcList(pParse, pTriggerStep); pSrc = targetSrcList(pParse, pTriggerStep);
sqlite3DeleteFrom(pParse, pSrc, sqlite3DeleteFrom(pParse, pSrc,
sqlite3ExprDup(db, pTriggerStep->pWhere)); sqlite3ExprDup(db, pTriggerStep->pWhere, 0));
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break; break;
} }
@ -757,6 +774,7 @@ static int codeTriggerProgram(
*/ */
int sqlite3CodeRowTrigger( int sqlite3CodeRowTrigger(
Parse *pParse, /* Parse context */ Parse *pParse, /* Parse context */
Trigger *pTrigger, /* List of triggers on table pTab */
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */
int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
@ -780,7 +798,7 @@ int sqlite3CodeRowTrigger(
assert(newIdx != -1 || oldIdx != -1); assert(newIdx != -1 || oldIdx != -1);
for(p=pTab->pTrigger; p; p=p->pNext){ for(p=pTrigger; p; p=p->pNext){
int fire_this = 0; int fire_this = 0;
/* Determine whether we should code this trigger */ /* Determine whether we should code this trigger */
@ -830,7 +848,7 @@ int sqlite3CodeRowTrigger(
/* code the WHEN clause */ /* code the WHEN clause */
endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
whenExpr = sqlite3ExprDup(db, p->pWhen); whenExpr = sqlite3ExprDup(db, p->pWhen, 0);
if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){ if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){
pParse->trigStack = trigStackEntry.pNext; pParse->trigStack = trigStackEntry.pNext;
sqlite3ExprDelete(db, whenExpr); sqlite3ExprDelete(db, whenExpr);

View file

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle UPDATE statements. ** to handle UPDATE statements.
** **
** $Id: update.c,v 1.191 2008/12/23 23:56:22 drh Exp $ ** $Id: update.c,v 1.196 2009/02/28 10:47:42 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -107,7 +107,7 @@ void sqlite3Update(
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
int isView; /* Trying to update a view */ int isView; /* Trying to update a view */
int triggers_exist = 0; /* True if any row triggers exist */ Trigger *pTrigger; /* List of triggers on pTab, if required */
#endif #endif
int iBeginAfterTrigger = 0; /* Address of after trigger program */ int iBeginAfterTrigger = 0; /* Address of after trigger program */
int iEndAfterTrigger = 0; /* Exit of after trigger program */ int iEndAfterTrigger = 0; /* Exit of after trigger program */
@ -143,10 +143,10 @@ void sqlite3Update(
** updated is a view ** updated is a view
*/ */
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
triggers_exist = sqlite3TriggersExist(pTab, TK_UPDATE, pChanges); pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0);
isView = pTab->pSelect!=0; isView = pTab->pSelect!=0;
#else #else
# define triggers_exist 0 # define pTrigger 0
# define isView 0 # define isView 0
#endif #endif
#ifdef SQLITE_OMIT_VIEW #ifdef SQLITE_OMIT_VIEW
@ -154,7 +154,7 @@ void sqlite3Update(
# define isView 0 # define isView 0
#endif #endif
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
goto update_cleanup; goto update_cleanup;
} }
if( sqlite3ViewGetColumnNames(pParse, pTab) ){ if( sqlite3ViewGetColumnNames(pParse, pTab) ){
@ -167,7 +167,7 @@ void sqlite3Update(
/* If there are FOR EACH ROW triggers, allocate cursors for the /* If there are FOR EACH ROW triggers, allocate cursors for the
** special OLD and NEW tables ** special OLD and NEW tables
*/ */
if( triggers_exist ){ if( pTrigger ){
newIdx = pParse->nTab++; newIdx = pParse->nTab++;
oldIdx = pParse->nTab++; oldIdx = pParse->nTab++;
} }
@ -299,27 +299,27 @@ void sqlite3Update(
/* Generate the code for triggers. /* Generate the code for triggers.
*/ */
if( triggers_exist ){ if( pTrigger ){
int iGoto; int iGoto;
/* Create pseudo-tables for NEW and OLD /* Create pseudo-tables for NEW and OLD
*/ */
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
sqlite3VdbeAddOp2(v, OP_OpenPseudo, oldIdx, 0); sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
addr = sqlite3VdbeMakeLabel(v); addr = sqlite3VdbeMakeLabel(v);
iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){ TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr,
&old_col_mask, &new_col_mask) ){
goto update_cleanup; goto update_cleanup;
} }
iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){ TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr,
&old_col_mask, &new_col_mask) ){
goto update_cleanup; goto update_cleanup;
} }
iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
@ -399,7 +399,7 @@ void sqlite3Update(
} }
/* Jump back to this point if a trigger encounters an IGNORE constraint. */ /* Jump back to this point if a trigger encounters an IGNORE constraint. */
if( triggers_exist ){ if( pTrigger ){
sqlite3VdbeResolveLabel(v, addr); sqlite3VdbeResolveLabel(v, addr);
} }
@ -412,7 +412,7 @@ void sqlite3Update(
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
} }
if( triggers_exist ){ if( pTrigger ){
int regRowid; int regRowid;
int regRow; int regRow;
int regCols; int regCols;
@ -541,7 +541,7 @@ void sqlite3Update(
/* If there are triggers, close all the cursors after each iteration /* If there are triggers, close all the cursors after each iteration
** through the loop. The fire the after triggers. ** through the loop. The fire the after triggers.
*/ */
if( triggers_exist ){ if( pTrigger ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
sqlite3VdbeJumpHere(v, iEndAfterTrigger); sqlite3VdbeJumpHere(v, iEndAfterTrigger);
} }
@ -559,7 +559,7 @@ void sqlite3Update(
} }
} }
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
if( triggers_exist ){ if( pTrigger ){
sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0); sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0);
sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0); sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0);
} }
@ -633,12 +633,12 @@ static void updateVirtualTable(
sqlite3CreateIdExpr(pParse, "_rowid_"), 0); sqlite3CreateIdExpr(pParse, "_rowid_"), 0);
if( pRowid ){ if( pRowid ){
pEList = sqlite3ExprListAppend(pParse, pEList, pEList = sqlite3ExprListAppend(pParse, pEList,
sqlite3ExprDup(db, pRowid), 0); sqlite3ExprDup(db, pRowid, 0), 0);
} }
assert( pTab->iPKey<0 ); assert( pTab->iPKey<0 );
for(i=0; i<pTab->nCol; i++){ for(i=0; i<pTab->nCol; i++){
if( aXRef[i]>=0 ){ if( aXRef[i]>=0 ){
pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr); pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
}else{ }else{
pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName); pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName);
} }

4
utf.c
View file

@ -12,7 +12,7 @@
** This file contains routines used to translate between UTF-8, ** This file contains routines used to translate between UTF-8,
** UTF-16, UTF-16BE, and UTF-16LE. ** UTF-16, UTF-16BE, and UTF-16LE.
** **
** $Id: utf.c,v 1.70 2008/12/10 22:30:25 shane Exp $ ** $Id: utf.c,v 1.71 2009/03/31 03:41:57 shane Exp $
** **
** Notes on UTF-8: ** Notes on UTF-8:
** **
@ -417,7 +417,7 @@ int sqlite3Utf8To8(unsigned char *zIn){
} }
} }
*zOut = 0; *zOut = 0;
return zOut - zStart; return (int)(zOut - zStart);
} }
#endif #endif

10
util.c
View file

@ -14,7 +14,7 @@
** This file contains functions for allocating memory, comparing ** This file contains functions for allocating memory, comparing
** strings, and stuff like that. ** strings, and stuff like that.
** **
** $Id: util.c,v 1.248 2009/02/04 03:59:25 shane Exp $ ** $Id: util.c,v 1.249 2009/03/01 22:29:20 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <stdarg.h> #include <stdarg.h>
@ -694,7 +694,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
/* a: p2<<28 | p4<<14 | p6 (unmasked) */ /* a: p2<<28 | p4<<14 | p6 (unmasked) */
if (!(a&0x80)) if (!(a&0x80))
{ {
a &= (0x7f<<28)|(0x7f<<14)|(0x7f); a &= (0x1f<<28)|(0x7f<<14)|(0x7f);
b &= (0x7f<<14)|(0x7f); b &= (0x7f<<14)|(0x7f);
b = b<<7; b = b<<7;
a |= b; a |= b;
@ -711,7 +711,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
/* b: p3<<28 | p5<<14 | p7 (unmasked) */ /* b: p3<<28 | p5<<14 | p7 (unmasked) */
if (!(b&0x80)) if (!(b&0x80))
{ {
b &= (0x7f<<28)|(0x7f<<14)|(0x7f); b &= (0x1f<<28)|(0x7f<<14)|(0x7f);
/* moved CSE2 up */ /* moved CSE2 up */
/* a &= (0x7f<<14)|(0x7f); */ /* a &= (0x7f<<14)|(0x7f); */
a = a<<7; a = a<<7;
@ -806,8 +806,8 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
/* a: p0<<28 | p2<<14 | p4 (unmasked) */ /* a: p0<<28 | p2<<14 | p4 (unmasked) */
if (!(a&0x80)) if (!(a&0x80))
{ {
a &= (0x7f<<28)|(0x7f<<14)|(0x7f); a &= (0x1f<<28)|(0x7f<<14)|(0x7f);
b &= (0x7f<<28)|(0x7f<<14)|(0x7f); b &= (0x1f<<28)|(0x7f<<14)|(0x7f);
b = b<<7; b = b<<7;
*v = a | b; *v = a | b;
return 5; return 5;

294
vdbe.c
View file

@ -43,7 +43,7 @@
** in this file for details. If in doubt, do not deviate from existing ** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code. ** commenting and indentation practices when changing or adding code.
** **
** $Id: vdbe.c,v 1.817 2009/02/16 17:55:47 shane Exp $ ** $Id: vdbe.c,v 1.828 2009/03/23 17:11:27 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "vdbeInt.h" #include "vdbeInt.h"
@ -187,7 +187,7 @@ int sqlite3VdbeOpcodeHasProperty(int opcode, int mask){
static VdbeCursor *allocateCursor( static VdbeCursor *allocateCursor(
Vdbe *p, /* The virtual machine */ Vdbe *p, /* The virtual machine */
int iCur, /* Index of the new VdbeCursor */ int iCur, /* Index of the new VdbeCursor */
Op *pOp, /* */ int nField, /* Number of fields in the table or index */
int iDb, /* When database the cursor belongs to, or -1 */ int iDb, /* When database the cursor belongs to, or -1 */
int isBtreeCursor /* */ int isBtreeCursor /* */
){ ){
@ -213,15 +213,6 @@ static VdbeCursor *allocateCursor(
int nByte; int nByte;
VdbeCursor *pCx = 0; VdbeCursor *pCx = 0;
/* If the opcode of pOp is OP_SetNumColumns, then pOp->p2 contains
** the number of fields in the records contained in the table or
** index being opened. Use this to reserve space for the
** VdbeCursor.aType[] array.
*/
int nField = 0;
if( pOp->opcode==OP_SetNumColumns || pOp->opcode==OP_OpenEphemeral ){
nField = pOp->p2;
}
nByte = nByte =
sizeof(VdbeCursor) + sizeof(VdbeCursor) +
(isBtreeCursor?sqlite3BtreeCursorSize():0) + (isBtreeCursor?sqlite3BtreeCursorSize():0) +
@ -563,7 +554,7 @@ int sqlite3VdbeExec(
Mem *pOut = 0; /* Output operand */ Mem *pOut = 0; /* Output operand */
u8 opProperty; u8 opProperty;
int iCompare = 0; /* Result of last OP_Compare operation */ int iCompare = 0; /* Result of last OP_Compare operation */
int *aPermute = 0; /* Permuation of columns for OP_Compare */ int *aPermute = 0; /* Permutation of columns for OP_Compare */
#ifdef VDBE_PROFILE #ifdef VDBE_PROFILE
u64 start; /* CPU clock count at start of opcode */ u64 start; /* CPU clock count at start of opcode */
int origPc; /* Program counter at start of opcode */ int origPc; /* Program counter at start of opcode */
@ -819,6 +810,16 @@ case OP_Yield: { /* in1 */
break; break;
} }
/* Opcode: HaltIfNull P1 P2 P3 P4 *
**
** Check the value in register P3. If is is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
** value in register P3 is not NULL, then this routine is a no-op.
*/
case OP_HaltIfNull: { /* in3 */
if( (pIn3->flags & MEM_Null)==0 ) break;
/* Fall through into OP_Halt */
}
/* Opcode: Halt P1 P2 * P4 * /* Opcode: Halt P1 P2 * P4 *
** **
@ -967,26 +968,34 @@ case OP_Blob: { /* out2-prerelease */
break; break;
} }
/* Opcode: Variable P1 P2 * * * /* Opcode: Variable P1 P2 P3 P4 *
** **
** The value of variable P1 is written into register P2. A variable is ** Transfer the values of bound parameters P1..P1+P3-1 into registers
** an unknown in the original SQL string as handed to sqlite3_compile(). ** P2..P2+P3-1.
** Any occurrence of the '?' character in the original SQL is considered **
** a variable. Variables in the SQL string are number from left to ** If the parameter is named, then its name appears in P4 and P3==1.
** right beginning with 1. The values of variables are set using the ** The P4 value is used by sqlite3_bind_parameter_name().
** sqlite3_bind() API.
*/ */
case OP_Variable: { /* out2-prerelease */ case OP_Variable: {
int j = pOp->p1 - 1; int j = pOp->p1 - 1;
int k = pOp->p2;
Mem *pVar; Mem *pVar;
assert( j>=0 && j<p->nVar ); int n = pOp->p3;
assert( j>=0 && j+n<=p->nVar );
assert( k>=1 && k+n-1<=p->nMem );
assert( pOp->p4.z==0 || pOp->p3==1 );
pVar = &p->aVar[j]; while( n-- > 0 ){
if( sqlite3VdbeMemTooBig(pVar) ){ pVar = &p->aVar[j++];
goto too_big; if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;
}
pOut = &p->aMem[k++];
sqlite3VdbeMemReleaseExternal(pOut);
pOut->flags = MEM_Null;
sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
UPDATE_MAX_BLOBSIZE(pOut);
} }
sqlite3VdbeMemShallowCopy(pOut, &p->aVar[j], MEM_Static);
UPDATE_MAX_BLOBSIZE(pOut);
break; break;
} }
@ -1002,15 +1011,14 @@ case OP_Move: {
int n = pOp->p3; int n = pOp->p3;
int p1 = pOp->p1; int p1 = pOp->p1;
int p2 = pOp->p2; int p2 = pOp->p2;
assert( n>0 ); assert( n>0 && p1>0 && p2>0 );
assert( p1>0 );
assert( p1+n<p->nMem );
pIn1 = &p->aMem[p1];
assert( p2>0 );
assert( p2+n<p->nMem );
pOut = &p->aMem[p2];
assert( p1+n<=p2 || p2+n<=p1 ); assert( p1+n<=p2 || p2+n<=p1 );
pIn1 = &p->aMem[p1];
pOut = &p->aMem[p2];
while( n-- ){ while( n-- ){
assert( pOut<=&p->aMem[p->nMem] );
assert( pIn1<=&p->aMem[p->nMem] );
zMalloc = pOut->zMalloc; zMalloc = pOut->zMalloc;
pOut->zMalloc = 0; pOut->zMalloc = 0;
sqlite3VdbeMemMove(pOut, pIn1); sqlite3VdbeMemMove(pOut, pIn1);
@ -1076,7 +1084,24 @@ case OP_ResultRow: {
int i; int i;
assert( p->nResColumn==pOp->p2 ); assert( p->nResColumn==pOp->p2 );
assert( pOp->p1>0 ); assert( pOp->p1>0 );
assert( pOp->p1+pOp->p2<=p->nMem ); assert( pOp->p1+pOp->p2<=p->nMem+1 );
/* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
** DML statements invoke this opcode to return the number of rows
** modified to the user. This is the only way that a VM that
** opens a statement transaction may invoke this opcode.
**
** In case this is such a statement, close any statement transaction
** opened by this VM before returning control to the user. This is to
** ensure that statement-transactions are always nested, not overlapping.
** If the open statement-transaction is not closed here, then the user
** may step another VM that opens its own statement transaction. This
** may lead to overlapping statement transactions.
*/
assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
if( SQLITE_OK!=(rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE)) ){
break;
}
/* Invalidate all ephemeral cursor row caches */ /* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1; p->cacheCtr = (p->cacheCtr + 2)|1;
@ -1095,7 +1120,6 @@ case OP_ResultRow: {
/* Return SQLITE_ROW /* Return SQLITE_ROW
*/ */
p->nCallback++;
p->pc = pc + 1; p->pc = pc + 1;
rc = SQLITE_ROW; rc = SQLITE_ROW;
goto vdbe_return; goto vdbe_return;
@ -1300,7 +1324,7 @@ case OP_Function: {
apVal = p->apArg; apVal = p->apArg;
assert( apVal || n==0 ); assert( apVal || n==0 );
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem) ); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) );
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
pArg = &p->aMem[pOp->p2]; pArg = &p->aMem[pOp->p2];
for(i=0; i<n; i++, pArg++){ for(i=0; i<n; i++, pArg++){
@ -1701,7 +1725,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
/* Opcode: Permutation * * * P4 * /* Opcode: Permutation * * * P4 *
** **
** Set the permuation used by the OP_Compare operator to be the array ** Set the permutation used by the OP_Compare operator to be the array
** of integers in P4. ** of integers in P4.
** **
** The permutation is only valid until the next OP_Permutation, OP_Compare, ** The permutation is only valid until the next OP_Permutation, OP_Compare,
@ -1736,9 +1760,9 @@ case OP_Compare: {
assert( n>0 ); assert( n>0 );
assert( pKeyInfo!=0 ); assert( pKeyInfo!=0 );
p1 = pOp->p1; p1 = pOp->p1;
assert( p1>0 && p1+n-1<p->nMem ); assert( p1>0 && p1+n<=p->nMem+1 );
p2 = pOp->p2; p2 = pOp->p2;
assert( p2>0 && p2+n-1<p->nMem ); assert( p2>0 && p2+n<=p->nMem+1 );
for(i=0; i<n; i++){ for(i=0; i<n; i++){
int idx = aPermute ? aPermute[i] : i; int idx = aPermute ? aPermute[i] : i;
CollSeq *pColl; /* Collating sequence to use on this term */ CollSeq *pColl; /* Collating sequence to use on this term */
@ -1932,9 +1956,11 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
** this opcode must be present immediately before the opcode that ** this opcode must be present immediately before the opcode that
** opens the cursor. ** opens the cursor.
*/ */
#if 0
case OP_SetNumColumns: { case OP_SetNumColumns: {
break; break;
} }
#endif
/* Opcode: Column P1 P2 P3 P4 * /* Opcode: Column P1 P2 P3 P4 *
** **
@ -2253,7 +2279,7 @@ case OP_MakeRecord: {
nField = pOp->p1; nField = pOp->p1;
zAffinity = pOp->p4.z; zAffinity = pOp->p4.z;
assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem ); assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 );
pData0 = &p->aMem[nField]; pData0 = &p->aMem[nField];
nField = pOp->p2; nField = pOp->p2;
pLast = &pData0[nField-1]; pLast = &pData0[nField-1];
@ -2330,6 +2356,22 @@ case OP_MakeRecord: {
break; break;
} }
/* Opcode: Count P1 P2 * * *
**
** Store the number of entries (an integer value) in the table or index
** opened by cursor P1 in register P2
*/
#ifndef SQLITE_OMIT_BTREECOUNT
case OP_Count: { /* out2-prerelease */
i64 nEntry;
BtCursor *pCrsr = p->apCsr[pOp->p1]->pCursor;
rc = sqlite3BtreeCount(pCrsr, &nEntry);
pOut->flags = MEM_Int;
pOut->u.i = nEntry;
break;
}
#endif
/* Opcode: Statement P1 * * * * /* Opcode: Statement P1 * * * *
** **
** Begin an individual statement transaction which is part of a larger ** Begin an individual statement transaction which is part of a larger
@ -2361,10 +2403,12 @@ case OP_Statement: {
pBt = db->aDb[i].pBt; pBt = db->aDb[i].pBt;
assert( sqlite3BtreeIsInTrans(pBt) ); assert( sqlite3BtreeIsInTrans(pBt) );
assert( (p->btreeMask & (1<<i))!=0 ); assert( (p->btreeMask & (1<<i))!=0 );
if( !sqlite3BtreeIsInStmt(pBt) ){ if( p->iStatement==0 ){
rc = sqlite3BtreeBeginStmt(pBt); assert( db->nStatement>=0 && db->nSavepoint>=0 );
p->openedStatement = 1; db->nStatement++;
p->iStatement = db->nSavepoint + db->nStatement;
} }
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
} }
break; break;
} }
@ -2471,7 +2515,7 @@ case OP_Savepoint: {
rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
goto abort_due_to_error; goto abort_due_to_error;
} }
} }
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
sqlite3ExpirePreparedStatements(db); sqlite3ExpirePreparedStatements(db);
@ -2507,7 +2551,8 @@ case OP_Savepoint: {
** **
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll ** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active ** back any currently active btree transactions. If there are any active
** VMs (apart from this one), then the COMMIT or ROLLBACK statement fails. ** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if
** there are active writing VMs or active VMs that use shared cache.
** **
** This instruction causes the VM to halt. ** This instruction causes the VM to halt.
*/ */
@ -2550,6 +2595,7 @@ case OP_AutoCommit: {
goto vdbe_return; goto vdbe_return;
} }
} }
assert( db->nStatement==0 );
sqlite3CloseSavepoints(db); sqlite3CloseSavepoints(db);
if( p->rc==SQLITE_OK ){ if( p->rc==SQLITE_OK ){
rc = SQLITE_DONE; rc = SQLITE_DONE;
@ -2767,9 +2813,11 @@ case OP_VerifyCookie: {
** to get a read lock but fails, the script terminates with an ** to get a read lock but fails, the script terminates with an
** SQLITE_BUSY error code. ** SQLITE_BUSY error code.
** **
** The P4 value is a pointer to a KeyInfo structure that defines the ** The P4 value may be either an integer (P4_INT32) or a pointer to
** content and collating sequence of indices. P4 is NULL for cursors ** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
** that are not pointing to indices. ** structure, then said structure defines the content and collating
** sequence of the index being opened. Otherwise, if P4 is an integer
** value, it is set to the number of columns in the table.
** **
** See also OpenWrite. ** See also OpenWrite.
*/ */
@ -2779,9 +2827,11 @@ case OP_VerifyCookie: {
** page is P2. Or if P5!=0 use the content of register P2 to find the ** page is P2. Or if P5!=0 use the content of register P2 to find the
** root page. ** root page.
** **
** The P4 value is a pointer to a KeyInfo structure that defines the ** The P4 value may be either an integer (P4_INT32) or a pointer to
** content and collating sequence of indices. P4 is NULL for cursors ** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
** that are not pointing to indices. ** structure, then said structure defines the content and collating
** sequence of the index being opened. Otherwise, if P4 is an integer
** value, it is set to the number of columns in the table.
** **
** This instruction works just like OpenRead except that it opens the cursor ** This instruction works just like OpenRead except that it opens the cursor
** in read/write mode. For a given table, there can be one or more read-only ** in read/write mode. For a given table, there can be one or more read-only
@ -2791,6 +2841,8 @@ case OP_VerifyCookie: {
*/ */
case OP_OpenRead: case OP_OpenRead:
case OP_OpenWrite: { case OP_OpenWrite: {
int nField = 0;
KeyInfo *pKeyInfo = 0;
int i = pOp->p1; int i = pOp->p1;
int p2 = pOp->p2; int p2 = pOp->p2;
int iDb = pOp->p3; int iDb = pOp->p3;
@ -2824,16 +2876,19 @@ case OP_OpenWrite: {
} }
} }
assert( i>=0 ); assert( i>=0 );
pCur = allocateCursor(p, i, &pOp[-1], iDb, 1); if( pOp->p4type==P4_KEYINFO ){
pKeyInfo = pOp->p4.pKeyInfo;
pKeyInfo->enc = ENC(p->db);
nField = pKeyInfo->nField+1;
}else if( pOp->p4type==P4_INT32 ){
nField = pOp->p4.i;
}
pCur = allocateCursor(p, i, nField, iDb, 1);
if( pCur==0 ) goto no_mem; if( pCur==0 ) goto no_mem;
pCur->nullRow = 1; pCur->nullRow = 1;
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, pCur->pCursor); rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
if( pOp->p4type==P4_KEYINFO ){ pCur->pKeyInfo = pKeyInfo;
pCur->pKeyInfo = pOp->p4.pKeyInfo;
pCur->pKeyInfo->enc = ENC(p->db);
}else{
pCur->pKeyInfo = 0;
}
switch( rc ){ switch( rc ){
case SQLITE_BUSY: { case SQLITE_BUSY: {
p->pc = pc; p->pc = pc;
@ -2908,7 +2963,7 @@ case OP_OpenEphemeral: {
SQLITE_OPEN_TRANSIENT_DB; SQLITE_OPEN_TRANSIENT_DB;
assert( i>=0 ); assert( i>=0 );
pCx = allocateCursor(p, i, pOp, -1, 1); pCx = allocateCursor(p, i, pOp->p2, -1, 1);
if( pCx==0 ) goto no_mem; if( pCx==0 ) goto no_mem;
pCx->nullRow = 1; pCx->nullRow = 1;
rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags, rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags,
@ -2943,7 +2998,7 @@ case OP_OpenEphemeral: {
break; break;
} }
/* Opcode: OpenPseudo P1 P2 * * * /* Opcode: OpenPseudo P1 P2 P3 * *
** **
** Open a new cursor that points to a fake table that contains a single ** Open a new cursor that points to a fake table that contains a single
** row of data. Any attempt to write a second row of data causes the ** row of data. Any attempt to write a second row of data causes the
@ -2962,12 +3017,15 @@ case OP_OpenEphemeral: {
** is stored. In this case, the vdbe program must ensure that the ** is stored. In this case, the vdbe program must ensure that the
** memory cell containing the row data is not overwritten until the ** memory cell containing the row data is not overwritten until the
** pseudo table is closed (or a new row is inserted into it). ** pseudo table is closed (or a new row is inserted into it).
**
** P3 is the number of fields in the records that will be stored by
** the pseudo-table.
*/ */
case OP_OpenPseudo: { case OP_OpenPseudo: {
int i = pOp->p1; int i = pOp->p1;
VdbeCursor *pCx; VdbeCursor *pCx;
assert( i>=0 ); assert( i>=0 );
pCx = allocateCursor(p, i, &pOp[-1], -1, 0); pCx = allocateCursor(p, i, pOp->p3, -1, 0);
if( pCx==0 ) goto no_mem; if( pCx==0 ) goto no_mem;
pCx->nullRow = 1; pCx->nullRow = 1;
pCx->pseudoTable = 1; pCx->pseudoTable = 1;
@ -3512,9 +3570,8 @@ case OP_NewRowid: { /* out2-prerelease */
#endif #endif
if( !pC->useRandomRowid ){ if( !pC->useRandomRowid ){
if( pC->nextRowidValid ){ v = sqlite3BtreeGetCachedRowid(pC->pCursor);
v = pC->nextRowid; if( v==0 ){
}else{
rc = sqlite3BtreeLast(pC->pCursor, &res); rc = sqlite3BtreeLast(pC->pCursor, &res);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
goto abort_due_to_error; goto abort_due_to_error;
@ -3551,12 +3608,7 @@ case OP_NewRowid: { /* out2-prerelease */
} }
#endif #endif
if( v<MAX_ROWID ){ sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0);
pC->nextRowidValid = 1;
pC->nextRowid = v+1;
}else{
pC->nextRowidValid = 0;
}
} }
if( pC->useRandomRowid ){ if( pC->useRandomRowid ){
assert( pOp->p3==0 ); /* SQLITE_FULL must have occurred prior to this */ assert( pOp->p3==0 ); /* SQLITE_FULL must have occurred prior to this */
@ -3634,9 +3686,6 @@ case OP_Insert: {
iKey = intToKey(pKey->u.i); iKey = intToKey(pKey->u.i);
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = pKey->u.i; if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = pKey->u.i;
if( pC->nextRowidValid && pKey->u.i>=pC->nextRowid ){
pC->nextRowidValid = 0;
}
if( pData->flags & MEM_Null ){ if( pData->flags & MEM_Null ){
pData->z = 0; pData->z = 0;
pData->n = 0; pData->n = 0;
@ -3671,6 +3720,7 @@ case OP_Insert: {
}else{ }else{
nZero = 0; nZero = 0;
} }
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
pData->z, pData->n, nZero, pData->z, pData->n, nZero,
pOp->p5 & OPFLAG_APPEND); pOp->p5 & OPFLAG_APPEND);
@ -3733,8 +3783,8 @@ case OP_Delete: {
rc = sqlite3VdbeCursorMoveto(pC); rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error; if( rc ) goto abort_due_to_error;
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
rc = sqlite3BtreeDelete(pC->pCursor); rc = sqlite3BtreeDelete(pC->pCursor);
pC->nextRowidValid = 0;
pC->cacheStatus = CACHE_STALE; pC->cacheStatus = CACHE_STALE;
/* Invoke the update-hook if required. */ /* Invoke the update-hook if required. */
@ -4066,7 +4116,7 @@ case OP_IdxDelete: {
VdbeCursor *pC; VdbeCursor *pC;
BtCursor *pCrsr; BtCursor *pCrsr;
assert( pOp->p3>0 ); assert( pOp->p3>0 );
assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem ); assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 );
assert( i>=0 && i<p->nCursor ); assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 ); assert( p->apCsr[i]!=0 );
if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
@ -4242,7 +4292,7 @@ case OP_Destroy: { /* out2-prerelease */
** P2==1 then the table to be clear is in the auxiliary database file ** P2==1 then the table to be clear is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE. ** that is used to store tables create using CREATE TEMPORARY TABLE.
** **
** If the P3 value is non-zero, then the table refered to must be an ** If the P3 value is non-zero, then the table referred to must be an
** intkey table (an SQL table, not an index). In this case the row change ** intkey table (an SQL table, not an index). In this case the row change
** count is incremented by the number of rows in the table being cleared. ** count is incremented by the number of rows in the table being cleared.
** If P3 is greater than zero, then the value stored in register P3 is ** If P3 is greater than zero, then the value stored in register P3 is
@ -4321,33 +4371,58 @@ case OP_CreateTable: { /* out2-prerelease */
** then runs the new virtual machine. It is thus a re-entrant opcode. ** then runs the new virtual machine. It is thus a re-entrant opcode.
*/ */
case OP_ParseSchema: { case OP_ParseSchema: {
char *zSql;
int iDb = pOp->p1; int iDb = pOp->p1;
const char *zMaster;
InitData initData;
assert( iDb>=0 && iDb<db->nDb ); assert( iDb>=0 && iDb<db->nDb );
if( !pOp->p2 && !DbHasProperty(db, iDb, DB_SchemaLoaded) ){
break; /* If pOp->p2 is 0, then this opcode is being executed to read a
** single row, for example the row corresponding to a new index
** created by this VDBE, from the sqlite_master table. It only
** does this if the corresponding in-memory schema is currently
** loaded. Otherwise, the new index definition can be loaded along
** with the rest of the schema when it is required.
**
** Although the mutex on the BtShared object that corresponds to
** database iDb (the database containing the sqlite_master table
** read by this instruction) is currently held, it is necessary to
** obtain the mutexes on all attached databases before checking if
** the schema of iDb is loaded. This is because, at the start of
** the sqlite3_exec() call below, SQLite will invoke
** sqlite3BtreeEnterAll(). If all mutexes are not already held, the
** iDb mutex may be temporarily released to avoid deadlock. If
** this happens, then some other thread may delete the in-memory
** schema of database iDb before the SQL statement runs. The schema
** will not be reloaded becuase the db->init.busy flag is set. This
** can result in a "no such table: sqlite_master" or "malformed
** database schema" error being returned to the user.
*/
assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
sqlite3BtreeEnterAll(db);
if( pOp->p2 || DbHasProperty(db, iDb, DB_SchemaLoaded) ){
const char *zMaster = SCHEMA_TABLE(iDb);
char *zSql;
InitData initData;
initData.db = db;
initData.iDb = pOp->p1;
initData.pzErrMsg = &p->zErrMsg;
zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s",
db->aDb[iDb].zName, zMaster, pOp->p4.z);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
(void)sqlite3SafetyOff(db);
assert( db->init.busy==0 );
db->init.busy = 1;
initData.rc = SQLITE_OK;
assert( !db->mallocFailed );
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
if( rc==SQLITE_OK ) rc = initData.rc;
sqlite3DbFree(db, zSql);
db->init.busy = 0;
(void)sqlite3SafetyOn(db);
}
} }
zMaster = SCHEMA_TABLE(iDb); sqlite3BtreeLeaveAll(db);
initData.db = db;
initData.iDb = pOp->p1;
initData.pzErrMsg = &p->zErrMsg;
zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s",
db->aDb[iDb].zName, zMaster, pOp->p4.z);
if( zSql==0 ) goto no_mem;
(void)sqlite3SafetyOff(db);
assert( db->init.busy==0 );
db->init.busy = 1;
initData.rc = SQLITE_OK;
assert( !db->mallocFailed );
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
if( rc==SQLITE_OK ) rc = initData.rc;
sqlite3DbFree(db, zSql);
db->init.busy = 0;
(void)sqlite3SafetyOn(db);
if( rc==SQLITE_NOMEM ){ if( rc==SQLITE_NOMEM ){
goto no_mem; goto no_mem;
} }
@ -4781,7 +4856,7 @@ case OP_TableLock: {
assert( (p->btreeMask & (1<<p1))!=0 ); assert( (p->btreeMask & (1<<p1))!=0 );
assert( isWriteLock==0 || isWriteLock==1 ); assert( isWriteLock==0 || isWriteLock==1 );
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
if( rc==SQLITE_LOCKED ){ if( (rc&0xFF)==SQLITE_LOCKED ){
const char *z = pOp->p4.z; const char *z = pOp->p4.z;
sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z);
} }
@ -4796,8 +4871,8 @@ case OP_TableLock: {
** xBegin method for that table. ** xBegin method for that table.
** **
** Also, whether or not P4 is set, check that this is not being called from ** Also, whether or not P4 is set, check that this is not being called from
** within a callback to a virtual table xSync() method. If it is, set the ** within a callback to a virtual table xSync() method. If it is, the error
** error code to SQLITE_LOCKED. ** code will be set to SQLITE_LOCKED.
*/ */
case OP_VBegin: { case OP_VBegin: {
sqlite3_vtab *pVtab = pOp->p4.pVtab; sqlite3_vtab *pVtab = pOp->p4.pVtab;
@ -4863,7 +4938,7 @@ case OP_VOpen: {
pVtabCursor->pVtab = pVtab; pVtabCursor->pVtab = pVtab;
/* Initialise vdbe cursor object */ /* Initialise vdbe cursor object */
pCur = allocateCursor(p, pOp->p1, &pOp[-1], -1, 0); pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
if( pCur ){ if( pCur ){
pCur->pVtabCursor = pVtabCursor; pCur->pVtabCursor = pVtabCursor;
pCur->pModule = pVtabCursor->pVtab->pModule; pCur->pModule = pVtabCursor->pVtab->pModule;
@ -5023,7 +5098,7 @@ case OP_VColumn: {
pVtab->zErrMsg = 0; pVtab->zErrMsg = 0;
/* Copy the result of the function to the P3 register. We /* Copy the result of the function to the P3 register. We
** do this regardless of whether or not an error occured to ensure any ** do this regardless of whether or not an error occurred to ensure any
** dynamic allocation in sContext.s (a Mem struct) is released. ** dynamic allocation in sContext.s (a Mem struct) is released.
*/ */
sqlite3VdbeChangeEncoding(&sContext.s, encoding); sqlite3VdbeChangeEncoding(&sContext.s, encoding);
@ -5204,13 +5279,14 @@ case OP_Pagecount: { /* out2-prerelease */
** the UTF-8 string contained in P4 is emitted on the trace callback. ** the UTF-8 string contained in P4 is emitted on the trace callback.
*/ */
case OP_Trace: { case OP_Trace: {
if( pOp->p4.z ){ char *zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
if( zTrace ){
if( db->xTrace ){ if( db->xTrace ){
db->xTrace(db->pTraceArg, pOp->p4.z); db->xTrace(db->pTraceArg, zTrace);
} }
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
if( (db->flags & SQLITE_SqlTrace)!=0 ){ if( (db->flags & SQLITE_SqlTrace)!=0 ){
sqlite3DebugPrintf("SQL-trace: %s\n", pOp->p4.z); sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
} }
#endif /* SQLITE_DEBUG */ #endif /* SQLITE_DEBUG */
} }

4
vdbe.h
View file

@ -15,7 +15,7 @@
** or VDBE. The VDBE implements an abstract machine that runs a ** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database. ** simple program to access and modify the underlying database.
** **
** $Id: vdbe.h,v 1.139 2008/10/31 10:53:23 danielk1977 Exp $ ** $Id: vdbe.h,v 1.140 2009/02/19 14:39:25 danielk1977 Exp $
*/ */
#ifndef _SQLITE_VDBE_H_ #ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_
@ -181,7 +181,7 @@ void sqlite3VdbeSetNumCols(Vdbe*,int);
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
void sqlite3VdbeCountChanges(Vdbe*); void sqlite3VdbeCountChanges(Vdbe*);
sqlite3 *sqlite3VdbeDb(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*);
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n); void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
void sqlite3VdbeSwap(Vdbe*,Vdbe*); void sqlite3VdbeSwap(Vdbe*,Vdbe*);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT

View file

@ -15,7 +15,7 @@
** 6000 lines long) it was split up into several smaller files and ** 6000 lines long) it was split up into several smaller files and
** this header information was factored out. ** this header information was factored out.
** **
** $Id: vdbeInt.h,v 1.162 2009/02/03 15:39:01 drh Exp $ ** $Id: vdbeInt.h,v 1.166 2009/03/18 10:33:02 danielk1977 Exp $
*/ */
#ifndef _VDBEINT_H_ #ifndef _VDBEINT_H_
#define _VDBEINT_H_ #define _VDBEINT_H_
@ -59,13 +59,11 @@ struct VdbeCursor {
BtCursor *pCursor; /* The cursor structure of the backend */ BtCursor *pCursor; /* The cursor structure of the backend */
int iDb; /* Index of cursor database in db->aDb[] (or -1) */ int iDb; /* Index of cursor database in db->aDb[] (or -1) */
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
i64 nextRowid; /* Next rowid returned by OP_NewRowid */
Bool zeroed; /* True if zeroed out and ready for reuse */ Bool zeroed; /* True if zeroed out and ready for reuse */
Bool rowidIsValid; /* True if lastRowid is valid */ Bool rowidIsValid; /* True if lastRowid is valid */
Bool atFirst; /* True if pointing to first entry */ Bool atFirst; /* True if pointing to first entry */
Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */
Bool nullRow; /* True if pointing to a row with no data */ Bool nullRow; /* True if pointing to a row with no data */
Bool nextRowidValid; /* True if the nextRowid field is valid */
Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */ Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */
Bool ephemPseudoTable; Bool ephemPseudoTable;
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
@ -279,16 +277,13 @@ struct Vdbe {
u32 magic; /* Magic number for sanity checking */ u32 magic; /* Magic number for sanity checking */
int nMem; /* Number of memory locations currently allocated */ int nMem; /* Number of memory locations currently allocated */
Mem *aMem; /* The memory locations */ Mem *aMem; /* The memory locations */
int nCallback; /* Number of callbacks invoked so far */
int cacheCtr; /* VdbeCursor row cache generation counter */ int cacheCtr; /* VdbeCursor row cache generation counter */
int contextStackTop; /* Index of top element in the context stack */ int contextStackTop; /* Index of top element in the context stack */
int contextStackDepth; /* The size of the "context" stack */ int contextStackDepth; /* The size of the "context" stack */
Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/ Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/
int pc; /* The program counter */ int pc; /* The program counter */
int rc; /* Value to return */ int rc; /* Value to return */
unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */
int errorAction; /* Recovery action to do in case of an error */ int errorAction; /* Recovery action to do in case of an error */
int inTempTrans; /* True if temp database is transactioned */
int nResColumn; /* Number of columns in one row of the result set */ int nResColumn; /* Number of columns in one row of the result set */
char **azResColumn; /* Values for one row of result */ char **azResColumn; /* Values for one row of result */
char *zErrMsg; /* Error message written here */ char *zErrMsg; /* Error message written here */
@ -300,17 +295,18 @@ struct Vdbe {
u8 inVtabMethod; /* See comments above */ u8 inVtabMethod; /* See comments above */
u8 usesStmtJournal; /* True if uses a statement journal */ u8 usesStmtJournal; /* True if uses a statement journal */
u8 readOnly; /* True for read-only statements */ u8 readOnly; /* True for read-only statements */
u8 isPrepareV2; /* True if prepared with prepare_v2() */
int nChange; /* Number of db changes made since last reset */ int nChange; /* Number of db changes made since last reset */
i64 startTime; /* Time when query started - used for profiling */ i64 startTime; /* Time when query started - used for profiling */
int btreeMask; /* Bitmask of db->aDb[] entries referenced */ int btreeMask; /* Bitmask of db->aDb[] entries referenced */
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
int aCounter[2]; /* Counters used by sqlite3_stmt_status() */ int aCounter[2]; /* Counters used by sqlite3_stmt_status() */
int nSql; /* Number of bytes in zSql */
char *zSql; /* Text of the SQL statement that generated this */ char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
FILE *trace; /* Write an execution trace here, if not NULL */ FILE *trace; /* Write an execution trace here, if not NULL */
#endif #endif
int openedStatement; /* True if this VM has opened a statement journal */ int iStatement; /* Statement number (or 0 if has not opened stmt) */
#ifdef SQLITE_SSE #ifdef SQLITE_SSE
int fetchId; /* Statement number used by sqlite3_fetch_statement */ int fetchId; /* Statement number used by sqlite3_fetch_statement */
int lru; /* Counter used for LRU cache replacement */ int lru; /* Counter used for LRU cache replacement */
@ -378,6 +374,7 @@ int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
const char *sqlite3OpcodeName(int); const char *sqlite3OpcodeName(int);
int sqlite3VdbeOpcodeHasProperty(int, int); int sqlite3VdbeOpcodeHasProperty(int, int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
int sqlite3VdbeCloseStatement(Vdbe *, int);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseBuffers(Vdbe *p); int sqlite3VdbeReleaseBuffers(Vdbe *p);
#endif #endif

View file

@ -13,7 +13,7 @@
** This file contains code use to implement APIs that are part of the ** This file contains code use to implement APIs that are part of the
** VDBE. ** VDBE.
** **
** $Id: vdbeapi.c,v 1.151 2009/02/04 03:59:25 shane Exp $ ** $Id: vdbeapi.c,v 1.156 2009/03/25 15:43:09 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "vdbeInt.h" #include "vdbeInt.h"
@ -204,12 +204,14 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
rc = SQLITE_OK; rc = SQLITE_OK;
}else{ }else{
Vdbe *v = (Vdbe*)pStmt; Vdbe *v = (Vdbe*)pStmt;
sqlite3 *db = v->db;
#if SQLITE_THREADSAFE #if SQLITE_THREADSAFE
sqlite3_mutex *mutex = v->db->mutex; sqlite3_mutex *mutex = v->db->mutex;
#endif #endif
sqlite3_mutex_enter(mutex); sqlite3_mutex_enter(mutex);
stmtLruRemove(v); stmtLruRemove(v);
rc = sqlite3VdbeFinalize(v); rc = sqlite3VdbeFinalize(v);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(mutex); sqlite3_mutex_leave(mutex);
} }
return rc; return rc;
@ -234,6 +236,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
stmtLruAdd(v); stmtLruAdd(v);
sqlite3VdbeMakeReady(v, -1, 0, 0, 0); sqlite3VdbeMakeReady(v, -1, 0, 0, 0);
assert( (rc & (v->db->errMask))==rc ); assert( (rc & (v->db->errMask))==rc );
rc = sqlite3ApiExit(v->db, rc);
sqlite3_mutex_leave(v->db->mutex); sqlite3_mutex_leave(v->db->mutex);
} }
return rc; return rc;
@ -488,34 +491,41 @@ static int sqlite3Step(Vdbe *p){
#ifndef SQLITE_OMIT_TRACE #ifndef SQLITE_OMIT_TRACE
/* Invoke the profile callback if there is one /* Invoke the profile callback if there is one
*/ */
if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->nOp>0 if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){
&& p->aOp[0].opcode==OP_Trace && p->aOp[0].p4.z!=0 ){
double rNow; double rNow;
u64 elapseTime; u64 elapseTime;
sqlite3OsCurrentTime(db->pVfs, &rNow); sqlite3OsCurrentTime(db->pVfs, &rNow);
elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0); elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0);
elapseTime -= p->startTime; elapseTime -= p->startTime;
db->xProfile(db->pProfileArg, p->aOp[0].p4.z, elapseTime); db->xProfile(db->pProfileArg, p->zSql, elapseTime);
} }
#endif #endif
db->errCode = rc; db->errCode = rc;
/*sqlite3Error(p->db, rc, 0);*/ if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
p->rc = sqlite3ApiExit(p->db, p->rc); p->rc = SQLITE_NOMEM;
end_of_step:
assert( (rc&0xff)==rc );
if( p->zSql && (rc&0xff)<SQLITE_ROW ){
/* This behavior occurs if sqlite3_prepare_v2() was used to build
** the prepared statement. Return error codes directly */
p->db->errCode = p->rc;
/* sqlite3Error(p->db, p->rc, 0); */
return p->rc;
}else{
/* This is for legacy sqlite3_prepare() builds and when the code
** is SQLITE_ROW or SQLITE_DONE */
return rc;
} }
end_of_step:
/* At this point local variable rc holds the value that should be
** returned if this statement was compiled using the legacy
** sqlite3_prepare() interface. According to the docs, this can only
** be one of the values in the first assert() below. Variable p->rc
** contains the value that would be returned if sqlite3_finalize()
** were called on statement p.
*/
assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR
|| rc==SQLITE_BUSY || rc==SQLITE_MISUSE
);
assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE );
if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
/* If this statement was prepared using sqlite3_prepare_v2(), and an
** error has occured, then return the error code in p->rc to the
** caller. Set the error code in the database handle to the same value.
*/
rc = db->errCode = p->rc;
}
return (rc&db->errMask);
} }
/* /*
@ -545,11 +555,11 @@ int sqlite3_step(sqlite3_stmt *pStmt){
sqlite3_mutex_enter(db->mutex); sqlite3_mutex_enter(db->mutex);
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
&& cnt++ < 5 && cnt++ < 5
&& vdbeReprepare(v) ){ && (rc = vdbeReprepare(v))==SQLITE_OK ){
sqlite3_reset(pStmt); sqlite3_reset(pStmt);
v->expired = 0; v->expired = 0;
} }
if( rc==SQLITE_SCHEMA && v->zSql && db->pErr ){ if( rc==SQLITE_SCHEMA && v->isPrepareV2 && db->pErr ){
/* This case occurs after failing to recompile an sql statement. /* This case occurs after failing to recompile an sql statement.
** The error message from the SQL compiler has already been loaded ** The error message from the SQL compiler has already been loaded
** into the database handle. This block copies the error message ** into the database handle. This block copies the error message
@ -711,7 +721,7 @@ failed:
** context. ** context.
*/ */
int sqlite3_aggregate_count(sqlite3_context *p){ int sqlite3_aggregate_count(sqlite3_context *p){
assert( p && p->pFunc && p->pFunc->xStep ); assert( p && p->pMem && p->pFunc && p->pFunc->xStep );
return p->pMem->n; return p->pMem->n;
} }
#endif #endif
@ -754,7 +764,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
}else{ }else{
/* ((double)0) In case of SQLITE_OMIT_FLOATING_POINT... */ /* ((double)0) In case of SQLITE_OMIT_FLOATING_POINT... */
static const Mem nullMem = {{0}, (double)0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 }; static const Mem nullMem = {{0}, (double)0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
if( pVm->db ){ if( pVm && pVm->db ){
sqlite3_mutex_enter(pVm->db->mutex); sqlite3_mutex_enter(pVm->db->mutex);
sqlite3Error(pVm->db, SQLITE_RANGE, 0); sqlite3Error(pVm->db, SQLITE_RANGE, 0);
} }

241
vdbeaux.c
View file

@ -14,7 +14,7 @@
** to version 2.8.7, all this code was combined into the vdbe.c source file. ** to version 2.8.7, all this code was combined into the vdbe.c source file.
** But that file was getting too big so this subroutines were split out. ** But that file was getting too big so this subroutines were split out.
** **
** $Id: vdbeaux.c,v 1.435 2009/02/03 16:51:25 danielk1977 Exp $ ** $Id: vdbeaux.c,v 1.446 2009/03/25 15:43:09 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "vdbeInt.h" #include "vdbeInt.h"
@ -52,17 +52,22 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
/* /*
** Remember the SQL string for a prepared statement. ** Remember the SQL string for a prepared statement.
*/ */
void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n){ void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
if( p==0 ) return; if( p==0 ) return;
#ifdef SQLITE_OMIT_TRACE
if( !isPrepareV2 ) return;
#endif
assert( p->zSql==0 ); assert( p->zSql==0 );
p->zSql = sqlite3DbStrNDup(p->db, z, n); p->zSql = sqlite3DbStrNDup(p->db, z, n);
p->isPrepareV2 = isPrepareV2 ? 1 : 0;
} }
/* /*
** Return the SQL associated with a prepared statement ** Return the SQL associated with a prepared statement
*/ */
const char *sqlite3_sql(sqlite3_stmt *pStmt){ const char *sqlite3_sql(sqlite3_stmt *pStmt){
return ((Vdbe *)pStmt)->zSql; Vdbe *p = (Vdbe *)pStmt;
return (p->isPrepareV2 ? p->zSql : 0);
} }
/* /*
@ -71,7 +76,6 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt){
void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
Vdbe tmp, *pTmp; Vdbe tmp, *pTmp;
char *zTmp; char *zTmp;
int nTmp;
tmp = *pA; tmp = *pA;
*pA = *pB; *pA = *pB;
*pB = tmp; *pB = tmp;
@ -84,9 +88,7 @@ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
zTmp = pA->zSql; zTmp = pA->zSql;
pA->zSql = pB->zSql; pA->zSql = pB->zSql;
pB->zSql = zTmp; pB->zSql = zTmp;
nTmp = pA->nSql; pB->isPrepareV2 = pA->isPrepareV2;
pA->nSql = pB->nSql;
pB->nSql = nTmp;
} }
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
@ -112,7 +114,7 @@ static int growOpArray(Vdbe *p){
int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op)); pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
if( pNew ){ if( pNew ){
p->nOpAlloc = nNew; p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
p->aOp = pNew; p->aOp = pNew;
} }
return (pNew ? SQLITE_OK : SQLITE_NOMEM); return (pNew ? SQLITE_OK : SQLITE_NOMEM);
@ -901,8 +903,8 @@ int sqlite3VdbeList(
} }
if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
p->db->mallocFailed = 1; assert( p->db->mallocFailed );
return SQLITE_NOMEM; return SQLITE_ERROR;
} }
pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
z = displayP4(pOp, pMem->z, 32); z = displayP4(pOp, pMem->z, 32);
@ -918,8 +920,8 @@ int sqlite3VdbeList(
if( p->explain==1 ){ if( p->explain==1 ){
if( sqlite3VdbeMemGrow(pMem, 4, 0) ){ if( sqlite3VdbeMemGrow(pMem, 4, 0) ){
p->db->mallocFailed = 1; assert( p->db->mallocFailed );
return SQLITE_NOMEM; return SQLITE_ERROR;
} }
pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
pMem->n = 2; pMem->n = 2;
@ -998,6 +1000,39 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
} }
#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */ #endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */
/*
** Allocate space from a fixed size buffer. Make *pp point to the
** allocated space. (Note: pp is a char* rather than a void** to
** work around the pointer aliasing rules of C.) *pp should initially
** be zero. If *pp is not zero, that means that the space has already
** been allocated and this routine is a noop.
**
** nByte is the number of bytes of space needed.
**
** *ppFrom point to available space and pEnd points to the end of the
** available space.
**
** *pnByte is a counter of the number of bytes of space that have failed
** to allocate. If there is insufficient space in *ppFrom to satisfy the
** request, then increment *pnByte by the amount of the request.
*/
static void allocSpace(
char *pp, /* IN/OUT: Set *pp to point to allocated buffer */
int nByte, /* Number of bytes to allocate */
u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */
u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */
int *pnByte /* If allocation cannot be made, increment *pnByte */
){
if( (*(void**)pp)==0 ){
nByte = ROUND8(nByte);
if( (pEnd - *ppFrom)>=nByte ){
*(void**)pp = (void *)*ppFrom;
*ppFrom += nByte;
}else{
*pnByte += nByte;
}
}
}
/* /*
** Prepare a virtual machine for execution. This involves things such ** Prepare a virtual machine for execution. This involves things such
@ -1007,6 +1042,14 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
** **
** This is the only way to move a VDBE from VDBE_MAGIC_INIT to ** This is the only way to move a VDBE from VDBE_MAGIC_INIT to
** VDBE_MAGIC_RUN. ** VDBE_MAGIC_RUN.
**
** This function may be called more than once on a single virtual machine.
** The first call is made while compiling the SQL statement. Subsequent
** calls are made as part of the process of resetting a statement to be
** re-executed (from a call to sqlite3_reset()). The nVar, nMem, nCursor
** and isExplain parameters are only passed correct values the first time
** the function is called. On subsequent calls, from sqlite3_reset(), nVar
** is passed -1 and nMem, nCursor and isExplain are all passed zero.
*/ */
void sqlite3VdbeMakeReady( void sqlite3VdbeMakeReady(
Vdbe *p, /* The VDBE */ Vdbe *p, /* The VDBE */
@ -1039,37 +1082,49 @@ void sqlite3VdbeMakeReady(
*/ */
nMem += nCursor; nMem += nCursor;
/* /* Allocate space for memory registers, SQL variables, VDBE cursors and
** Allocation space for registers. ** an array to marshal SQL function arguments in. This is only done the
** first time this function is called for a given VDBE, not when it is
** being called from sqlite3_reset() to reset the virtual machine.
*/ */
if( p->aMem==0 ){ if( nVar>=0 && !db->mallocFailed ){
u8 *zCsr = (u8 *)&p->aOp[p->nOp];
u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc];
int nByte;
int nArg; /* Maximum number of args passed to a user function. */ int nArg; /* Maximum number of args passed to a user function. */
resolveP2Values(p, &nArg); resolveP2Values(p, &nArg);
assert( nVar>=0 );
if( isExplain && nMem<10 ){ if( isExplain && nMem<10 ){
nMem = 10; nMem = 10;
} }
p->aMem = sqlite3DbMallocZero(db,
nMem*sizeof(Mem) /* aMem */ do {
+ nVar*sizeof(Mem) /* aVar */ memset(zCsr, 0, zEnd-zCsr);
+ nArg*sizeof(Mem*) /* apArg */ nByte = 0;
+ nVar*sizeof(char*) /* azVar */ allocSpace((char*)&p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
+ nCursor*sizeof(VdbeCursor*)+1 /* apCsr */ allocSpace((char*)&p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
); allocSpace((char*)&p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
if( !db->mallocFailed ){ allocSpace((char*)&p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
p->aMem--; /* aMem[] goes from 1..nMem */ allocSpace((char*)&p->apCsr,
p->nMem = nMem; /* not from 0..nMem-1 */ nCursor*sizeof(VdbeCursor*), &zCsr, zEnd, &nByte
p->aVar = &p->aMem[nMem+1]; );
if( nByte ){
p->pFree = sqlite3DbMallocRaw(db, nByte);
}
zCsr = p->pFree;
zEnd = &zCsr[nByte];
}while( nByte && !db->mallocFailed );
p->nCursor = nCursor;
if( p->aVar ){
p->nVar = nVar; p->nVar = nVar;
p->okVar = 0;
p->apArg = (Mem**)&p->aVar[nVar];
p->azVar = (char**)&p->apArg[nArg];
p->apCsr = (VdbeCursor**)&p->azVar[nVar];
p->nCursor = nCursor;
for(n=0; n<nVar; n++){ for(n=0; n<nVar; n++){
p->aVar[n].flags = MEM_Null; p->aVar[n].flags = MEM_Null;
p->aVar[n].db = db; p->aVar[n].db = db;
} }
}
if( p->aMem ){
p->aMem--; /* aMem[] goes from 1..nMem */
p->nMem = nMem; /* not from 0..nMem-1 */
for(n=1; n<=nMem; n++){ for(n=1; n<=nMem; n++){
p->aMem[n].flags = MEM_Null; p->aMem[n].flags = MEM_Null;
p->aMem[n].db = db; p->aMem[n].db = db;
@ -1084,14 +1139,13 @@ void sqlite3VdbeMakeReady(
p->pc = -1; p->pc = -1;
p->rc = SQLITE_OK; p->rc = SQLITE_OK;
p->uniqueCnt = 0;
p->errorAction = OE_Abort; p->errorAction = OE_Abort;
p->explain |= isExplain; p->explain |= isExplain;
p->magic = VDBE_MAGIC_RUN; p->magic = VDBE_MAGIC_RUN;
p->nChange = 0; p->nChange = 0;
p->cacheCtr = 1; p->cacheCtr = 1;
p->minWriteFileFormat = 255; p->minWriteFileFormat = 255;
p->openedStatement = 0; p->iStatement = 0;
#ifdef VDBE_PROFILE #ifdef VDBE_PROFILE
{ {
int i; int i;
@ -1405,7 +1459,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
** sqlite3BtreeCommitPhaseOne(), then there is a chance that the ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the
** master journal file will be orphaned. But we cannot delete it, ** master journal file will be orphaned. But we cannot delete it,
** in case the master journal file name was written into the journal ** in case the master journal file name was written into the journal
** file before the failure occured. ** file before the failure occurred.
*/ */
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt; Btree *pBt = db->aDb[i].pBt;
@ -1510,6 +1564,48 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
} }
} }
/*
** If the Vdbe passed as the first argument opened a statement-transaction,
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
** statement transaction is commtted.
**
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
** Otherwise SQLITE_OK.
*/
int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
sqlite3 *const db = p->db;
int rc = SQLITE_OK;
if( p->iStatement && db->nStatement ){
int i;
const int iSavepoint = p->iStatement-1;
assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE);
assert( db->nStatement>0 );
assert( p->iStatement==(db->nStatement+db->nSavepoint) );
for(i=0; i<db->nDb; i++){
int rc2 = SQLITE_OK;
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
if( eOp==SAVEPOINT_ROLLBACK ){
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint);
}
if( rc2==SQLITE_OK ){
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint);
}
if( rc==SQLITE_OK ){
rc = rc2;
}
}
}
db->nStatement--;
p->iStatement = 0;
}
return rc;
}
/* /*
** This routine is called the when a VDBE tries to halt. If the VDBE ** This routine is called the when a VDBE tries to halt. If the VDBE
** has made changes and is in autocommit mode, then commit those ** has made changes and is in autocommit mode, then commit those
@ -1524,10 +1620,8 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
** means the close did not happen and needs to be repeated. ** means the close did not happen and needs to be repeated.
*/ */
int sqlite3VdbeHalt(Vdbe *p){ int sqlite3VdbeHalt(Vdbe *p){
int rc; /* Used to store transient return codes */
sqlite3 *db = p->db; sqlite3 *db = p->db;
int i;
int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */
int isSpecialError; /* Set to true if SQLITE_NOMEM or IOERR */
/* This function contains the logic that determines if a statement or /* This function contains the logic that determines if a statement or
** transaction will be committed or rolled back as a result of the ** transaction will be committed or rolled back as a result of the
@ -1557,6 +1651,8 @@ int sqlite3VdbeHalt(Vdbe *p){
/* No commit or rollback needed if the program never started */ /* No commit or rollback needed if the program never started */
if( p->pc>=0 ){ if( p->pc>=0 ){
int mrc; /* Primary error code from p->rc */ int mrc; /* Primary error code from p->rc */
int eStatementOp = 0;
int isSpecialError; /* Set to true if a 'special' error */
/* Lock all btrees used by the statement */ /* Lock all btrees used by the statement */
sqlite3BtreeMutexArrayEnter(&p->aMutex); sqlite3BtreeMutexArrayEnter(&p->aMutex);
@ -1571,11 +1667,11 @@ int sqlite3VdbeHalt(Vdbe *p){
*/ */
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){ if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){
xFunc = sqlite3BtreeRollbackStmt; eStatementOp = SAVEPOINT_ROLLBACK;
p->rc = SQLITE_BUSY; p->rc = SQLITE_BUSY;
}else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) }else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL)
&& p->usesStmtJournal ){ && p->usesStmtJournal ){
xFunc = sqlite3BtreeRollbackStmt; eStatementOp = SAVEPOINT_ROLLBACK;
}else{ }else{
/* We are forced to roll back the active transaction. Before doing /* We are forced to roll back the active transaction. Before doing
** so, abort any other statements this handle currently has active. ** so, abort any other statements this handle currently has active.
@ -1588,8 +1684,8 @@ int sqlite3VdbeHalt(Vdbe *p){
} }
} }
/* If the auto-commit flag is set and this is the only active vdbe, then /* If the auto-commit flag is set and this is the only active writer
** we do either a commit or rollback of the current transaction. ** VM, then we do either a commit or rollback of the current transaction.
** **
** Note: This block also runs if one of the special errors handled ** Note: This block also runs if one of the special errors handled
** above has occurred. ** above has occurred.
@ -1604,7 +1700,7 @@ int sqlite3VdbeHalt(Vdbe *p){
** successful or hit an 'OR FAIL' constraint. This means a commit ** successful or hit an 'OR FAIL' constraint. This means a commit
** is required. ** is required.
*/ */
int rc = vdbeCommit(db, p); rc = vdbeCommit(db, p);
if( rc==SQLITE_BUSY ){ if( rc==SQLITE_BUSY ){
sqlite3BtreeMutexArrayLeave(&p->aMutex); sqlite3BtreeMutexArrayLeave(&p->aMutex);
return SQLITE_BUSY; return SQLITE_BUSY;
@ -1617,13 +1713,12 @@ int sqlite3VdbeHalt(Vdbe *p){
}else{ }else{
sqlite3RollbackAll(db); sqlite3RollbackAll(db);
} }
}else if( !xFunc ){ db->nStatement = 0;
}else if( eStatementOp==0 ){
if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
if( p->openedStatement ){ eStatementOp = SAVEPOINT_RELEASE;
xFunc = sqlite3BtreeCommitStmt;
}
}else if( p->errorAction==OE_Abort ){ }else if( p->errorAction==OE_Abort ){
xFunc = sqlite3BtreeRollbackStmt; eStatementOp = SAVEPOINT_ROLLBACK;
}else{ }else{
invalidateCursorsOnModifiedBtrees(db); invalidateCursorsOnModifiedBtrees(db);
sqlite3RollbackAll(db); sqlite3RollbackAll(db);
@ -1632,33 +1727,26 @@ int sqlite3VdbeHalt(Vdbe *p){
} }
} }
/* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or /* If eStatementOp is non-zero, then a statement transaction needs to
** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
** and the return code is still SQLITE_OK, set the return code to the new ** do so. If this operation returns an error, and the current statement
** error value. ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then set the error
** code to the new value.
*/ */
assert(!xFunc || if( eStatementOp ){
xFunc==sqlite3BtreeCommitStmt || rc = sqlite3VdbeCloseStatement(p, eStatementOp);
xFunc==sqlite3BtreeRollbackStmt if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
); p->rc = rc;
for(i=0; xFunc && i<db->nDb; i++){ sqlite3DbFree(db, p->zErrMsg);
int rc; p->zErrMsg = 0;
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
rc = xFunc(pBt);
if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
p->rc = rc;
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}
} }
} }
/* If this was an INSERT, UPDATE or DELETE and the statement was committed, /* If this was an INSERT, UPDATE or DELETE and no statement transaction
** set the change counter. ** has been rolled back, update the database connection change-counter.
*/ */
if( p->changeCntOn && p->pc>=0 ){ if( p->changeCntOn && p->pc>=0 ){
if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){ if( eStatementOp!=SAVEPOINT_ROLLBACK ){
sqlite3VdbeSetChanges(db, p->nChange); sqlite3VdbeSetChanges(db, p->nChange);
}else{ }else{
sqlite3VdbeSetChanges(db, 0); sqlite3VdbeSetChanges(db, 0);
@ -1690,6 +1778,15 @@ int sqlite3VdbeHalt(Vdbe *p){
p->rc = SQLITE_NOMEM; p->rc = SQLITE_NOMEM;
} }
/* If the auto-commit flag is set to true, then any locks that were held
** by connection db have now been released. Call sqlite3ConnectionUnlocked()
** to invoke any required unlock-notify callbacks.
*/
if( db->autoCommit ){
sqlite3ConnectionUnlocked(db);
}
assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
return SQLITE_OK; return SQLITE_OK;
} }
@ -1847,17 +1944,15 @@ void sqlite3VdbeDelete(Vdbe *p){
sqlite3DbFree(db, pOp->zComment); sqlite3DbFree(db, pOp->zComment);
#endif #endif
} }
sqlite3DbFree(db, p->aOp);
} }
releaseMemArray(p->aVar, p->nVar); releaseMemArray(p->aVar, p->nVar);
sqlite3DbFree(db, p->aLabel); sqlite3DbFree(db, p->aLabel);
if( p->aMem ){
sqlite3DbFree(db, &p->aMem[1]);
}
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql); sqlite3DbFree(db, p->zSql);
p->magic = VDBE_MAGIC_DEAD; p->magic = VDBE_MAGIC_DEAD;
sqlite3DbFree(db, p->aOp);
sqlite3DbFree(db, p->pFree);
sqlite3DbFree(db, p); sqlite3DbFree(db, p);
} }

View file

@ -12,7 +12,7 @@
** **
** This file contains code used to implement incremental BLOB I/O. ** This file contains code used to implement incremental BLOB I/O.
** **
** $Id: vdbeblob.c,v 1.26 2008/10/02 14:49:02 danielk1977 Exp $ ** $Id: vdbeblob.c,v 1.31 2009/03/24 15:08:10 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -70,17 +70,15 @@ int sqlite3_blob_open(
/* One of the following two instructions is replaced by an /* One of the following two instructions is replaced by an
** OP_Noop before exection. ** OP_Noop before exection.
*/ */
{OP_SetNumColumns, 0, 0, 0}, /* 2: Num cols for cursor */ {OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
{OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ {OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
{OP_SetNumColumns, 0, 0, 0}, /* 4: Num cols for cursor */
{OP_OpenWrite, 0, 0, 0}, /* 5: Open cursor 0 for read/write */
{OP_Variable, 1, 1, 0}, /* 6: Push the rowid to the stack */ {OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
{OP_NotExists, 0, 10, 1}, /* 7: Seek the cursor */ {OP_NotExists, 0, 8, 1}, /* 5: Seek the cursor */
{OP_Column, 0, 0, 1}, /* 8 */ {OP_Column, 0, 0, 1}, /* 6 */
{OP_ResultRow, 1, 0, 0}, /* 9 */ {OP_ResultRow, 1, 0, 0}, /* 7 */
{OP_Close, 0, 0, 0}, /* 10 */ {OP_Close, 0, 0, 0}, /* 8 */
{OP_Halt, 0, 0, 0}, /* 11 */ {OP_Halt, 0, 0, 0}, /* 9 */
}; };
Vdbe *v = 0; Vdbe *v = 0;
@ -178,19 +176,19 @@ int sqlite3_blob_open(
/* Remove either the OP_OpenWrite or OpenRead. Set the P2 /* Remove either the OP_OpenWrite or OpenRead. Set the P2
** parameter of the other to pTab->tnum. ** parameter of the other to pTab->tnum.
*/ */
sqlite3VdbeChangeToNoop(v, (flags ? 3 : 5), 1); sqlite3VdbeChangeToNoop(v, (flags ? 2 : 3), 1);
sqlite3VdbeChangeP2(v, (flags ? 5 : 3), pTab->tnum); sqlite3VdbeChangeP2(v, (flags ? 3 : 2), pTab->tnum);
sqlite3VdbeChangeP3(v, (flags ? 5 : 3), iDb); sqlite3VdbeChangeP3(v, (flags ? 3 : 2), iDb);
/* Configure the OP_SetNumColumns. Configure the cursor to /* Configure the number of columns. Configure the cursor to
** think that the table has one more column than it really ** think that the table has one more column than it really
** does. An OP_Column to retrieve this imaginary column will ** does. An OP_Column to retrieve this imaginary column will
** always return an SQL NULL. This is useful because it means ** always return an SQL NULL. This is useful because it means
** we can invoke OP_Column to fill in the vdbe cursors type ** we can invoke OP_Column to fill in the vdbe cursors type
** and offset cache without causing any IO. ** and offset cache without causing any IO.
*/ */
sqlite3VdbeChangeP2(v, flags ? 4 : 2, pTab->nCol+1); sqlite3VdbeChangeP4(v, flags ? 3 : 2, SQLITE_INT_TO_PTR(pTab->nCol+1), P4_INT32);
sqlite3VdbeChangeP2(v, 8, pTab->nCol); sqlite3VdbeChangeP2(v, 6, pTab->nCol);
if( !db->mallocFailed ){ if( !db->mallocFailed ){
sqlite3VdbeMakeReady(v, 1, 1, 1, 0); sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
} }
@ -250,8 +248,8 @@ int sqlite3_blob_open(
blob_open_out: blob_open_out:
zErr[sizeof(zErr)-1] = '\0'; zErr[sizeof(zErr)-1] = '\0';
if( rc!=SQLITE_OK || db->mallocFailed ){ if( v && (rc!=SQLITE_OK || db->mallocFailed) ){
sqlite3_finalize((sqlite3_stmt *)v); sqlite3VdbeFinalize(v);
} }
sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr)); sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr));
rc = sqlite3ApiExit(db, rc); rc = sqlite3ApiExit(db, rc);
@ -266,9 +264,13 @@ blob_open_out:
int sqlite3_blob_close(sqlite3_blob *pBlob){ int sqlite3_blob_close(sqlite3_blob *pBlob){
Incrblob *p = (Incrblob *)pBlob; Incrblob *p = (Incrblob *)pBlob;
int rc; int rc;
sqlite3 *db;
db = p->db;
sqlite3_mutex_enter(db->mutex);
rc = sqlite3_finalize(p->pStmt); rc = sqlite3_finalize(p->pStmt);
sqlite3DbFree(p->db, p); sqlite3DbFree(db, p);
sqlite3_mutex_leave(db->mutex);
return rc; return rc;
} }

View file

@ -15,7 +15,7 @@
** only within the VDBE. Interface routines refer to a Mem using the ** only within the VDBE. Interface routines refer to a Mem using the
** name sqlite_value ** name sqlite_value
** **
** $Id: vdbemem.c,v 1.137 2009/02/04 03:59:25 shane Exp $ ** $Id: vdbemem.c,v 1.139 2009/03/29 15:12:10 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "vdbeInt.h" #include "vdbeInt.h"
@ -321,6 +321,10 @@ static i64 doubleToInt64(double r){
if( r<(double)minInt ){ if( r<(double)minInt ){
return minInt; return minInt;
}else if( r>(double)maxInt ){ }else if( r>(double)maxInt ){
/* minInt is correct here - not maxInt. It turns out that assigning
** a very large positive number to an integer results in a very large
** negative integer. This makes no sense, but it is what x86 hardware
** does so for compatibility we will do the same in software. */
return minInt; return minInt;
}else{ }else{
return (i64)r; return (i64)r;
@ -333,9 +337,10 @@ static i64 doubleToInt64(double r){
** If pMem is an integer, then the value is exact. If pMem is ** If pMem is an integer, then the value is exact. If pMem is
** a floating-point then the value returned is the integer part. ** a floating-point then the value returned is the integer part.
** If pMem is a string or blob, then we make an attempt to convert ** If pMem is a string or blob, then we make an attempt to convert
** it into a integer and return that. If pMem is NULL, return 0. ** it into a integer and return that. If pMem represents an
** an SQL-NULL value, return 0.
** **
** If pMem is a string, its encoding might be changed. ** If pMem represents a string value, its encoding might be changed.
*/ */
i64 sqlite3VdbeIntValue(Mem *pMem){ i64 sqlite3VdbeIntValue(Mem *pMem){
int flags; int flags;

11
vtab.c
View file

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file contains code used to help implement virtual tables. ** This file contains code used to help implement virtual tables.
** **
** $Id: vtab.c,v 1.81 2008/12/10 19:26:24 drh Exp $ ** $Id: vtab.c,v 1.84 2009/03/24 15:08:10 drh Exp $
*/ */
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h" #include "sqliteInt.h"
@ -118,7 +118,8 @@ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
*/ */
void sqlite3VtabClear(Table *p){ void sqlite3VtabClear(Table *p){
sqlite3_vtab *pVtab = p->pVtab; sqlite3_vtab *pVtab = p->pVtab;
sqlite3 *db = p->db; Schema *pSchema = p->pSchema;
sqlite3 *db = pSchema ? pSchema->db : 0;
if( pVtab ){ if( pVtab ){
assert( p->pMod && p->pMod->pModule ); assert( p->pMod && p->pMod->pModule );
sqlite3VtabUnlock(db, pVtab); sqlite3VtabUnlock(db, pVtab);
@ -571,7 +572,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
} }
sParse.declareVtab = 0; sParse.declareVtab = 0;
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe); if( sParse.pVdbe ){
sqlite3VdbeFinalize(sParse.pVdbe);
}
sqlite3DeleteTable(sParse.pNewTable); sqlite3DeleteTable(sParse.pNewTable);
sParse.pNewTable = 0; sParse.pNewTable = 0;
@ -711,7 +714,7 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
/* Special case: If db->aVTrans is NULL and db->nVTrans is greater /* Special case: If db->aVTrans is NULL and db->nVTrans is greater
** than zero, then this function is being called from within a ** than zero, then this function is being called from within a
** virtual module xSync() callback. It is illegal to write to ** virtual module xSync() callback. It is illegal to write to
** virtual module tables in this case, so return SQLITE_LOCKED. ** virtual module tables in this case, so return SQLITE_MISUSE.
*/ */
if( sqlite3VtabInSync(db) ){ if( sqlite3VtabInSync(db) ){
return SQLITE_LOCKED; return SQLITE_LOCKED;

View file

@ -12,7 +12,7 @@
** This file contains routines used for walking the parser tree for ** This file contains routines used for walking the parser tree for
** an SQL statement. ** an SQL statement.
** **
** $Id: walker.c,v 1.1 2008/08/20 16:35:10 drh Exp $ ** $Id: walker.c,v 1.2 2009/02/19 14:39:25 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <stdlib.h> #include <stdlib.h>
@ -45,9 +45,10 @@ int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
if( rc==WRC_Continue ){ if( rc==WRC_Continue ){
if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort; if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
if( sqlite3WalkExprList(pWalker, pExpr->pList) ) return WRC_Abort; if( ExprHasProperty(pExpr, EP_xIsSelect) ){
if( sqlite3WalkSelect(pWalker, pExpr->pSelect) ){ if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
return WRC_Abort; }else{
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
} }
} }
return rc & WRC_Abort; return rc & WRC_Abort;

156
where.c
View file

@ -16,7 +16,7 @@
** so is applicable. Because this module is responsible for selecting ** so is applicable. Because this module is responsible for selecting
** indices, you might also think of this module as the "query optimizer". ** indices, you might also think of this module as the "query optimizer".
** **
** $Id: where.c,v 1.368 2009/02/04 03:59:25 shane Exp $ ** $Id: where.c,v 1.379 2009/03/29 00:15:54 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -431,8 +431,11 @@ static Bitmask exprTableUsage(WhereMaskSet *pMaskSet, Expr *p){
} }
mask = exprTableUsage(pMaskSet, p->pRight); mask = exprTableUsage(pMaskSet, p->pRight);
mask |= exprTableUsage(pMaskSet, p->pLeft); mask |= exprTableUsage(pMaskSet, p->pLeft);
mask |= exprListTableUsage(pMaskSet, p->pList); if( ExprHasProperty(p, EP_xIsSelect) ){
mask |= exprSelectTableUsage(pMaskSet, p->pSelect); mask |= exprSelectTableUsage(pMaskSet, p->x.pSelect);
}else{
mask |= exprListTableUsage(pMaskSet, p->x.pList);
}
return mask; return mask;
} }
static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){ static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){
@ -634,7 +637,7 @@ static int isLikeOrGlob(
#ifdef SQLITE_EBCDIC #ifdef SQLITE_EBCDIC
if( *pnoCase ) return 0; if( *pnoCase ) return 0;
#endif #endif
pList = pExpr->pList; pList = pExpr->x.pList;
pRight = pList->a[0].pExpr; pRight = pList->a[0].pExpr;
if( pRight->op!=TK_STRING ){ if( pRight->op!=TK_STRING ){
return 0; return 0;
@ -653,7 +656,7 @@ static int isLikeOrGlob(
(pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){ (pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){
return 0; return 0;
} }
sqlite3DequoteExpr(db, pRight); sqlite3DequoteExpr(pRight);
z = (char *)pRight->token.z; z = (char *)pRight->token.z;
cnt = 0; cnt = 0;
if( z ){ if( z ){
@ -689,7 +692,7 @@ static int isMatchOfColumn(
sqlite3StrNICmp((const char*)pExpr->token.z,"match",5)!=0 ){ sqlite3StrNICmp((const char*)pExpr->token.z,"match",5)!=0 ){
return 0; return 0;
} }
pList = pExpr->pList; pList = pExpr->x.pList;
if( pList->nExpr!=2 ){ if( pList->nExpr!=2 ){
return 0; return 0;
} }
@ -953,17 +956,18 @@ static void exprAnalyzeOrTerm(
assert( pOrTerm->eOperator==WO_EQ ); assert( pOrTerm->eOperator==WO_EQ );
assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->leftCursor==iCursor );
assert( pOrTerm->u.leftColumn==iColumn ); assert( pOrTerm->u.leftColumn==iColumn );
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight); pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup, 0); pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup, 0);
pLeft = pOrTerm->pExpr->pLeft; pLeft = pOrTerm->pExpr->pLeft;
} }
assert( pLeft!=0 ); assert( pLeft!=0 );
pDup = sqlite3ExprDup(db, pLeft); pDup = sqlite3ExprDup(db, pLeft, 0);
pNew = sqlite3Expr(db, TK_IN, pDup, 0, 0); pNew = sqlite3Expr(db, TK_IN, pDup, 0, 0);
if( pNew ){ if( pNew ){
int idxNew; int idxNew;
transferJoinMarkings(pNew, pExpr); transferJoinMarkings(pNew, pExpr);
pNew->pList = pList; assert( !ExprHasProperty(pNew, EP_xIsSelect) );
pNew->x.pList = pList;
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 ); testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew); exprAnalyze(pSrc, pWC, idxNew);
@ -1026,8 +1030,11 @@ static void exprAnalyze(
op = pExpr->op; op = pExpr->op;
if( op==TK_IN ){ if( op==TK_IN ){
assert( pExpr->pRight==0 ); assert( pExpr->pRight==0 );
pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->pList) if( ExprHasProperty(pExpr, EP_xIsSelect) ){
| exprSelectTableUsage(pMaskSet, pExpr->pSelect); pTerm->prereqRight = exprSelectTableUsage(pMaskSet, pExpr->x.pSelect);
}else{
pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->x.pList);
}
}else if( op==TK_ISNULL ){ }else if( op==TK_ISNULL ){
pTerm->prereqRight = 0; pTerm->prereqRight = 0;
}else{ }else{
@ -1057,7 +1064,7 @@ static void exprAnalyze(
Expr *pDup; Expr *pDup;
if( pTerm->leftCursor>=0 ){ if( pTerm->leftCursor>=0 ){
int idxNew; int idxNew;
pDup = sqlite3ExprDup(db, pExpr); pDup = sqlite3ExprDup(db, pExpr, 0);
if( db->mallocFailed ){ if( db->mallocFailed ){
sqlite3ExprDelete(db, pDup); sqlite3ExprDelete(db, pDup);
return; return;
@ -1100,7 +1107,7 @@ static void exprAnalyze(
** BETWEEN term is skipped. ** BETWEEN term is skipped.
*/ */
else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){
ExprList *pList = pExpr->pList; ExprList *pList = pExpr->x.pList;
int i; int i;
static const u8 ops[] = {TK_GE, TK_LE}; static const u8 ops[] = {TK_GE, TK_LE};
assert( pList!=0 ); assert( pList!=0 );
@ -1108,8 +1115,8 @@ static void exprAnalyze(
for(i=0; i<2; i++){ for(i=0; i<2; i++){
Expr *pNewExpr; Expr *pNewExpr;
int idxNew; int idxNew;
pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft), pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft, 0),
sqlite3ExprDup(db, pList->a[i].pExpr), 0); sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0);
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 ); testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew); exprAnalyze(pSrc, pWC, idxNew);
@ -1148,18 +1155,18 @@ static void exprAnalyze(
Expr *pNewExpr1, *pNewExpr2; Expr *pNewExpr1, *pNewExpr2;
int idxNew1, idxNew2; int idxNew1, idxNew2;
pLeft = pExpr->pList->a[1].pExpr; pLeft = pExpr->x.pList->a[1].pExpr;
pRight = pExpr->pList->a[0].pExpr; pRight = pExpr->x.pList->a[0].pExpr;
pStr1 = sqlite3PExpr(pParse, TK_STRING, 0, 0, 0); pStr1 = sqlite3PExpr(pParse, TK_STRING, 0, 0, 0);
if( pStr1 ){ if( pStr1 ){
sqlite3TokenCopy(db, &pStr1->token, &pRight->token); sqlite3TokenCopy(db, &pStr1->token, &pRight->token);
pStr1->token.n = nPattern; pStr1->token.n = nPattern;
pStr1->flags = EP_Dequoted; pStr1->flags = EP_Dequoted;
} }
pStr2 = sqlite3ExprDup(db, pStr1); pStr2 = sqlite3ExprDup(db, pStr1, 0);
if( !db->mallocFailed ){ if( !db->mallocFailed ){
u8 c, *pC; u8 c, *pC;
assert( pStr2->token.dyn ); /* assert( pStr2->token.dyn ); */
pC = (u8*)&pStr2->token.z[nPattern-1]; pC = (u8*)&pStr2->token.z[nPattern-1];
c = *pC; c = *pC;
if( noCase ){ if( noCase ){
@ -1168,11 +1175,11 @@ static void exprAnalyze(
} }
*pC = c + 1; *pC = c + 1;
} }
pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprDup(db,pLeft), pStr1, 0); pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprDup(db,pLeft,0),pStr1,0);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC); idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew1==0 ); testcase( idxNew1==0 );
exprAnalyze(pSrc, pWC, idxNew1); exprAnalyze(pSrc, pWC, idxNew1);
pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprDup(db,pLeft), pStr2, 0); pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprDup(db,pLeft,0),pStr2,0);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC); idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew2==0 ); testcase( idxNew2==0 );
exprAnalyze(pSrc, pWC, idxNew2); exprAnalyze(pSrc, pWC, idxNew2);
@ -1198,13 +1205,13 @@ static void exprAnalyze(
WhereTerm *pNewTerm; WhereTerm *pNewTerm;
Bitmask prereqColumn, prereqExpr; Bitmask prereqColumn, prereqExpr;
pRight = pExpr->pList->a[0].pExpr; pRight = pExpr->x.pList->a[0].pExpr;
pLeft = pExpr->pList->a[1].pExpr; pLeft = pExpr->x.pList->a[1].pExpr;
prereqExpr = exprTableUsage(pMaskSet, pRight); prereqExpr = exprTableUsage(pMaskSet, pRight);
prereqColumn = exprTableUsage(pMaskSet, pLeft); prereqColumn = exprTableUsage(pMaskSet, pLeft);
if( (prereqExpr & prereqColumn)==0 ){ if( (prereqExpr & prereqColumn)==0 ){
Expr *pNewExpr; Expr *pNewExpr;
pNewExpr = sqlite3Expr(db, TK_MATCH, 0, sqlite3ExprDup(db, pRight), 0); pNewExpr = sqlite3Expr(db, TK_MATCH, 0, sqlite3ExprDup(db, pRight, 0), 0);
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 ); testcase( idxNew==0 );
pNewTerm = &pWC->a[idxNew]; pNewTerm = &pWC->a[idxNew];
@ -1708,15 +1715,15 @@ static double bestVirtualIndex(
** * Whether or not there must be separate lookups in the ** * Whether or not there must be separate lookups in the
** index and in the main table. ** index and in the main table.
** **
** If there was an INDEXED BY clause attached to the table in the SELECT ** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in
** statement, then this function only considers plans using the ** the SQL statement, then this function only considers plans using the
** named index. If one cannot be found, then the returned cost is ** named index. If no such plan is found, then the returned cost is
** SQLITE_BIG_DBL. If a plan can be found that uses the named index, ** SQLITE_BIG_DBL. If a plan is found that uses the named index,
** then the cost is calculated in the usual way. ** then the cost is calculated in the usual way.
** **
** If a NOT INDEXED clause was attached to the table in the SELECT ** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table
** statement, then no indexes are considered. However, the selected ** in the SELECT statement, then no indexes are considered. However, the
** plan may still take advantage of the tables built-in rowid ** selected plan may still take advantage of the tables built-in rowid
** index. ** index.
*/ */
static void bestIndex( static void bestIndex(
@ -1776,10 +1783,12 @@ static void bestIndex(
pCost->rCost = 0; pCost->rCost = 0;
pCost->nRow = 1; pCost->nRow = 1;
return; return;
}else if( (pExpr = pTerm->pExpr)->pList!=0 ){ }else if( !ExprHasProperty((pExpr = pTerm->pExpr), EP_xIsSelect)
&& pExpr->x.pList
){
/* Rowid IN (LIST): cost is NlogN where N is the number of list /* Rowid IN (LIST): cost is NlogN where N is the number of list
** elements. */ ** elements. */
pCost->rCost = pCost->nRow = pExpr->pList->nExpr; pCost->rCost = pCost->nRow = pExpr->x.pList->nExpr;
pCost->rCost *= estLog(pCost->rCost); pCost->rCost *= estLog(pCost->rCost);
}else{ }else{
/* Rowid IN (SELECT): cost is NlogN where N is the number of rows /* Rowid IN (SELECT): cost is NlogN where N is the number of rows
@ -1828,7 +1837,15 @@ static void bestIndex(
cost += cost*estLog(cost); cost += cost*estLog(cost);
WHERETRACE(("... sorting increases cost to %.9g\n", cost)); WHERETRACE(("... sorting increases cost to %.9g\n", cost));
} }
}else if( pParse->db->flags & SQLITE_ReverseOrder ){
/* For application testing, randomly reverse the output order for
** SELECT statements that omit the ORDER BY clause. This will help
** to find cases where
*/
wsFlags |= WHERE_REVERSE;
} }
/* Remember this case if it is the best so far */
if( cost<pCost->rCost ){ if( cost<pCost->rCost ){
pCost->rCost = cost; pCost->rCost = cost;
pCost->nRow = nRow; pCost->nRow = nRow;
@ -1909,12 +1926,18 @@ static void bestIndex(
pProbe = pSrc->pIndex; pProbe = pSrc->pIndex;
} }
for(; pProbe; pProbe=(pSrc->pIndex ? 0 : pProbe->pNext)){ for(; pProbe; pProbe=(pSrc->pIndex ? 0 : pProbe->pNext)){
double inMultiplier = 1; double inMultiplier = 1; /* Number of equality look-ups needed */
int inMultIsEst = 0; /* True if inMultiplier is an estimate */
WHERETRACE(("... index %s:\n", pProbe->zName)); WHERETRACE(("... index %s:\n", pProbe->zName));
/* Count the number of columns in the index that are satisfied /* Count the number of columns in the index that are satisfied
** by x=EXPR constraints or x IN (...) constraints. ** by x=EXPR constraints or x IN (...) constraints. For a term
** of the form x=EXPR we only have to do a single binary search.
** But for x IN (...) we have to do a number of binary searched
** equal to the number of entries on the RHS of the IN operator.
** The inMultipler variable with try to estimate the number of
** binary searches needed.
*/ */
wsFlags = 0; wsFlags = 0;
for(i=0; i<pProbe->nColumn; i++){ for(i=0; i<pProbe->nColumn; i++){
@ -1925,23 +1948,33 @@ static void bestIndex(
if( pTerm->eOperator & WO_IN ){ if( pTerm->eOperator & WO_IN ){
Expr *pExpr = pTerm->pExpr; Expr *pExpr = pTerm->pExpr;
wsFlags |= WHERE_COLUMN_IN; wsFlags |= WHERE_COLUMN_IN;
if( pExpr->pSelect!=0 ){ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
inMultiplier *= 25; inMultiplier *= 25;
}else if( pExpr->pList ){ inMultIsEst = 1;
inMultiplier *= pExpr->pList->nExpr + 1; }else if( pExpr->x.pList ){
inMultiplier *= pExpr->x.pList->nExpr + 1;
} }
} }
} }
nRow = pProbe->aiRowEst[i] * inMultiplier; nRow = pProbe->aiRowEst[i] * inMultiplier;
cost = nRow * estLog(inMultiplier); /* If inMultiplier is an estimate and that estimate results in an
** nRow it that is more than half number of rows in the table,
** then reduce inMultipler */
if( inMultIsEst && nRow*2 > pProbe->aiRowEst[0] ){
nRow = pProbe->aiRowEst[0]/2;
inMultiplier = nRow/pProbe->aiRowEst[i];
}
cost = nRow + inMultiplier*estLog(pProbe->aiRowEst[0]);
nEq = i; nEq = i;
if( pProbe->onError!=OE_None && (wsFlags & WHERE_COLUMN_IN)==0 if( pProbe->onError!=OE_None && (wsFlags & WHERE_COLUMN_IN)==0
&& nEq==pProbe->nColumn ){ && nEq==pProbe->nColumn ){
wsFlags |= WHERE_UNIQUE; wsFlags |= WHERE_UNIQUE;
} }
WHERETRACE(("...... nEq=%d inMult=%.9g cost=%.9g\n",nEq,inMultiplier,cost)); WHERETRACE(("...... nEq=%d inMult=%.9g nRow=%.9g cost=%.9g\n",
nEq, inMultiplier, nRow, cost));
/* Look for range constraints /* Look for range constraints. Assume that each range constraint
** makes the search space 1/3rd smaller.
*/ */
if( nEq<pProbe->nColumn ){ if( nEq<pProbe->nColumn ){
int j = pProbe->aiColumn[nEq]; int j = pProbe->aiColumn[nEq];
@ -1958,7 +1991,8 @@ static void bestIndex(
cost /= 3; cost /= 3;
nRow /= 3; nRow /= 3;
} }
WHERETRACE(("...... range reduces cost to %.9g\n", cost)); WHERETRACE(("...... range reduces nRow to %.9g and cost to %.9g\n",
nRow, cost));
} }
} }
@ -1978,6 +2012,12 @@ static void bestIndex(
cost += cost*estLog(cost); cost += cost*estLog(cost);
WHERETRACE(("...... orderby increases cost to %.9g\n", cost)); WHERETRACE(("...... orderby increases cost to %.9g\n", cost));
} }
}else if( pParse->db->flags & SQLITE_ReverseOrder ){
/* For application testing, randomly reverse the output order for
** SELECT statements that omit the ORDER BY clause. This will help
** to find cases where
*/
wsFlags |= WHERE_REVERSE;
} }
/* Check to see if we can get away with using just the index without /* Check to see if we can get away with using just the index without
@ -2738,11 +2778,13 @@ static Bitmask codeOneLoopStart(
/* Case 5: There is no usable index. We must do a complete /* Case 5: There is no usable index. We must do a complete
** scan of the entire table. ** scan of the entire table.
*/ */
static const u8 aStep[] = { OP_Next, OP_Prev };
static const u8 aStart[] = { OP_Rewind, OP_Last };
assert( bRev==0 || bRev==1 );
assert( omitTable==0 ); assert( omitTable==0 );
assert( bRev==0 ); pLevel->op = aStep[bRev];
pLevel->op = OP_Next;
pLevel->p1 = iCur; pLevel->p1 = iCur;
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addrBrk); pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
codeRowSetEarly = 0; codeRowSetEarly = 0;
} }
@ -2836,7 +2878,7 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed );
if( pInfo->needToFreeIdxStr ){ if( pInfo->needToFreeIdxStr ){
sqlite3_free(pInfo->idxStr); sqlite3_free(pInfo->idxStr);
} }
sqlite3DbFree(db, pInfo); sqlite3DbFree(db, pInfo);
} }
} }
@ -2943,6 +2985,7 @@ WhereInfo *sqlite3WhereBegin(
int regRowSet /* Register hold RowSet if WHERE_FILL_ROWSET is set */ int regRowSet /* Register hold RowSet if WHERE_FILL_ROWSET is set */
){ ){
int i; /* Loop counter */ int i; /* Loop counter */
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
WhereInfo *pWInfo; /* Will become the return value of this function */ WhereInfo *pWInfo; /* Will become the return value of this function */
Vdbe *v = pParse->pVdbe; /* The virtual database engine */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */
Bitmask notReady; /* Cursors that are not yet positioned */ Bitmask notReady; /* Cursors that are not yet positioned */
@ -2968,15 +3011,19 @@ WhereInfo *sqlite3WhereBegin(
} }
/* Allocate and initialize the WhereInfo structure that will become the /* Allocate and initialize the WhereInfo structure that will become the
** return value. ** return value. A single allocation is used to store the WhereInfo
** struct, the contents of WhereInfo.a[], the WhereClause structure
** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/ */
db = pParse->db; db = pParse->db;
nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel));
pWInfo = sqlite3DbMallocZero(db, pWInfo = sqlite3DbMallocZero(db,
sizeof(WhereInfo) nByteWInfo +
+ (pTabList->nSrc-1)*sizeof(WhereLevel) sizeof(WhereClause) +
+ sizeof(WhereClause) sizeof(WhereMaskSet)
+ sizeof(WhereMaskSet) );
);
if( db->mallocFailed ){ if( db->mallocFailed ){
goto whereBeginError; goto whereBeginError;
} }
@ -2985,7 +3032,7 @@ WhereInfo *sqlite3WhereBegin(
pWInfo->pTabList = pTabList; pWInfo->pTabList = pTabList;
pWInfo->iBreak = sqlite3VdbeMakeLabel(v); pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
pWInfo->regRowSet = (wctrlFlags & WHERE_FILL_ROWSET) ? regRowSet : -1; pWInfo->regRowSet = (wctrlFlags & WHERE_FILL_ROWSET) ? regRowSet : -1;
pWInfo->pWC = pWC = (WhereClause*)&pWInfo->a[pWInfo->nLevel]; pWInfo->pWC = pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo];
pWInfo->wctrlFlags = wctrlFlags; pWInfo->wctrlFlags = wctrlFlags;
pMaskSet = (WhereMaskSet*)&pWC[1]; pMaskSet = (WhereMaskSet*)&pWC[1];
@ -3231,7 +3278,7 @@ WhereInfo *sqlite3WhereBegin(
Bitmask b = pTabItem->colUsed; Bitmask b = pTabItem->colUsed;
int n = 0; int n = 0;
for(; b; b=b>>1, n++){} for(; b; b=b>>1, n++){}
sqlite3VdbeChangeP2(v, sqlite3VdbeCurrentAddr(v)-2, n); sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, SQLITE_INT_TO_PTR(n), P4_INT32);
assert( n<=pTab->nCol ); assert( n<=pTab->nCol );
} }
}else{ }else{
@ -3244,7 +3291,6 @@ WhereInfo *sqlite3WhereBegin(
int iIdxCur = pLevel->iIdxCur; int iIdxCur = pLevel->iIdxCur;
assert( pIx->pSchema==pTab->pSchema ); assert( pIx->pSchema==pTab->pSchema );
assert( iIdxCur>=0 ); assert( iIdxCur>=0 );
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIx->nColumn+1);
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb, sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb,
(char*)pKey, P4_KEYINFO_HANDOFF); (char*)pKey, P4_KEYINFO_HANDOFF);
VdbeComment((v, "%s", pIx->zName)); VdbeComment((v, "%s", pIx->zName));