mirror of
https://github.com/DBD-SQLite/DBD-SQLite
synced 2025-06-07 14:19:10 -04:00
DBD-SQLite: bumped up the bundled library to 3.6.12
This commit is contained in:
parent
735d55875f
commit
ebc9d17ee9
54 changed files with 6121 additions and 5266 deletions
8
alter.c
8
alter.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that used to generate VDBE code
|
||||
** 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"
|
||||
|
||||
|
@ -193,7 +193,7 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
|
|||
*/
|
||||
if( pTab->pSchema!=pTempSchema ){
|
||||
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( !zWhere ){
|
||||
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
|
||||
/* 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);
|
||||
assert( iTrigDb==iDb || iTrigDb==1 );
|
||||
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;
|
||||
pParse->pNewTable = pNew;
|
||||
pNew->nRef = 1;
|
||||
pNew->db = db;
|
||||
pNew->dbMem = pTab->dbMem;
|
||||
pNew->nCol = pTab->nCol;
|
||||
assert( pNew->nCol>0 );
|
||||
nAlloc = (((pNew->nCol-1)/8)*8)+8;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** 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
|
||||
#include "sqliteInt.h"
|
||||
|
@ -74,8 +74,8 @@ static void openStatTable(
|
|||
if( !createStat1 ){
|
||||
sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1");
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 3);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
|
||||
sqlite3VdbeChangeP5(v, createStat1);
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ static void analyzeOneTable(
|
|||
/* Establish a read-lock on the table at the shared-cache level. */
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
iIdxCur = pParse->nTab;
|
||||
iIdxCur = pParse->nTab++;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
int regFields; /* Register block for building records */
|
||||
|
@ -131,7 +131,6 @@ static void analyzeOneTable(
|
|||
*/
|
||||
assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
|
||||
nCol = pIdx->nColumn;
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nCol+1);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
|
||||
(char *)pKey, P4_KEYINFO_HANDOFF);
|
||||
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
|
||||
** 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
|
||||
** 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
|
||||
** 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
|
||||
|
|
12
attach.c
12
attach.c
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** 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"
|
||||
|
||||
|
@ -488,11 +488,11 @@ int sqlite3FixExpr(
|
|||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pExpr->pList) ){
|
||||
return 1;
|
||||
if( ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_SpanOnly) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
|
||||
}else{
|
||||
if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pExpr->pRight) ){
|
||||
return 1;
|
||||
|
|
6
backup.c
6
backup.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains the implementation of the sqlite3_backup_XXX()
|
||||
** 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 "btreeInt.h"
|
||||
|
@ -292,10 +292,10 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
|||
int bCloseTrans = 0; /* True if src db requires unlocking */
|
||||
|
||||
/* 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 ){
|
||||
rc = SQLITE_LOCKED;
|
||||
rc = SQLITE_BUSY;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
|
|
@ -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 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.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** This routine is used only from within assert() statements.
|
||||
|
|
470
btree.c
470
btree.c
|
@ -9,7 +9,7 @@
|
|||
** 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.
|
||||
** 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,
|
||||
** but the test harness needs to access it so we make it global for
|
||||
** test builds.
|
||||
**
|
||||
** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
|
||||
|
@ -68,31 +70,32 @@ int sqlite3_enable_shared_cache(int enable){
|
|||
/*
|
||||
** Forward declaration
|
||||
*/
|
||||
static int checkReadLocks(Btree*, Pgno, BtCursor*, i64);
|
||||
static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64);
|
||||
|
||||
|
||||
#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
|
||||
** shared-cache table level locks. If the library is compiled with the
|
||||
** shared-cache feature disabled, then there is only ever one user
|
||||
** of each BtShared structure and so this locking is not necessary.
|
||||
** So define the lock related functions as no-ops.
|
||||
*/
|
||||
#define queryTableLock(a,b,c) SQLITE_OK
|
||||
#define lockTable(a,b,c) SQLITE_OK
|
||||
#define unlockAllTables(a)
|
||||
#define querySharedCacheTableLock(a,b,c) SQLITE_OK
|
||||
#define setSharedCacheTableLock(a,b,c) SQLITE_OK
|
||||
#define clearAllSharedCacheTableLocks(a)
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
/*
|
||||
** 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
|
||||
** SQLITE_OK if the lock may be obtained (by calling lockTable()), or
|
||||
** SQLITE_LOCKED if not.
|
||||
** SQLITE_OK if the lock may be obtained (by calling
|
||||
** 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;
|
||||
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
|
||||
** requested lock may not be obtained.
|
||||
*/
|
||||
if( pBt->pExclusive && pBt->pExclusive!=p ){
|
||||
return SQLITE_LOCKED;
|
||||
if( pBt->pWriter!=p && pBt->isExclusive ){
|
||||
sqlite3ConnectionBlocked(p->db, pBt->pWriter->db);
|
||||
return SQLITE_LOCKED_SHAREDCACHE;
|
||||
}
|
||||
|
||||
/* This (along with lockTable()) is where the ReadUncommitted flag is
|
||||
** dealt with. If the caller is querying for a read-lock and the flag is
|
||||
** set, it is unconditionally granted - even if there are write-locks
|
||||
/* This (along with setSharedCacheTableLock()) is where
|
||||
** the ReadUncommitted flag is dealt with.
|
||||
** 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
|
||||
** 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
|
||||
** (BtShared.pLock).
|
||||
**
|
||||
** To summarize: If the ReadUncommitted flag is set, then read cursors do
|
||||
** not create or respect table locks. The locking procedure for a
|
||||
** write-cursor does not change.
|
||||
** To summarize: If the ReadUncommitted flag is set, then read cursors
|
||||
** on non-schema tables do not create or respect table locks. The locking
|
||||
** procedure for a write-cursor does not change.
|
||||
*/
|
||||
if(
|
||||
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){
|
||||
if( pIter->pBtree!=p && pIter->iTable==iTab &&
|
||||
(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_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;
|
||||
BtLock *pLock = 0;
|
||||
BtLock *pIter;
|
||||
|
@ -165,12 +176,13 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
|
|||
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,
|
||||
** return early without adding an entry to the BtShared.pLock list. See
|
||||
** comment in function queryTableLock() for more info on handling
|
||||
** the ReadUncommitted flag.
|
||||
/* If the read-uncommitted flag is set and a read-lock is requested on
|
||||
** a non-schema table, then the lock is always granted. Return early
|
||||
** without adding an entry to the BtShared.pLock list. See
|
||||
** comment in function querySharedCacheTableLock() for more info
|
||||
** on handling the ReadUncommitted flag.
|
||||
*/
|
||||
if(
|
||||
(p->db->flags&SQLITE_ReadUncommitted) &&
|
||||
|
@ -217,10 +229,10 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
|
|||
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
/*
|
||||
** Release all the table locks (locks obtained via calls to the lockTable()
|
||||
** procedure) held by Btree handle p.
|
||||
** Release all the table locks (locks obtained via calls to
|
||||
** the setSharedCacheTableLock() procedure) held by Btree handle p.
|
||||
*/
|
||||
static void unlockAllTables(Btree *p){
|
||||
static void clearAllSharedCacheTableLocks(Btree *p){
|
||||
BtShared *pBt = p->pBt;
|
||||
BtLock **ppIter = &pBt->pLock;
|
||||
|
||||
|
@ -229,7 +241,7 @@ static void unlockAllTables(Btree *p){
|
|||
|
||||
while( *ppIter ){
|
||||
BtLock *pLock = *ppIter;
|
||||
assert( pBt->pExclusive==0 || pBt->pExclusive==pLock->pBtree );
|
||||
assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree );
|
||||
if( pLock->pBtree==p ){
|
||||
*ppIter = pLock->pNext;
|
||||
sqlite3_free(pLock);
|
||||
|
@ -238,8 +250,22 @@ static void unlockAllTables(Btree *p){
|
|||
}
|
||||
}
|
||||
|
||||
if( pBt->pExclusive==p ){
|
||||
pBt->pExclusive = 0;
|
||||
assert( pBt->isPending==0 || pBt->pWriter );
|
||||
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 */
|
||||
|
@ -830,7 +856,7 @@ static int defragmentPage(MemPage *pPage){
|
|||
**
|
||||
** If the page contains nBytes of free space but does not contain
|
||||
** 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.
|
||||
*/
|
||||
static int allocateSpace(MemPage *pPage, int nByte){
|
||||
|
@ -1279,6 +1305,12 @@ static void pageReinit(DbPage *pData){
|
|||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
pPage->isInit = 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);
|
||||
}
|
||||
}
|
||||
|
@ -1310,12 +1342,13 @@ int sqlite3BtreeOpen(
|
|||
int flags, /* Options */
|
||||
int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */
|
||||
){
|
||||
sqlite3_vfs *pVfs; /* The VFS to use for this btree */
|
||||
BtShared *pBt = 0; /* Shared part of btree structure */
|
||||
Btree *p; /* Handle to return */
|
||||
int rc = SQLITE_OK;
|
||||
u8 nReserve;
|
||||
unsigned char zDbHeader[100];
|
||||
sqlite3_vfs *pVfs; /* The VFS to use for this btree */
|
||||
BtShared *pBt = 0; /* Shared part of btree structure */
|
||||
Btree *p; /* Handle to return */
|
||||
sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */
|
||||
int rc = SQLITE_OK; /* Result code from this function */
|
||||
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
|
||||
** false for a file-based database. This symbol is only required if
|
||||
|
@ -1361,6 +1394,8 @@ int sqlite3BtreeOpen(
|
|||
return SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
|
||||
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
|
||||
sqlite3_mutex_enter(mutexOpen);
|
||||
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex_enter(mutexShared);
|
||||
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
|
||||
|
@ -1514,6 +1549,10 @@ btree_open_out:
|
|||
sqlite3_free(p);
|
||||
*ppBtree = 0;
|
||||
}
|
||||
if( mutexOpen ){
|
||||
assert( sqlite3_mutex_held(mutexOpen) );
|
||||
sqlite3_mutex_leave(mutexOpen);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1740,6 +1779,12 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
|
|||
int sqlite3BtreeGetPageSize(Btree *p){
|
||||
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 n;
|
||||
sqlite3BtreeEnter(p);
|
||||
|
@ -1774,13 +1819,14 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
|
|||
#else
|
||||
BtShared *pBt = p->pBt;
|
||||
int rc = SQLITE_OK;
|
||||
u8 av = autoVacuum ?1:0;
|
||||
u8 av = (u8)autoVacuum;
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
if( pBt->pageSizeFixed && av!=pBt->autoVacuum ){
|
||||
if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){
|
||||
rc = SQLITE_READONLY;
|
||||
}else{
|
||||
pBt->autoVacuum = av;
|
||||
pBt->autoVacuum = av ?1:0;
|
||||
pBt->incrVacuum = av==2 ?1:0;
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
|
@ -1965,7 +2011,6 @@ static void unlockBtreeIfUnused(BtShared *pBt){
|
|||
releasePage(pBt->pPage1);
|
||||
}
|
||||
pBt->pPage1 = 0;
|
||||
pBt->inStmt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2047,6 +2092,7 @@ static int newDatabase(BtShared *pBt){
|
|||
** proceed.
|
||||
*/
|
||||
int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
||||
sqlite3 *pBlock = 0;
|
||||
BtShared *pBt = p->pBt;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
|
@ -2068,25 +2114,27 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
goto trans_begun;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
/* If another database handle has already opened a write transaction
|
||||
** 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 ){
|
||||
rc = SQLITE_BUSY;
|
||||
goto trans_begun;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
if( wrflag>1 ){
|
||||
if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){
|
||||
pBlock = pBt->pWriter->db;
|
||||
}else if( wrflag>1 ){
|
||||
BtLock *pIter;
|
||||
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
|
||||
if( pIter->pBtree!=p ){
|
||||
rc = SQLITE_BUSY;
|
||||
goto trans_begun;
|
||||
pBlock = pIter->pBtree->db;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pBlock ){
|
||||
sqlite3ConnectionBlocked(p->db, pBlock);
|
||||
rc = SQLITE_LOCKED_SHAREDCACHE;
|
||||
goto trans_begun;
|
||||
}
|
||||
#endif
|
||||
|
||||
do {
|
||||
|
@ -2107,9 +2155,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( wrflag ) pBt->inStmt = 0;
|
||||
}else{
|
||||
if( rc!=SQLITE_OK ){
|
||||
unlockBtreeIfUnused(pBt);
|
||||
}
|
||||
}while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
|
||||
|
@ -2124,9 +2170,10 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
pBt->inTransaction = p->inTrans;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
if( wrflag>1 ){
|
||||
assert( !pBt->pExclusive );
|
||||
pBt->pExclusive = p;
|
||||
if( wrflag ){
|
||||
assert( !pBt->pWriter );
|
||||
pBt->pWriter = p;
|
||||
pBt->isExclusive = (u8)(wrflag>1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -2433,6 +2480,18 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
|
|||
if( nFin==0 ){
|
||||
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--;
|
||||
}
|
||||
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.
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
int sqlite3BtreeIncrVacuum(Btree *p){
|
||||
|
@ -2608,9 +2667,8 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
|
|||
return rc;
|
||||
}
|
||||
pBt->inTransaction = TRANS_READ;
|
||||
pBt->inStmt = 0;
|
||||
}
|
||||
unlockAllTables(p);
|
||||
clearAllSharedCacheTableLocks(p);
|
||||
|
||||
/* If the handle has any kind of transaction open, decrement the transaction
|
||||
** 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);
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
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
|
||||
** the result of a constraint, malloc() failure or IO error) then
|
||||
** the cache may be internally inconsistent (not contain valid trees) so
|
||||
|
@ -2734,7 +2792,7 @@ int sqlite3BtreeRollback(Btree *p){
|
|||
}
|
||||
#endif
|
||||
btreeIntegrity(p);
|
||||
unlockAllTables(p);
|
||||
clearAllSharedCacheTableLocks(p);
|
||||
|
||||
if( p->inTrans==TRANS_WRITE ){
|
||||
int rc2;
|
||||
|
@ -2765,7 +2823,6 @@ int sqlite3BtreeRollback(Btree *p){
|
|||
|
||||
btreeClearHasContent(pBt);
|
||||
p->inTrans = TRANS_NONE;
|
||||
pBt->inStmt = 0;
|
||||
unlockBtreeIfUnused(pBt);
|
||||
|
||||
btreeIntegrity(p);
|
||||
|
@ -2774,29 +2831,33 @@ int sqlite3BtreeRollback(Btree *p){
|
|||
}
|
||||
|
||||
/*
|
||||
** Start a statement subtransaction. The subtransaction can
|
||||
** can be rolled back independently of the main transaction.
|
||||
** You must start a transaction before starting a subtransaction.
|
||||
** The subtransaction is ended automatically if the main transaction
|
||||
** 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.
|
||||
** Start a statement subtransaction. The subtransaction can can be rolled
|
||||
** back independently of the main transaction. You must start a transaction
|
||||
** before starting a subtransaction. The subtransaction is ended automatically
|
||||
** if the main transaction commits or rolls back.
|
||||
**
|
||||
** Statement subtransactions are used around individual SQL statements
|
||||
** that are contained within a BEGIN...COMMIT block. If a constraint
|
||||
** error occurs within the statement, the effect of that one statement
|
||||
** 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;
|
||||
BtShared *pBt = p->pBt;
|
||||
sqlite3BtreeEnter(p);
|
||||
pBt->db = p->db;
|
||||
assert( p->inTrans==TRANS_WRITE );
|
||||
assert( !pBt->inStmt );
|
||||
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;
|
||||
}else{
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
|
@ -2805,55 +2866,7 @@ int sqlite3BtreeBeginStmt(Btree *p){
|
|||
** SQL statements. It is illegal to open, release or rollback any
|
||||
** such savepoints while the statement transaction savepoint is active.
|
||||
*/
|
||||
rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint+1);
|
||||
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;
|
||||
rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement);
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
|
@ -2875,7 +2888,6 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
|
|||
int rc = SQLITE_OK;
|
||||
if( p && p->inTrans==TRANS_WRITE ){
|
||||
BtShared *pBt = p->pBt;
|
||||
assert( pBt->inStmt==0 );
|
||||
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
|
||||
assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) );
|
||||
sqlite3BtreeEnter(p);
|
||||
|
@ -2937,8 +2949,10 @@ static int btreeCursor(
|
|||
if( NEVER(pBt->readOnly) ){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
if( checkReadLocks(p, iTable, 0, 0) ){
|
||||
return SQLITE_LOCKED;
|
||||
rc = checkForReadConflicts(p, iTable, 0, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2976,6 +2990,7 @@ static int btreeCursor(
|
|||
}
|
||||
pBt->pCursor = pCur;
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
pCur->cachedRowid = 0;
|
||||
|
||||
return SQLITE_OK;
|
||||
|
||||
|
@ -2998,11 +3013,48 @@ int sqlite3BtreeCursor(
|
|||
sqlite3BtreeLeave(p);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
|
@ -3064,6 +3116,8 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
|
|||
sqlite3_free(pCur->pKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Make sure the BtCursor* given in the argument has a valid
|
||||
** 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
|
||||
** 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
|
||||
** 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 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
|
||||
** 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));
|
||||
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)++;
|
||||
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
|
||||
** SQLITE_LOCKED.
|
||||
**
|
||||
** As well as cursors with wrFlag==0, cursors with wrFlag==1 and
|
||||
** isIncrblobHandle==1 are also considered 'read' cursors. Incremental
|
||||
** blob cursors are used for both reading and writing.
|
||||
** As well as cursors with wrFlag==0, cursors with
|
||||
** isIncrblobHandle==1 are also considered 'read' cursors because
|
||||
** incremental blob cursors are used for both reading and writing.
|
||||
**
|
||||
** When pgnoRoot is the root page of an intkey table, this function is also
|
||||
** 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
|
||||
** cursors are invalidated.
|
||||
*/
|
||||
static int checkReadLocks(
|
||||
Btree *pBtree,
|
||||
Pgno pgnoRoot,
|
||||
BtCursor *pExclude,
|
||||
i64 iRow
|
||||
static int checkForReadConflicts(
|
||||
Btree *pBtree, /* The database file to check */
|
||||
Pgno pgnoRoot, /* Look for read cursors on this btree */
|
||||
BtCursor *pExclude, /* Ignore this cursor */
|
||||
i64 iRow /* The rowid that might be changing */
|
||||
){
|
||||
BtCursor *p;
|
||||
BtShared *pBt = pBtree->pBt;
|
||||
|
@ -5965,9 +6026,10 @@ static int checkReadLocks(
|
|||
#endif
|
||||
){
|
||||
sqlite3 *dbOther = p->pBtree->db;
|
||||
if( dbOther==0 ||
|
||||
(dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){
|
||||
return SQLITE_LOCKED;
|
||||
assert(dbOther);
|
||||
if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){
|
||||
sqlite3ConnectionBlocked(db, dbOther);
|
||||
return SQLITE_LOCKED_SHAREDCACHE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6004,8 +6066,11 @@ int sqlite3BtreeInsert(
|
|||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
assert( !pBt->readOnly );
|
||||
assert( pCur->wrFlag );
|
||||
if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, nKey) ){
|
||||
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
|
||||
rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey);
|
||||
if( rc ){
|
||||
/* The table pCur points to has a read lock */
|
||||
assert( rc==SQLITE_LOCKED_SHAREDCACHE );
|
||||
return rc;
|
||||
}
|
||||
if( pCur->eState==CURSOR_FAULT ){
|
||||
return pCur->skip;
|
||||
|
@ -6101,8 +6166,11 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||
return SQLITE_ERROR; /* The cursor is not pointing to anything */
|
||||
}
|
||||
assert( pCur->wrFlag );
|
||||
if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, pCur->info.nKey) ){
|
||||
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
|
||||
rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey);
|
||||
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
|
||||
|
@ -6487,7 +6555,7 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
|
|||
sqlite3BtreeEnter(p);
|
||||
pBt->db = p->db;
|
||||
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 */
|
||||
}else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
|
||||
/* nothing to do */
|
||||
|
@ -6533,7 +6601,8 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
|
|||
** occur.
|
||||
*/
|
||||
if( pBt->pCursor ){
|
||||
return SQLITE_LOCKED;
|
||||
sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db);
|
||||
return SQLITE_LOCKED_SHAREDCACHE;
|
||||
}
|
||||
|
||||
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
|
||||
** the sqlite_master table. We grab this lock regardless of whether or
|
||||
** 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 ){
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
|
@ -6699,7 +6769,7 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
|
|||
#endif
|
||||
|
||||
/* Grab the read-lock on page 1. */
|
||||
rc = lockTable(p, 1, READ_LOCK);
|
||||
rc = setSharedCacheTableLock(p, 1, READ_LOCK);
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
}
|
||||
|
@ -6750,6 +6820,75 @@ int sqlite3BtreeFlags(BtCursor *pCur){
|
|||
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
|
||||
|
@ -6986,7 +7125,9 @@ static int checkTreePage(
|
|||
sz = info.nData;
|
||||
if( !pPage->intKey ) sz += (int)info.nKey;
|
||||
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);
|
||||
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
|
@ -7248,14 +7389,6 @@ int sqlite3BtreeIsInTrans(Btree *p){
|
|||
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.
|
||||
*/
|
||||
|
@ -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
|
||||
** handle holds an exclusive lock on the sqlite_master table.
|
||||
** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
|
||||
** btree as the argument handle holds an exclusive lock on the
|
||||
** sqlite_master table. Otherwise SQLITE_OK.
|
||||
*/
|
||||
int sqlite3BtreeSchemaLocked(Btree *p){
|
||||
int rc;
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
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);
|
||||
return rc;
|
||||
}
|
||||
|
@ -7329,9 +7464,9 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
|
|||
assert( READ_LOCK+1==WRITE_LOCK );
|
||||
assert( isWriteLock==0 || isWriteLock==1 );
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = queryTableLock(p, iTab, lockType);
|
||||
rc = querySharedCacheTableLock(p, iTab, lockType);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = lockTable(p, iTab, lockType);
|
||||
rc = setSharedCacheTableLock(p, iTab, lockType);
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
}
|
||||
|
@ -7348,6 +7483,8 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
|
|||
** to change the length of the data stored.
|
||||
*/
|
||||
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
||||
int rc;
|
||||
|
||||
assert( cursorHoldsMutex(pCsr) );
|
||||
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
|
||||
assert(pCsr->isIncrblobHandle);
|
||||
|
@ -7368,8 +7505,11 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
|||
}
|
||||
assert( !pCsr->pBt->readOnly
|
||||
&& pCsr->pBt->inTransaction==TRANS_WRITE );
|
||||
if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0) ){
|
||||
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
|
||||
rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0);
|
||||
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 ){
|
||||
return SQLITE_ERROR;
|
||||
|
|
13
btree.h
13
btree.h
|
@ -13,7 +13,7 @@
|
|||
** subsystem. See comments in the source code for a detailed description
|
||||
** 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_
|
||||
#define _BTREE_H_
|
||||
|
@ -91,12 +91,9 @@ int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
|||
int sqlite3BtreeCommitPhaseTwo(Btree*);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*);
|
||||
int sqlite3BtreeCommitStmt(Btree*);
|
||||
int sqlite3BtreeRollbackStmt(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*,int);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInStmt(Btree*);
|
||||
int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
|
@ -165,6 +162,8 @@ const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
|
|||
const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
|
||||
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
|
||||
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*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
@ -173,6 +172,10 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
|
|||
void sqlite3BtreeCacheOverflow(BtCursor *);
|
||||
void sqlite3BtreeClearCursor(BtCursor *);
|
||||
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
int sqlite3BtreeCount(BtCursor *, i64 *);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
|
||||
void sqlite3BtreeCursorList(Btree*);
|
||||
|
|
31
btreeInt.h
31
btreeInt.h
|
@ -9,7 +9,7 @@
|
|||
** 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.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
|
@ -205,11 +205,6 @@
|
|||
*/
|
||||
#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
|
||||
** size give above.
|
||||
|
@ -356,13 +351,30 @@ struct Btree {
|
|||
** 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
|
||||
** 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 {
|
||||
Pager *pPager; /* The page cache */
|
||||
sqlite3 *db; /* Database connection currently using this Btree */
|
||||
BtCursor *pCursor; /* A list of all open cursors */
|
||||
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 pageSizeFixed; /* True if the page size can no longer be changed */
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
|
@ -385,7 +397,9 @@ struct BtShared {
|
|||
int nRef; /* Number of references to this structure */
|
||||
BtShared *pNext; /* Next on a list of sharable BtShared structs */
|
||||
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
|
||||
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 */
|
||||
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
|
||||
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 */
|
||||
u8 wrFlag; /* True if writable */
|
||||
u8 atLast; /* Cursor pointing to the last entry */
|
||||
|
|
190
build.c
190
build.c
|
@ -22,7 +22,7 @@
|
|||
** COMMIT
|
||||
** 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"
|
||||
|
||||
|
@ -178,19 +178,6 @@ void sqlite3FinishCoding(Parse *pParse){
|
|||
codeTableLocks(pParse);
|
||||
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);
|
||||
#endif
|
||||
assert( pParse->disableColCache==0 ); /* Disables and re-enables match */
|
||||
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3,
|
||||
pParse->nTab+3, pParse->explain);
|
||||
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
|
||||
pParse->nTab, pParse->explain);
|
||||
pParse->rc = SQLITE_DONE;
|
||||
pParse->colNamesSet = 0;
|
||||
}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
|
||||
*/
|
||||
static void freeIndex(Index *p){
|
||||
sqlite3 *db = p->pTable->db;
|
||||
sqlite3 *db = p->pTable->dbMem;
|
||||
sqlite3DbFree(db, p->zColAff);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
@ -480,7 +467,7 @@ void sqlite3CommitInternalChanges(sqlite3 *db){
|
|||
static void sqliteResetColumnNames(Table *pTable){
|
||||
int i;
|
||||
Column *pCol;
|
||||
sqlite3 *db = pTable->db;
|
||||
sqlite3 *db = pTable->dbMem;
|
||||
assert( pTable!=0 );
|
||||
if( (pCol = pTable->aCol)!=0 ){
|
||||
for(i=0; i<pTable->nCol; i++, pCol++){
|
||||
|
@ -511,7 +498,7 @@ void sqlite3DeleteTable(Table *pTable){
|
|||
sqlite3 *db;
|
||||
|
||||
if( pTable==0 ) return;
|
||||
db = pTable->db;
|
||||
db = pTable->dbMem;
|
||||
|
||||
/* Do not delete the table until the reference count reaches zero. */
|
||||
pTable->nRef--;
|
||||
|
@ -616,8 +603,11 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
|
|||
void sqlite3OpenMasterTable(Parse *p, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(p);
|
||||
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);
|
||||
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->pSchema = db->aDb[iDb].pSchema;
|
||||
pTable->nRef = 1;
|
||||
pTable->db = db;
|
||||
pTable->dbMem = db->lookaside.bEnabled ? db : 0;
|
||||
if( pParse->pNewTable ) sqlite3DeleteTable(pParse->pNewTable);
|
||||
pParse->pNewTable = pTable;
|
||||
|
||||
|
@ -901,9 +891,10 @@ void sqlite3StartTable(
|
|||
** The record created does not contain anything yet. It will be replaced
|
||||
** 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 value is needed by the code that sqlite3EndTable will
|
||||
** generate.
|
||||
** The rowid for the new entry is left in register pParse->regRowid.
|
||||
** The root page number of the new table is left in reg pParse->regRoot.
|
||||
** 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( isView || isVirtual ){
|
||||
|
@ -1116,12 +1107,14 @@ void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){
|
|||
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
|
||||
pCol->zName);
|
||||
}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);
|
||||
pCol->pDflt = pCopy = sqlite3ExprDup(db, pExpr);
|
||||
if( pCopy ){
|
||||
sqlite3TokenCopy(db, &pCopy->span, &pExpr->span);
|
||||
}
|
||||
pCol->pDflt = sqlite3ExprDup(
|
||||
db, pExpr, EXPRDUP_REDUCE|EXPRDUP_DISTINCTSPAN
|
||||
);
|
||||
}
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
|
@ -1217,7 +1210,7 @@ void sqlite3AddCheckConstraint(
|
|||
** to malloced space and not the (ephemeral) text of the CREATE TABLE
|
||||
** statement */
|
||||
pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck,
|
||||
sqlite3ExprDup(db, pCheckExpr));
|
||||
sqlite3ExprDup(db, pCheckExpr, 0));
|
||||
}
|
||||
#endif
|
||||
sqlite3ExprDelete(db, pCheckExpr);
|
||||
|
@ -1340,18 +1333,100 @@ static int identLength(const char *z){
|
|||
}
|
||||
|
||||
/*
|
||||
** Write an identifier onto the end of the given string. Add
|
||||
** quote characters as needed.
|
||||
** This function is a wrapper around sqlite3GetToken() used by
|
||||
** 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;
|
||||
int i, j, needQuote;
|
||||
i = *pIdx;
|
||||
|
||||
for(j=0; zIdent[j]; j++){
|
||||
if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
|
||||
}
|
||||
needQuote = zIdent[j]!=0 || sqlite3Isdigit(zIdent[0])
|
||||
|| sqlite3KeywordCode(zIdent, j)!=TK_ID;
|
||||
needQuote = sqlite3Isdigit(zIdent[0]) || 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++] = '"';
|
||||
for(j=0; zIdent[j]; j++){
|
||||
z[i++] = zIdent[j];
|
||||
|
@ -1377,7 +1452,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
|
|||
n += identLength(pCol->zName);
|
||||
z = pCol->zType;
|
||||
if( z ){
|
||||
n += (sqlite3Strlen30(z) + 1);
|
||||
n += identLength(z);
|
||||
}
|
||||
}
|
||||
n += identLength(p->zName);
|
||||
|
@ -1398,18 +1473,17 @@ static char *createTableStmt(sqlite3 *db, Table *p){
|
|||
}
|
||||
sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
|
||||
k = sqlite3Strlen30(zStmt);
|
||||
identPut(zStmt, &k, p->zName);
|
||||
identPut(zStmt, &k, p->zName, 0);
|
||||
zStmt[k++] = '(';
|
||||
for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
|
||||
sqlite3_snprintf(n-k, &zStmt[k], zSep);
|
||||
k += sqlite3Strlen30(&zStmt[k]);
|
||||
zSep = zSep2;
|
||||
identPut(zStmt, &k, pCol->zName);
|
||||
identPut(zStmt, &k, pCol->zName, 0);
|
||||
if( (z = pCol->zType)!=0 ){
|
||||
zStmt[k++] = ' ';
|
||||
assert( (int)(sqlite3Strlen30(z)+k+1)<=n );
|
||||
sqlite3_snprintf(n-k, &zStmt[k], "%s", z);
|
||||
k += sqlite3Strlen30(z);
|
||||
identPut(zStmt, &k, z, 1);
|
||||
}
|
||||
}
|
||||
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
|
||||
** in the SQLITE_MASTER table of the database. The record number
|
||||
** for the new table entry should already be on the stack.
|
||||
** in the SQLITE_MASTER table of the database.
|
||||
**
|
||||
** If this is a TEMPORARY table, write the entry into the auxiliary
|
||||
** file instead of into the main database file.
|
||||
|
@ -1507,9 +1580,8 @@ void sqlite3EndTable(
|
|||
|
||||
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
|
||||
** views. Initialize zType at the same time.
|
||||
/*
|
||||
** Initialize zType for the new view or table.
|
||||
*/
|
||||
if( p->pSelect==0 ){
|
||||
/* A regular table */
|
||||
|
@ -1525,7 +1597,7 @@ void sqlite3EndTable(
|
|||
|
||||
/* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
|
||||
** 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
|
||||
** suitable state to query for the column names and types to be used
|
||||
|
@ -1540,7 +1612,7 @@ void sqlite3EndTable(
|
|||
SelectDest dest;
|
||||
Table *pSelTab;
|
||||
|
||||
assert(pParse->nTab==0);
|
||||
assert(pParse->nTab==1);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
pParse->nTab = 2;
|
||||
|
@ -1571,9 +1643,7 @@ void sqlite3EndTable(
|
|||
|
||||
/* A slot for the record has already been allocated in the
|
||||
** SQLITE_MASTER table. We just need to update that slot with all
|
||||
** the information we've collected. The rowid for the preallocated
|
||||
** 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).
|
||||
** the information we've collected.
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s "
|
||||
|
@ -1701,7 +1771,7 @@ void sqlite3CreateView(
|
|||
** allocated rather than point to the input string - which means that
|
||||
** 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);
|
||||
if( db->mallocFailed ){
|
||||
return;
|
||||
|
@ -1783,11 +1853,13 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
|||
** statement that defines the view.
|
||||
*/
|
||||
assert( pTable->pSelect );
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect);
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
|
||||
if( pSel ){
|
||||
u8 enableLookaside = db->lookaside.bEnabled;
|
||||
n = pParse->nTab;
|
||||
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
||||
pTable->nCol = -1;
|
||||
db->lookaside.bEnabled = 0;
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
|
@ -1796,6 +1868,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
|||
#else
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
#endif
|
||||
db->lookaside.bEnabled = enableLookaside;
|
||||
pParse->nTab = n;
|
||||
if( pSelTab ){
|
||||
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
|
||||
** reflect this.
|
||||
**
|
||||
** The "#%d" in the SQL is a special constant that means whatever value
|
||||
** is on the top of the stack. See sqlite3RegisterExpr().
|
||||
** The "#NNN" in the SQL is a special constant that means whatever value
|
||||
** is in register NNN. See sqlite3RegisterExpr().
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"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
|
||||
** sqlite_temp_master if required.
|
||||
*/
|
||||
pTrigger = pTab->pTrigger;
|
||||
pTrigger = sqlite3TriggerList(pParse, pTab);
|
||||
while( pTrigger ){
|
||||
assert( pTrigger->pSchema==pTab->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){
|
||||
Table *pTab = pIndex->pTable; /* The table that is indexed */
|
||||
int iTab = pParse->nTab; /* Btree cursor used for pTab */
|
||||
int iIdx = pParse->nTab+1; /* Btree cursor used for pIndex */
|
||||
int iTab = pParse->nTab++; /* Btree cursor used for pTab */
|
||||
int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */
|
||||
int addr1; /* Address of top of loop */
|
||||
int tnum; /* Root page of index */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
|
@ -2773,7 +2846,8 @@ void sqlite3CreateIndex(
|
|||
/* Clean up before exiting */
|
||||
exit_create_index:
|
||||
if( pIndex ){
|
||||
freeIndex(pIndex);
|
||||
sqlite3_free(pIndex->zColAff);
|
||||
sqlite3DbFree(db, pIndex);
|
||||
}
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
sqlite3SrcListDelete(db, pTblName);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
** This file contains functions used to access the internal hash tables
|
||||
** 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"
|
||||
|
@ -175,7 +175,7 @@ static CollSeq *findCollSeqEntry(
|
|||
pColl[0].zName[nName] = 0;
|
||||
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
|
||||
** to the hash table).
|
||||
*/
|
||||
|
@ -423,6 +423,7 @@ void sqlite3SchemaFree(void *p){
|
|||
sqlite3HashInit(&pSchema->tblHash, 0);
|
||||
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
|
||||
Table *pTab = sqliteHashData(pElem);
|
||||
assert( pTab->dbMem==0 );
|
||||
sqlite3DeleteTable(pTab);
|
||||
}
|
||||
sqlite3HashClear(&temp1);
|
||||
|
|
53
delete.c
53
delete.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** in order to generate code for DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.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"
|
||||
|
||||
|
@ -77,8 +77,8 @@ void sqlite3OpenTable(
|
|||
v = sqlite3GetVdbe(p);
|
||||
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
|
||||
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);
|
||||
sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
}
|
||||
|
||||
|
@ -99,12 +99,12 @@ void sqlite3MaterializeView(
|
|||
Select *pDup;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
pDup = sqlite3SelectDup(db, pView->pSelect);
|
||||
pDup = sqlite3SelectDup(db, pView->pSelect, 0);
|
||||
if( pWhere ){
|
||||
SrcList *pFrom;
|
||||
Token viewName;
|
||||
|
||||
pWhere = sqlite3ExprDup(db, pWhere);
|
||||
pWhere = sqlite3ExprDup(db, pWhere, 0);
|
||||
viewName.z = (u8*)pView->zName;
|
||||
viewName.n = (unsigned int)sqlite3Strlen30((const char*)viewName.z);
|
||||
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
|
||||
** and the SELECT subtree. */
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc);
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
|
||||
if( pSelectSrc == 0 ) {
|
||||
sqlite3ExprListDelete(pParse->db, pEList);
|
||||
goto limit_where_cleanup_2;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
if( pInClause == 0 ) goto limit_where_cleanup_1;
|
||||
|
||||
pInClause->pSelect = pSelect;
|
||||
pInClause->x.pSelect = pSelect;
|
||||
pInClause->flags |= EP_xIsSelect;
|
||||
sqlite3ExprSetHeight(pParse, pInClause);
|
||||
return pInClause;
|
||||
|
||||
|
@ -238,7 +240,7 @@ void sqlite3DeleteFrom(
|
|||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
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
|
||||
int iBeginAfterTrigger = 0; /* Address of after trigger program */
|
||||
int iEndAfterTrigger = 0; /* Exit of after trigger program */
|
||||
|
@ -265,10 +267,10 @@ void sqlite3DeleteFrom(
|
|||
** deleted from is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_DELETE, 0);
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
|
@ -276,7 +278,7 @@ void sqlite3DeleteFrom(
|
|||
# define isView 0
|
||||
#endif
|
||||
|
||||
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
|
||||
if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
|
@ -287,7 +289,7 @@ void sqlite3DeleteFrom(
|
|||
if( rcauth==SQLITE_DENY ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
assert(!isView || triggers_exist);
|
||||
assert(!isView || pTrigger);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
oldIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
|
@ -322,21 +324,21 @@ void sqlite3DeleteFrom(
|
|||
goto delete_from_cleanup;
|
||||
}
|
||||
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 iGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
addr = sqlite3VdbeMakeLabel(v);
|
||||
|
||||
iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
|
||||
-1, oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
(void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
|
||||
TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
|
||||
iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1,
|
||||
oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
(void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
|
||||
TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
|
||||
sqlite3VdbeJumpHere(v, iGoto);
|
||||
|
@ -373,7 +375,7 @@ void sqlite3DeleteFrom(
|
|||
** It is easier just to erase the whole table. Note, however, that
|
||||
** 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 );
|
||||
sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt);
|
||||
if( !pParse->nested ){
|
||||
|
@ -405,9 +407,8 @@ void sqlite3DeleteFrom(
|
|||
|
||||
/* Open the pseudo-table used to store OLD if there are triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp1(v, OP_OpenPseudo, oldIdx);
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
|
||||
}
|
||||
|
||||
/* 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
|
||||
** an IGNORE constraint, it jumps back to here.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeResolveLabel(v, addr);
|
||||
}
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
|
||||
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
int iData = ++pParse->nMem; /* For storing row data of OLD table */
|
||||
|
||||
/* 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
|
||||
** the AFTER triggers
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
/* Jump back and run the AFTER triggers */
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
|
||||
sqlite3VdbeJumpHere(v, iEndAfterTrigger);
|
||||
|
|
465
expr.c
465
expr.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains routines used for analyzing expressions and
|
||||
** 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"
|
||||
|
||||
|
@ -35,7 +35,8 @@
|
|||
char sqlite3ExprAffinity(Expr *pExpr){
|
||||
int op = pExpr->op;
|
||||
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
|
||||
if( op==TK_CAST ){
|
||||
|
@ -155,11 +156,9 @@ static char comparisonAffinity(Expr *pExpr){
|
|||
aff = sqlite3ExprAffinity(pExpr->pLeft);
|
||||
if( pExpr->pRight ){
|
||||
aff = sqlite3CompareAffinity(pExpr->pRight, aff);
|
||||
}
|
||||
else if( pExpr->pSelect ){
|
||||
aff = sqlite3CompareAffinity(pExpr->pSelect->pEList->a[0].pExpr, aff);
|
||||
}
|
||||
else if( !aff ){
|
||||
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff);
|
||||
}else if( !aff ){
|
||||
aff = SQLITE_AFF_NONE;
|
||||
}
|
||||
return aff;
|
||||
|
@ -345,8 +344,11 @@ static void exprSetHeight(Expr *p){
|
|||
int nHeight = 0;
|
||||
heightOfExpr(p->pLeft, &nHeight);
|
||||
heightOfExpr(p->pRight, &nHeight);
|
||||
heightOfExprList(p->pList, &nHeight);
|
||||
heightOfSelect(p->pSelect, &nHeight);
|
||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
heightOfSelect(p->x.pSelect, &nHeight);
|
||||
}else{
|
||||
heightOfExprList(p->x.pList, &nHeight);
|
||||
}
|
||||
p->nHeight = nHeight + 1;
|
||||
}
|
||||
|
||||
|
@ -402,8 +404,24 @@ Expr *sqlite3Expr(
|
|||
pNew->iAgg = -1;
|
||||
pNew->span.z = (u8*)"";
|
||||
if( pToken ){
|
||||
int c;
|
||||
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 ){
|
||||
if( pRight ){
|
||||
if( pRight->span.dyn==0 && pLeft->span.dyn==0 ){
|
||||
|
@ -492,7 +510,10 @@ void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
|
|||
assert( pLeft!=0 );
|
||||
if( pExpr ){
|
||||
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 );
|
||||
pNew = sqlite3DbMallocZero(db, sizeof(Expr) );
|
||||
if( pNew==0 ){
|
||||
sqlite3ExprListDelete(db, pList); /* Avoid leaking memory when malloc fails */
|
||||
sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */
|
||||
return 0;
|
||||
}
|
||||
pNew->op = TK_FUNCTION;
|
||||
pNew->pList = pList;
|
||||
pNew->x.pList = pList;
|
||||
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
|
||||
assert( pToken->dyn==0 );
|
||||
pNew->token = *pToken;
|
||||
pNew->span = pNew->token;
|
||||
|
||||
pNew->span = *pToken;
|
||||
sqlite3TokenCopy(db, &pNew->token, pToken);
|
||||
sqlite3ExprSetHeight(pParse, pNew);
|
||||
return pNew;
|
||||
}
|
||||
|
@ -607,12 +628,22 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
|
|||
** Substructure is deleted.
|
||||
*/
|
||||
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);
|
||||
sqlite3ExprDelete(db, p->pLeft);
|
||||
sqlite3ExprDelete(db, p->pRight);
|
||||
sqlite3ExprListDelete(db, p->pList);
|
||||
sqlite3SelectDelete(db, p->pSelect);
|
||||
if( !ExprHasAnyProperty(p, EP_TokenOnly|EP_SpanOnly) ){
|
||||
if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z);
|
||||
if( ExprHasProperty(p, EP_Reduced) ){
|
||||
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.
|
||||
** If so, remove the quotation marks.
|
||||
*/
|
||||
void sqlite3DequoteExpr(sqlite3 *db, Expr *p){
|
||||
if( ExprHasAnyProperty(p, EP_Dequoted) ){
|
||||
return;
|
||||
void sqlite3DequoteExpr(Expr *p){
|
||||
if( !ExprHasAnyProperty(p, EP_Dequoted) ){
|
||||
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.
|
||||
**
|
||||
** 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 *pNew;
|
||||
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;
|
||||
Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){
|
||||
return exprDup(db, p, flags, 0);
|
||||
}
|
||||
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( pFrom->z ){
|
||||
pTo->n = pFrom->n;
|
||||
|
@ -680,7 +885,7 @@ void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){
|
|||
pTo->z = 0;
|
||||
}
|
||||
}
|
||||
ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
|
||||
ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
|
||||
ExprList *pNew;
|
||||
struct ExprList_item *pItem, *pOldItem;
|
||||
int i;
|
||||
|
@ -696,17 +901,9 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
|
|||
}
|
||||
pOldItem = p->a;
|
||||
for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){
|
||||
Expr *pNewExpr, *pOldExpr;
|
||||
pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr = pOldItem->pExpr);
|
||||
if( pOldExpr->span.z!=0 && pNewExpr ){
|
||||
/* 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 );
|
||||
Expr *pNewExpr;
|
||||
Expr *pOldExpr = pOldItem->pExpr;
|
||||
pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr, flags);
|
||||
pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
|
||||
pItem->sortOrder = pOldItem->sortOrder;
|
||||
pItem->done = 0;
|
||||
|
@ -724,7 +921,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
|
|||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
|
||||
|| !defined(SQLITE_OMIT_SUBQUERY)
|
||||
SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){
|
||||
SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
|
||||
SrcList *pNew;
|
||||
int i;
|
||||
int nByte;
|
||||
|
@ -750,8 +947,8 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){
|
|||
if( pTab ){
|
||||
pTab->nRef++;
|
||||
}
|
||||
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect);
|
||||
pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn);
|
||||
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
|
||||
pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
|
||||
pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
|
||||
pNewItem->colUsed = pOldItem->colUsed;
|
||||
}
|
||||
|
@ -777,21 +974,24 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
|
|||
}
|
||||
return pNew;
|
||||
}
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p){
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
||||
Select *pNew;
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->pEList = sqlite3ExprListDup(db, p->pEList);
|
||||
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc);
|
||||
pNew->pWhere = sqlite3ExprDup(db, p->pWhere);
|
||||
pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy);
|
||||
pNew->pHaving = sqlite3ExprDup(db, p->pHaving);
|
||||
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy);
|
||||
/* 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 */
|
||||
pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags|EXPRDUP_SPAN);
|
||||
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
|
||||
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->pPrior = sqlite3SelectDup(db, p->pPrior);
|
||||
pNew->pLimit = sqlite3ExprDup(db, p->pLimit);
|
||||
pNew->pOffset = sqlite3ExprDup(db, p->pOffset);
|
||||
pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags);
|
||||
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
|
||||
pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
|
||||
pNew->iLimit = 0;
|
||||
pNew->iOffset = 0;
|
||||
pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
|
||||
|
@ -802,7 +1002,7 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p){
|
|||
return pNew;
|
||||
}
|
||||
#else
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p){
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
||||
assert( p==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
|
||||
** or index instead of generating an epheremal table.
|
||||
*/
|
||||
p = pX->pSelect;
|
||||
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
|
||||
if( isCandidateForInOpt(p) ){
|
||||
sqlite3 *db = pParse->db;
|
||||
Index *pIdx;
|
||||
|
@ -1202,7 +1402,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIdx->nColumn);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
|
||||
pKey,P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
|
@ -1222,7 +1421,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
eType = IN_INDEX_EPH;
|
||||
if( prNotFound ){
|
||||
*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;
|
||||
}
|
||||
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
|
||||
|
@ -1311,7 +1510,7 @@ void sqlite3CodeSubselect(
|
|||
memset(&keyInfo, 0, sizeof(keyInfo));
|
||||
keyInfo.nField = 1;
|
||||
|
||||
if( pExpr->pSelect ){
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
|
@ -1324,15 +1523,15 @@ void sqlite3CodeSubselect(
|
|||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||
dest.affinity = (u8)affinity;
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
if( sqlite3Select(pParse, pExpr->pSelect, &dest) ){
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
return;
|
||||
}
|
||||
pEList = pExpr->pSelect->pEList;
|
||||
pEList = pExpr->x.pSelect->pEList;
|
||||
if( pEList && pEList->nExpr>0 ){
|
||||
keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
||||
pEList->a[0].pExpr);
|
||||
}
|
||||
}else if( pExpr->pList ){
|
||||
}else if( pExpr->x.pList ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
**
|
||||
** For each expression, build an index key from the evaluation and
|
||||
|
@ -1341,7 +1540,7 @@ void sqlite3CodeSubselect(
|
|||
** a column, use numeric affinity.
|
||||
*/
|
||||
int i;
|
||||
ExprList *pList = pExpr->pList;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
struct ExprList_item *pItem;
|
||||
int r1, r2, r3;
|
||||
|
||||
|
@ -1401,7 +1600,8 @@ void sqlite3CodeSubselect(
|
|||
Select *pSel;
|
||||
SelectDest dest;
|
||||
|
||||
pSel = pExpr->pSelect;
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pSel = pExpr->x.pSelect;
|
||||
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
|
||||
if( pExpr->op==TK_SELECT ){
|
||||
dest.eDest = SRT_Mem;
|
||||
|
@ -1795,7 +1995,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
break;
|
||||
}
|
||||
case TK_STRING: {
|
||||
sqlite3DequoteExpr(db, pExpr);
|
||||
sqlite3DequoteExpr(pExpr);
|
||||
sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0,
|
||||
(char*)pExpr->token.z, pExpr->token.n);
|
||||
break;
|
||||
|
@ -1821,9 +2021,26 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
}
|
||||
#endif
|
||||
case TK_VARIABLE: {
|
||||
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iTable, target);
|
||||
if( pExpr->token.n>1 ){
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n);
|
||||
int iPrior;
|
||||
VdbeOp *pOp;
|
||||
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;
|
||||
}
|
||||
|
@ -1985,7 +2202,9 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
}
|
||||
case TK_CONST_FUNC:
|
||||
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;
|
||||
FuncDef *pDef;
|
||||
int nId;
|
||||
|
@ -1995,6 +2214,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
u8 enc = ENC(db);
|
||||
CollSeq *pColl = 0;
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
testcase( op==TK_CONST_FUNC );
|
||||
testcase( op==TK_FUNCTION );
|
||||
zId = (char*)pExpr->token.z;
|
||||
|
@ -2162,7 +2382,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
*/
|
||||
case TK_BETWEEN: {
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
struct ExprList_item *pLItem = pExpr->pList->a;
|
||||
struct ExprList_item *pLItem = pExpr->x.pList->a;
|
||||
Expr *pRight = pLItem->pExpr;
|
||||
|
||||
codeCompareOperands(pParse, pLeft, &r1, ®Free1,
|
||||
|
@ -2222,10 +2442,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
Expr *pX; /* The X expression */
|
||||
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
|
||||
|
||||
assert(pExpr->pList);
|
||||
assert((pExpr->pList->nExpr % 2) == 0);
|
||||
assert(pExpr->pList->nExpr > 0);
|
||||
pEList = pExpr->pList;
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
|
||||
assert((pExpr->x.pList->nExpr % 2) == 0);
|
||||
assert(pExpr->x.pList->nExpr > 0);
|
||||
pEList = pExpr->x.pList;
|
||||
aListelem = pEList->a;
|
||||
nExpr = pEList->nExpr;
|
||||
endLabel = sqlite3VdbeMakeLabel(v);
|
||||
|
@ -2273,15 +2493,15 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
"RAISE() may only be used within a trigger-program");
|
||||
return 0;
|
||||
}
|
||||
if( pExpr->iColumn!=OE_Ignore ){
|
||||
assert( pExpr->iColumn==OE_Rollback ||
|
||||
pExpr->iColumn == OE_Abort ||
|
||||
pExpr->iColumn == OE_Fail );
|
||||
sqlite3DequoteExpr(db, pExpr);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, 0,
|
||||
if( pExpr->affinity!=OE_Ignore ){
|
||||
assert( pExpr->affinity==OE_Rollback ||
|
||||
pExpr->affinity == OE_Abort ||
|
||||
pExpr->affinity == OE_Fail );
|
||||
sqlite3DequoteExpr(pExpr);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0,
|
||||
(char*)pExpr->token.z, pExpr->token.n);
|
||||
} else {
|
||||
assert( pExpr->iColumn == OE_Ignore );
|
||||
assert( pExpr->affinity == OE_Ignore );
|
||||
sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
|
||||
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
|
||||
** instructions.
|
||||
*/
|
||||
ExprList *pList = pExpr->pList;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
if( pList ){
|
||||
int i = pList->nExpr;
|
||||
struct ExprList_item *pItem = pList->a;
|
||||
|
@ -2614,16 +2835,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||
Expr compRight;
|
||||
Expr exprX;
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
exprX = *pExpr->pLeft;
|
||||
exprAnd.op = TK_AND;
|
||||
exprAnd.pLeft = &compLeft;
|
||||
exprAnd.pRight = &compRight;
|
||||
compLeft.op = TK_GE;
|
||||
compLeft.pLeft = &exprX;
|
||||
compLeft.pRight = pExpr->pList->a[0].pExpr;
|
||||
compLeft.pRight = pExpr->x.pList->a[0].pExpr;
|
||||
compRight.op = TK_LE;
|
||||
compRight.pLeft = &exprX;
|
||||
compRight.pRight = pExpr->pList->a[1].pExpr;
|
||||
compRight.pRight = pExpr->x.pList->a[1].pExpr;
|
||||
exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1);
|
||||
testcase( regFree1==0 );
|
||||
exprX.op = TK_REGISTER;
|
||||
|
@ -2765,16 +2987,17 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||
Expr compRight;
|
||||
Expr exprX;
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
exprX = *pExpr->pLeft;
|
||||
exprAnd.op = TK_AND;
|
||||
exprAnd.pLeft = &compLeft;
|
||||
exprAnd.pRight = &compRight;
|
||||
compLeft.op = TK_GE;
|
||||
compLeft.pLeft = &exprX;
|
||||
compLeft.pRight = pExpr->pList->a[0].pExpr;
|
||||
compLeft.pRight = pExpr->x.pList->a[0].pExpr;
|
||||
compRight.op = TK_LE;
|
||||
compRight.pLeft = &exprX;
|
||||
compRight.pRight = pExpr->pList->a[1].pExpr;
|
||||
compRight.pRight = pExpr->x.pList->a[1].pExpr;
|
||||
exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1);
|
||||
testcase( regFree1==0 );
|
||||
exprX.op = TK_REGISTER;
|
||||
|
@ -2813,22 +3036,25 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
|
|||
if( pA==0||pB==0 ){
|
||||
return pB==pA;
|
||||
}
|
||||
if( pA->op!=pB->op ) return 0;
|
||||
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 ){
|
||||
if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){
|
||||
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->op!=TK_COLUMN && pA->token.z ){
|
||||
if( pB->token.z==0 ) return 0;
|
||||
|
@ -2976,12 +3202,13 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
|||
u8 enc = ENC(pParse->db);
|
||||
i = addAggInfoFunc(pParse->db, pAggInfo);
|
||||
if( i>=0 ){
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pItem = &pAggInfo->aFunc[i];
|
||||
pItem->pExpr = pExpr;
|
||||
pItem->iMem = ++pParse->nMem;
|
||||
pItem->pFunc = sqlite3FindFunction(pParse->db,
|
||||
(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 ){
|
||||
pItem->iDistinct = pParse->nTab++;
|
||||
}else{
|
||||
|
|
4
fts3.c
4
fts3.c
|
@ -6410,7 +6410,7 @@ static void optimizeFunc(sqlite3_context *pContext,
|
|||
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 ){
|
||||
assert( i==nReaders );
|
||||
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 );
|
||||
if( pHash ){
|
||||
sqlite3Fts3HashClear(pHash);
|
||||
|
|
14
fts3_expr.c
14
fts3_expr.c
|
@ -212,8 +212,6 @@ static int getNextString(
|
|||
if( ii==0 ){
|
||||
memset(p, 0, nByte);
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
p->eType = FTSQUERY_PHRASE;
|
||||
p->pPhrase->iColumn = pParse->iDefaultCol;
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
p->pPhrase->nToken = ii+1;
|
||||
|
@ -237,19 +235,25 @@ static int getNextString(
|
|||
char *zNew;
|
||||
int nNew = 0;
|
||||
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);
|
||||
if( !p ){
|
||||
goto no_mem;
|
||||
}
|
||||
if( zTemp ){
|
||||
zNew = &(((char *)p)[nByte]);
|
||||
memcpy(zNew, zTemp, nTemp);
|
||||
}else{
|
||||
memset(p, 0, nByte+nTemp);
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
zNew = &(((char *)p)[nByte]);
|
||||
memcpy(zNew, zTemp, nTemp);
|
||||
for(jj=0; jj<p->pPhrase->nToken; jj++){
|
||||
p->pPhrase->aToken[jj].z = &zNew[nNew];
|
||||
nNew += p->pPhrase->aToken[jj].n;
|
||||
}
|
||||
sqlite3_free(zTemp);
|
||||
p->eType = FTSQUERY_PHRASE;
|
||||
p->pPhrase->iColumn = pParse->iDefaultCol;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
|
|
20
func.c
20
func.c
|
@ -16,7 +16,7 @@
|
|||
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: func.c,v 1.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 <stdlib.h>
|
||||
|
@ -1305,9 +1305,9 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
|||
}else{
|
||||
pInfo = (struct compareInfo*)&likeInfoNorm;
|
||||
}
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY,
|
||||
(struct compareInfo*)&globInfo, likeFunc, 0,0);
|
||||
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
|
||||
setLikeOptFlag(db, "like",
|
||||
|
@ -1323,12 +1323,13 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
|||
*/
|
||||
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
FuncDef *pDef;
|
||||
if( pExpr->op!=TK_FUNCTION || !pExpr->pList ){
|
||||
return 0;
|
||||
}
|
||||
if( pExpr->pList->nExpr!=2 ){
|
||||
if( pExpr->op!=TK_FUNCTION
|
||||
|| !pExpr->x.pList
|
||||
|| pExpr->x.pList->nExpr!=2
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pDef = sqlite3FindFunction(db, (char*)pExpr->token.z, pExpr->token.n, 2,
|
||||
SQLITE_UTF8, 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(total, 1, 0, 0, sumStep, totalFinalize ),
|
||||
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(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
|
|
55
insert.c
55
insert.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.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"
|
||||
|
||||
|
@ -168,7 +168,7 @@ static int autoIncBegin(
|
|||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Db *pDb = &pParse->db->aDb[iDb];
|
||||
int iCur = pParse->nTab;
|
||||
int iCur = pParse->nTab++;
|
||||
int addr; /* Address of the top of the loop */
|
||||
assert( v );
|
||||
pParse->nMem++; /* Holds name of table */
|
||||
|
@ -217,7 +217,7 @@ static void autoIncEnd(
|
|||
int memId /* Memory cell holding the maximum rowid */
|
||||
){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
int iCur = pParse->nTab;
|
||||
int iCur = pParse->nTab++;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Db *pDb = &pParse->db->aDb[iDb];
|
||||
int j1;
|
||||
|
@ -401,7 +401,8 @@ void sqlite3Insert(
|
|||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
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
|
||||
|
||||
db = pParse->db;
|
||||
|
@ -431,22 +432,24 @@ void sqlite3Insert(
|
|||
** inserted into is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_INSERT, 0);
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
# define pTrigger 0
|
||||
# define tmask 0
|
||||
# define isView 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
# undef isView
|
||||
# define isView 0
|
||||
#endif
|
||||
assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) );
|
||||
|
||||
/* Ensure that:
|
||||
* (a) the table is not read-only,
|
||||
* (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;
|
||||
}
|
||||
assert( pTab!=0 );
|
||||
|
@ -464,10 +467,10 @@ void sqlite3Insert(
|
|||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) goto insert_cleanup;
|
||||
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( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
newIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
|
@ -482,7 +485,7 @@ void sqlite3Insert(
|
|||
** This is the 2nd template.
|
||||
*/
|
||||
if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){
|
||||
assert( !triggers_exist );
|
||||
assert( !pTrigger );
|
||||
assert( pList==0 );
|
||||
goto insert_cleanup;
|
||||
}
|
||||
|
@ -557,7 +560,7 @@ void sqlite3Insert(
|
|||
** of the tables being read by the SELECT statement. Also use a
|
||||
** 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;
|
||||
}
|
||||
|
||||
|
@ -676,9 +679,8 @@ void sqlite3Insert(
|
|||
|
||||
/* Open the temp table for FOR EACH ROW triggers
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
if( triggers_exist & TRIGGER_BEFORE ){
|
||||
if( tmask & TRIGGER_BEFORE ){
|
||||
int regTrigRowid;
|
||||
int regCols;
|
||||
int regRec;
|
||||
|
@ -813,8 +815,8 @@ void sqlite3Insert(
|
|||
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
|
||||
|
||||
/* Fire BEFORE or INSTEAD OF triggers */
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab,
|
||||
newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
|
||||
pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
|
@ -936,7 +938,7 @@ void sqlite3Insert(
|
|||
regIns,
|
||||
aRegIdx,
|
||||
0,
|
||||
(triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1,
|
||||
(tmask&TRIGGER_AFTER) ? newIdx : -1,
|
||||
appendFlag
|
||||
);
|
||||
}
|
||||
|
@ -948,10 +950,10 @@ void sqlite3Insert(
|
|||
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
|
||||
}
|
||||
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
/* Code AFTER triggers */
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab,
|
||||
newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
|
||||
pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
|
@ -1125,7 +1127,6 @@ void sqlite3GenerateConstraintChecks(
|
|||
if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
|| onError==OE_Ignore || onError==OE_Replace );
|
||||
switch( onError ){
|
||||
|
@ -1133,22 +1134,24 @@ void sqlite3GenerateConstraintChecks(
|
|||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
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",
|
||||
pTab->zName, pTab->aCol[i].zName);
|
||||
sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
|
||||
break;
|
||||
}
|
||||
case OE_Ignore: {
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
|
||||
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
}
|
||||
|
||||
/* Test all CHECK constraints
|
||||
|
@ -1530,7 +1533,7 @@ static int xferOptimization(
|
|||
if( pSelect==0 ){
|
||||
return 0; /* Must be of the form INSERT INTO ... SELECT ... */
|
||||
}
|
||||
if( pDest->pTrigger ){
|
||||
if( sqlite3TriggerList(pParse, pDest) ){
|
||||
return 0; /* tab1 must not have triggers */
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
|
8
legacy.c
8
legacy.c
|
@ -14,7 +14,7 @@
|
|||
** other files are for internal use by SQLite and should not be
|
||||
** 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"
|
||||
|
@ -101,7 +101,7 @@ int sqlite3_exec(
|
|||
}
|
||||
if( xCallback(pArg, nCol, azVals, azCols) ){
|
||||
rc = SQLITE_ABORT;
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3VdbeFinalize((Vdbe *)pStmt);
|
||||
pStmt = 0;
|
||||
sqlite3Error(db, SQLITE_ABORT, 0);
|
||||
goto exec_out;
|
||||
|
@ -109,7 +109,7 @@ int sqlite3_exec(
|
|||
}
|
||||
|
||||
if( rc!=SQLITE_ROW ){
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
rc = sqlite3VdbeFinalize((Vdbe *)pStmt);
|
||||
pStmt = 0;
|
||||
if( rc!=SQLITE_SCHEMA ){
|
||||
nRetry = 0;
|
||||
|
@ -125,7 +125,7 @@ int sqlite3_exec(
|
|||
}
|
||||
|
||||
exec_out:
|
||||
if( pStmt ) sqlite3_finalize(pStmt);
|
||||
if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt);
|
||||
sqlite3DbFree(db, azCols);
|
||||
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
|
|
85
main.c
85
main.c
|
@ -14,7 +14,7 @@
|
|||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.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"
|
||||
|
||||
|
@ -404,12 +404,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
|||
sz = 0;
|
||||
pStart = 0;
|
||||
}else if( pBuf==0 ){
|
||||
sz = (sz + 7)&~7;
|
||||
sz = ROUND8(sz);
|
||||
sqlite3BeginBenignMalloc();
|
||||
pStart = sqlite3Malloc( sz*cnt );
|
||||
sqlite3EndBenignMalloc();
|
||||
}else{
|
||||
sz = sz&~7;
|
||||
sz = ROUNDDOWN8(sz);
|
||||
pStart = pBuf;
|
||||
}
|
||||
db->lookaside.pStart = pStart;
|
||||
|
@ -560,6 +560,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){
|
|||
sqlite3DbFree(db, pTmp);
|
||||
}
|
||||
db->nSavepoint = 0;
|
||||
db->nStatement = 0;
|
||||
db->isTransactionSavepoint = 0;
|
||||
}
|
||||
|
||||
|
@ -629,6 +630,12 @@ int sqlite3_close(sqlite3 *db){
|
|||
}
|
||||
}
|
||||
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->aDb==db->aDbStatic );
|
||||
for(j=0; j<ArraySize(db->aFunc.a); j++){
|
||||
|
@ -1238,15 +1245,15 @@ const char *sqlite3_errmsg(sqlite3 *db){
|
|||
if( !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
return sqlite3ErrStr(SQLITE_MISUSE);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
return sqlite3ErrStr(SQLITE_NOMEM);
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
z = (char*)sqlite3_value_text(db->pErr);
|
||||
assert( !db->mallocFailed );
|
||||
if( z==0 ){
|
||||
z = sqlite3ErrStr(db->errCode);
|
||||
if( db->mallocFailed ){
|
||||
z = sqlite3ErrStr(SQLITE_NOMEM);
|
||||
}else{
|
||||
z = (char*)sqlite3_value_text(db->pErr);
|
||||
assert( !db->mallocFailed );
|
||||
if( z==0 ){
|
||||
z = sqlite3ErrStr(db->errCode);
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return z;
|
||||
|
@ -1258,46 +1265,42 @@ const char *sqlite3_errmsg(sqlite3 *db){
|
|||
** error.
|
||||
*/
|
||||
const void *sqlite3_errmsg16(sqlite3 *db){
|
||||
/* Because all the characters in the string are in the unicode
|
||||
** range 0x00-0xFF, if we pad the big-endian string with a
|
||||
** 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 u16 outOfMem[] = {
|
||||
'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0
|
||||
};
|
||||
static const char misuseBe [] = {
|
||||
0, 'l', 0, 'i', 0, 'b', 0, 'r', 0, 'a', 0, 'r', 0, 'y', 0, ' ',
|
||||
0, 'r', 0, 'o', 0, 'u', 0, 't', 0, 'i', 0, 'n', 0, 'e', 0, ' ',
|
||||
0, 'c', 0, 'a', 0, 'l', 0, 'l', 0, 'e', 0, 'd', 0, ' ',
|
||||
0, 'o', 0, 'u', 0, 't', 0, ' ',
|
||||
0, 'o', 0, 'f', 0, ' ',
|
||||
0, 's', 0, 'e', 0, 'q', 0, 'u', 0, 'e', 0, 'n', 0, 'c', 0, 'e', 0, 0, 0
|
||||
static const u16 misuse[] = {
|
||||
'l', 'i', 'b', 'r', 'a', 'r', 'y', ' ',
|
||||
'r', 'o', 'u', 't', 'i', 'n', 'e', ' ',
|
||||
'c', 'a', 'l', 'l', 'e', 'd', ' ',
|
||||
'o', 'u', 't', ' ',
|
||||
'o', 'f', ' ',
|
||||
's', 'e', 'q', 'u', 'e', 'n', 'c', 'e', 0
|
||||
};
|
||||
|
||||
const void *z;
|
||||
if( !db ){
|
||||
return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
|
||||
return (void *)outOfMem;
|
||||
}
|
||||
if( !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
|
||||
return (void *)misuse;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
z = sqlite3_value_text16(db->pErr);
|
||||
if( z==0 ){
|
||||
sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
|
||||
SQLITE_UTF8, SQLITE_STATIC);
|
||||
if( db->mallocFailed ){
|
||||
z = (void *)outOfMem;
|
||||
}else{
|
||||
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);
|
||||
return z;
|
||||
}
|
||||
|
@ -1940,7 +1943,6 @@ int sqlite3_table_column_metadata(
|
|||
(void)sqlite3SafetyOn(db);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
rc = sqlite3Init(db, &zErrMsg);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
if( SQLITE_OK!=rc ){
|
||||
goto error_out;
|
||||
}
|
||||
|
@ -1996,6 +1998,7 @@ int sqlite3_table_column_metadata(
|
|||
}
|
||||
|
||||
error_out:
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
(void)sqlite3SafetyOff(db);
|
||||
|
||||
/* Whether the function call succeeded or failed, set the output parameters
|
||||
|
|
104
malloc.c
104
malloc.c
|
@ -12,7 +12,7 @@
|
|||
**
|
||||
** 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 <stdarg.h>
|
||||
|
@ -121,7 +121,7 @@ int sqlite3MallocInit(void){
|
|||
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
|
||||
&& sqlite3GlobalConfig.nScratch>=0 ){
|
||||
int i;
|
||||
sqlite3GlobalConfig.szScratch = (sqlite3GlobalConfig.szScratch - 4) & ~7;
|
||||
sqlite3GlobalConfig.szScratch = ROUNDDOWN8(sqlite3GlobalConfig.szScratch-4);
|
||||
mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch)
|
||||
[sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch];
|
||||
for(i=0; i<sqlite3GlobalConfig.nScratch; i++){ mem0.aScratchFree[i] = i; }
|
||||
|
@ -134,7 +134,7 @@ int sqlite3MallocInit(void){
|
|||
&& sqlite3GlobalConfig.nPage>=1 ){
|
||||
int i;
|
||||
int overhead;
|
||||
int sz = sqlite3GlobalConfig.szPage & ~7;
|
||||
int sz = ROUNDDOWN8(sqlite3GlobalConfig.szPage);
|
||||
int n = sqlite3GlobalConfig.nPage;
|
||||
overhead = (4*n + sz - 1)/sz;
|
||||
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
|
||||
*/
|
||||
|
@ -515,6 +426,7 @@ int sqlite3MallocSize(void *p){
|
|||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
if( p==0 ){
|
||||
return 0;
|
||||
}else if( isLookaside(db, p) ){
|
||||
|
@ -544,6 +456,7 @@ void sqlite3_free(void *p){
|
|||
** connection.
|
||||
*/
|
||||
void sqlite3DbFree(sqlite3 *db, void *p){
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
if( isLookaside(db, p) ){
|
||||
LookasideSlot *pBuf = (LookasideSlot*)p;
|
||||
pBuf->pNext = db->lookaside.pFree;
|
||||
|
@ -652,6 +565,7 @@ void *sqlite3DbMallocZero(sqlite3 *db, int n){
|
|||
*/
|
||||
void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
void *p;
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
if( db ){
|
||||
LookasideSlot *pBuf;
|
||||
|
@ -686,6 +600,8 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
|||
*/
|
||||
void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
|
||||
void *pNew = 0;
|
||||
assert( db!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( db->mallocFailed==0 ){
|
||||
if( p==0 ){
|
||||
return sqlite3DbMallocRaw(db, n);
|
||||
|
@ -780,10 +696,10 @@ void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
|
|||
** sqlite3_realloc.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** 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())
|
||||
** is set to SQLITE_NOMEM.
|
||||
*/
|
||||
|
|
8
mem1.c
8
mem1.c
|
@ -17,7 +17,7 @@
|
|||
** This file contains implementations of the low-level memory allocation
|
||||
** 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"
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
|||
static void *sqlite3MemMalloc(int nByte){
|
||||
sqlite3_int64 *p;
|
||||
assert( nByte>0 );
|
||||
nByte = (nByte+7)&~7;
|
||||
nByte = ROUND8(nByte);
|
||||
p = malloc( nByte+8 );
|
||||
if( p ){
|
||||
p[0] = nByte;
|
||||
|
@ -76,7 +76,7 @@ static void sqlite3MemFree(void *pPrior){
|
|||
static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
||||
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
|
||||
assert( pPrior!=0 && nByte>0 );
|
||||
nByte = (nByte+7)&~7;
|
||||
nByte = ROUND8(nByte);
|
||||
p = (sqlite3_int64*)pPrior;
|
||||
p--;
|
||||
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.
|
||||
*/
|
||||
static int sqlite3MemRoundup(int n){
|
||||
return (n+7) & ~7;
|
||||
return ROUND8(n);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
13
mem2.c
13
mem2.c
|
@ -19,7 +19,7 @@
|
|||
** This file contains implementations of the low-level memory allocation
|
||||
** 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"
|
||||
|
||||
|
@ -128,7 +128,7 @@ static struct {
|
|||
** Adjust memory usage statistics
|
||||
*/
|
||||
static void adjustStats(int iSize, int increment){
|
||||
int i = ((iSize+7)&~7)/8;
|
||||
int i = ROUND8(iSize)/8;
|
||||
if( i>NCSIZE-1 ){
|
||||
i = NCSIZE - 1;
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
|
|||
p = (struct MemBlockHdr*)pAllocation;
|
||||
p--;
|
||||
assert( p->iForeGuard==(int)FOREGUARD );
|
||||
nReserve = (p->iSize+7)&~7;
|
||||
nReserve = ROUND8(p->iSize);
|
||||
pInt = (int*)pAllocation;
|
||||
pU8 = (u8*)pAllocation;
|
||||
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.
|
||||
*/
|
||||
static int sqlite3MemRoundup(int n){
|
||||
return (n+7) & ~7;
|
||||
return ROUND8(n);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -225,7 +225,7 @@ static void *sqlite3MemMalloc(int nByte){
|
|||
int nReserve;
|
||||
sqlite3_mutex_enter(mem.mutex);
|
||||
assert( mem.disallow==0 );
|
||||
nReserve = (nByte+7)&~7;
|
||||
nReserve = ROUND8(nByte);
|
||||
totalSize = nReserve + sizeof(*pHdr) + sizeof(int) +
|
||||
mem.nBacktrace*sizeof(void*) + mem.nTitle;
|
||||
p = malloc(totalSize);
|
||||
|
@ -248,6 +248,7 @@ static void *sqlite3MemMalloc(int nByte){
|
|||
void *aAddr[40];
|
||||
pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1;
|
||||
memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*));
|
||||
assert(pBt[0]);
|
||||
if( mem.xBacktrace ){
|
||||
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;
|
||||
memcpy(mem.zTitle, zTitle, n);
|
||||
mem.zTitle[n] = 0;
|
||||
mem.nTitle = (n+7)&~7;
|
||||
mem.nTitle = ROUND8(n);
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
}
|
||||
|
||||
|
|
309
notify.c
Normal file
309
notify.c
Normal 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
202
opcodes.h
|
@ -16,7 +16,7 @@
|
|||
#define OP_RowKey 14
|
||||
#define OP_IsUnique 15
|
||||
#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_Expire 18
|
||||
#define OP_NullRow 20
|
||||
|
@ -43,103 +43,103 @@
|
|||
#define OP_IdxDelete 41
|
||||
#define OP_Sort 42
|
||||
#define OP_ResetCount 43
|
||||
#define OP_NotNull 71 /* same as TK_NOTNULL */
|
||||
#define OP_Ge 77 /* same as TK_GE */
|
||||
#define OP_Remainder 87 /* same as TK_REM */
|
||||
#define OP_Divide 86 /* same as TK_SLASH */
|
||||
#define OP_Integer 44
|
||||
#define OP_Explain 45
|
||||
#define OP_IncrVacuum 46
|
||||
#define OP_AggStep 47
|
||||
#define OP_CreateIndex 48
|
||||
#define OP_NewRowid 49
|
||||
#define OP_And 66 /* same as TK_AND */
|
||||
#define OP_ShiftLeft 81 /* same as TK_LSHIFT */
|
||||
#define OP_Count 44
|
||||
#define OP_NotNull 72 /* same as TK_NOTNULL */
|
||||
#define OP_Ge 78 /* same as TK_GE */
|
||||
#define OP_Remainder 88 /* same as TK_REM */
|
||||
#define OP_Divide 87 /* same as TK_SLASH */
|
||||
#define OP_Integer 45
|
||||
#define OP_Explain 46
|
||||
#define OP_IncrVacuum 47
|
||||
#define OP_AggStep 48
|
||||
#define OP_CreateIndex 49
|
||||
#define OP_NewRowid 50
|
||||
#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_Return 50
|
||||
#define OP_Trace 51
|
||||
#define OP_IfPos 52
|
||||
#define OP_IdxLT 53
|
||||
#define OP_Rewind 54
|
||||
#define OP_SeekGe 55
|
||||
#define OP_Affinity 56
|
||||
#define OP_Gt 74 /* same as TK_GT */
|
||||
#define OP_AddImm 57
|
||||
#define OP_Subtract 84 /* same as TK_MINUS */
|
||||
#define OP_Null 58
|
||||
#define OP_VColumn 59
|
||||
#define OP_Clear 60
|
||||
#define OP_IsNull 70 /* same as TK_ISNULL */
|
||||
#define OP_If 61
|
||||
#define OP_Permutation 62
|
||||
#define OP_Return 51
|
||||
#define OP_Trace 52
|
||||
#define OP_IfPos 53
|
||||
#define OP_IdxLT 54
|
||||
#define OP_Rewind 55
|
||||
#define OP_SeekGe 56
|
||||
#define OP_Affinity 57
|
||||
#define OP_Gt 75 /* same as TK_GT */
|
||||
#define OP_AddImm 58
|
||||
#define OP_Subtract 85 /* same as TK_MINUS */
|
||||
#define OP_Null 59
|
||||
#define OP_VColumn 60
|
||||
#define OP_Clear 61
|
||||
#define OP_IsNull 71 /* same as TK_ISNULL */
|
||||
#define OP_If 62
|
||||
#define OP_Permutation 63
|
||||
#define OP_ToBlob 142 /* same as TK_TO_BLOB */
|
||||
#define OP_RealAffinity 63
|
||||
#define OP_Yield 64
|
||||
#define OP_AggFinal 67
|
||||
#define OP_IfZero 68
|
||||
#define OP_Last 69
|
||||
#define OP_Rowid 78
|
||||
#define OP_Sequence 89
|
||||
#define OP_NotFound 90
|
||||
#define OP_SeekGt 91
|
||||
#define OP_MakeRecord 94
|
||||
#define OP_RealAffinity 64
|
||||
#define OP_HaltIfNull 65
|
||||
#define OP_Yield 68
|
||||
#define OP_AggFinal 69
|
||||
#define OP_IfZero 70
|
||||
#define OP_Last 79
|
||||
#define OP_Rowid 90
|
||||
#define OP_Sequence 91
|
||||
#define OP_NotFound 92
|
||||
#define OP_SeekGt 95
|
||||
#define OP_MakeRecord 96
|
||||
#define OP_ToText 141 /* same as TK_TO_TEXT */
|
||||
#define OP_BitAnd 79 /* same as TK_BITAND */
|
||||
#define OP_Add 83 /* same as TK_PLUS */
|
||||
#define OP_ResultRow 95
|
||||
#define OP_String 96
|
||||
#define OP_Goto 97
|
||||
#define OP_Noop 98
|
||||
#define OP_VCreate 99
|
||||
#define OP_RowSetRead 100
|
||||
#define OP_DropTable 101
|
||||
#define OP_IdxRowid 102
|
||||
#define OP_Insert 103
|
||||
#define OP_Column 104
|
||||
#define OP_BitAnd 80 /* same as TK_BITAND */
|
||||
#define OP_Add 84 /* same as TK_PLUS */
|
||||
#define OP_ResultRow 97
|
||||
#define OP_String 98
|
||||
#define OP_Goto 99
|
||||
#define OP_Noop 100
|
||||
#define OP_VCreate 101
|
||||
#define OP_RowSetRead 102
|
||||
#define OP_DropTable 103
|
||||
#define OP_IdxRowid 104
|
||||
#define OP_Insert 105
|
||||
#define OP_Column 106
|
||||
#define OP_Not 19 /* same as TK_NOT */
|
||||
#define OP_Compare 105
|
||||
#define OP_Le 75 /* same as TK_LE */
|
||||
#define OP_BitOr 80 /* same as TK_BITOR */
|
||||
#define OP_Multiply 85 /* same as TK_STAR */
|
||||
#define OP_String8 93 /* same as TK_STRING */
|
||||
#define OP_VOpen 106
|
||||
#define OP_CreateTable 107
|
||||
#define OP_Found 108
|
||||
#define OP_Seek 109
|
||||
#define OP_Close 110
|
||||
#define OP_Savepoint 111
|
||||
#define OP_Statement 112
|
||||
#define OP_IfNot 113
|
||||
#define OP_Compare 107
|
||||
#define OP_Le 76 /* same as TK_LE */
|
||||
#define OP_BitOr 81 /* same as TK_BITOR */
|
||||
#define OP_Multiply 86 /* same as TK_STAR */
|
||||
#define OP_String8 94 /* same as TK_STRING */
|
||||
#define OP_VOpen 108
|
||||
#define OP_CreateTable 109
|
||||
#define OP_Found 110
|
||||
#define OP_Seek 111
|
||||
#define OP_Close 112
|
||||
#define OP_Savepoint 113
|
||||
#define OP_Statement 114
|
||||
#define OP_IfNot 115
|
||||
#define OP_ToInt 144 /* same as TK_TO_INT */
|
||||
#define OP_VBegin 114
|
||||
#define OP_MemMax 115
|
||||
#define OP_Next 116
|
||||
#define OP_Prev 117
|
||||
#define OP_SeekLe 118
|
||||
#define OP_Lt 76 /* same as TK_LT */
|
||||
#define OP_Ne 72 /* same as TK_NE */
|
||||
#define OP_MustBeInt 119
|
||||
#define OP_ShiftRight 82 /* same as TK_RSHIFT */
|
||||
#define OP_CollSeq 120
|
||||
#define OP_Gosub 121
|
||||
#define OP_ContextPush 122
|
||||
#define OP_ParseSchema 123
|
||||
#define OP_Destroy 124
|
||||
#define OP_IdxGE 125
|
||||
#define OP_ReadCookie 126
|
||||
#define OP_BitNot 92 /* same as TK_BITNOT */
|
||||
#define OP_Or 65 /* same as TK_OR */
|
||||
#define OP_Jump 127
|
||||
#define OP_VBegin 116
|
||||
#define OP_MemMax 117
|
||||
#define OP_Next 118
|
||||
#define OP_Prev 119
|
||||
#define OP_SeekLe 120
|
||||
#define OP_Lt 77 /* same as TK_LT */
|
||||
#define OP_Ne 73 /* same as TK_NE */
|
||||
#define OP_MustBeInt 121
|
||||
#define OP_ShiftRight 83 /* same as TK_RSHIFT */
|
||||
#define OP_CollSeq 122
|
||||
#define OP_Gosub 123
|
||||
#define OP_ContextPush 124
|
||||
#define OP_ParseSchema 125
|
||||
#define OP_Destroy 126
|
||||
#define OP_IdxGE 127
|
||||
#define OP_ReadCookie 128
|
||||
#define OP_BitNot 93 /* same as TK_BITNOT */
|
||||
#define OP_Or 66 /* same as TK_OR */
|
||||
#define OP_Jump 129
|
||||
#define OP_ToReal 145 /* same as TK_TO_REAL */
|
||||
#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/
|
||||
#define OP_Function 128
|
||||
#define OP_Concat 88 /* same as TK_CONCAT */
|
||||
#define OP_SCopy 129
|
||||
#define OP_Int64 131
|
||||
#define OP_Function 131
|
||||
#define OP_Concat 89 /* same as TK_CONCAT */
|
||||
#define OP_SCopy 132
|
||||
#define OP_Int64 133
|
||||
|
||||
/* 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_135 135
|
||||
#define OP_NotUsed_136 136
|
||||
|
@ -164,18 +164,18 @@
|
|||
/* 8 */ 0x08, 0x00, 0x11, 0x00, 0x00, 0x02, 0x00, 0x11,\
|
||||
/* 16 */ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,\
|
||||
/* 24 */ 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x11,\
|
||||
/* 32 */ 0x10, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x08,\
|
||||
/* 40 */ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,\
|
||||
/* 48 */ 0x02, 0x02, 0x04, 0x00, 0x05, 0x11, 0x01, 0x11,\
|
||||
/* 56 */ 0x00, 0x04, 0x02, 0x00, 0x00, 0x05, 0x00, 0x04,\
|
||||
/* 64 */ 0x04, 0x2c, 0x2c, 0x00, 0x05, 0x01, 0x05, 0x05,\
|
||||
/* 72 */ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x02, 0x2c,\
|
||||
/* 32 */ 0x10, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x08,\
|
||||
/* 40 */ 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01,\
|
||||
/* 48 */ 0x00, 0x02, 0x02, 0x04, 0x00, 0x05, 0x11, 0x01,\
|
||||
/* 56 */ 0x11, 0x00, 0x04, 0x02, 0x00, 0x00, 0x05, 0x00,\
|
||||
/* 64 */ 0x04, 0x10, 0x2c, 0x2c, 0x04, 0x00, 0x05, 0x05,\
|
||||
/* 72 */ 0x05, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x01,\
|
||||
/* 80 */ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\
|
||||
/* 88 */ 0x2c, 0x02, 0x11, 0x11, 0x04, 0x02, 0x00, 0x00,\
|
||||
/* 96 */ 0x02, 0x01, 0x00, 0x00, 0x21, 0x00, 0x02, 0x00,\
|
||||
/* 104 */ 0x00, 0x00, 0x00, 0x02, 0x11, 0x08, 0x00, 0x00,\
|
||||
/* 112 */ 0x00, 0x05, 0x00, 0x0c, 0x01, 0x01, 0x11, 0x05,\
|
||||
/* 120 */ 0x00, 0x01, 0x00, 0x00, 0x02, 0x11, 0x02, 0x01,\
|
||||
/* 128 */ 0x00, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,\
|
||||
/* 88 */ 0x2c, 0x2c, 0x02, 0x02, 0x11, 0x04, 0x02, 0x11,\
|
||||
/* 96 */ 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x21, 0x00,\
|
||||
/* 104 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x11, 0x08,\
|
||||
/* 112 */ 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x01, 0x01,\
|
||||
/* 120 */ 0x11, 0x05, 0x00, 0x01, 0x00, 0x00, 0x02, 0x11,\
|
||||
/* 128 */ 0x02, 0x01, 0x02, 0x00, 0x04, 0x02, 0x00, 0x00,\
|
||||
/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\
|
||||
/* 144 */ 0x04, 0x04,}
|
||||
|
|
7
os.c
7
os.c
|
@ -13,7 +13,7 @@
|
|||
** This file contains OS interface code that is common to all
|
||||
** 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
|
||||
#include "sqliteInt.h"
|
||||
|
@ -112,8 +112,11 @@ int sqlite3OsOpen(
|
|||
int flags,
|
||||
int *pFlagsOut
|
||||
){
|
||||
int rc;
|
||||
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){
|
||||
return pVfs->xDelete(pVfs, zPath, dirSync);
|
||||
|
|
11
os_common.h
11
os_common.h
|
@ -17,7 +17,7 @@
|
|||
** This file should be #included by the os_*.c files only. It is not a
|
||||
** 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_
|
||||
#define _OS_COMMON_H_
|
||||
|
@ -31,15 +31,6 @@
|
|||
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
||||
#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
|
||||
int sqlite3OSTrace = 0;
|
||||
#define OSTRACE1(X) if( sqlite3OSTrace ) sqlite3DebugPrintf(X)
|
||||
|
|
90
os_unix.c
90
os_unix.c
|
@ -43,7 +43,7 @@
|
|||
** * Definitions of sqlite3_vfs objects for all locking methods
|
||||
** 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"
|
||||
#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) ){
|
||||
pFile->lastErrno = tErrno;
|
||||
}
|
||||
goto end_unlock;
|
||||
goto end_unlock;
|
||||
}
|
||||
}
|
||||
if( locktype==NO_LOCK ){
|
||||
struct unixOpenCnt *pOpen;
|
||||
int rc2 = SQLITE_OK;
|
||||
|
||||
/* Decrement the shared lock counter. Release the lock using an
|
||||
** 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) ){
|
||||
pFile->lastErrno = tErrno;
|
||||
}
|
||||
pLock->cnt = 1;
|
||||
goto end_unlock;
|
||||
pLock->locktype = NO_LOCK;
|
||||
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
|
||||
** was deferred because of outstanding locks.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
pOpen = pFile->pOpen;
|
||||
pOpen->nLock--;
|
||||
assert( pOpen->nLock>=0 );
|
||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||
int i;
|
||||
for(i=0; i<pOpen->nPending; i++){
|
||||
/* close pending fds, but if closing fails don't free the array
|
||||
** assign -1 to the successfully closed descriptors and record the
|
||||
** error. The next attempt to unlock will try again. */
|
||||
if( pOpen->aPending[i] < 0 ) continue;
|
||||
if( close(pOpen->aPending[i]) ){
|
||||
pFile->lastErrno = errno;
|
||||
rc = SQLITE_IOERR_CLOSE;
|
||||
}else{
|
||||
pOpen->aPending[i] = -1;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_free(pOpen->aPending);
|
||||
pOpen->nPending = 0;
|
||||
pOpen->aPending = 0;
|
||||
pOpen = pFile->pOpen;
|
||||
pOpen->nLock--;
|
||||
assert( pOpen->nLock>=0 );
|
||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||
int i;
|
||||
for(i=0; i<pOpen->nPending; i++){
|
||||
/* close pending fds, but if closing fails don't free the array
|
||||
** assign -1 to the successfully closed descriptors and record the
|
||||
** error. The next attempt to unlock will try again. */
|
||||
if( pOpen->aPending[i] < 0 ) continue;
|
||||
if( close(pOpen->aPending[i]) ){
|
||||
pFile->lastErrno = errno;
|
||||
rc2 = SQLITE_IOERR_CLOSE;
|
||||
}else{
|
||||
pOpen->aPending[i] = -1;
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
/*
|
||||
** Use the fdatasync() API only if the HAVE_FDATASYNC macro is defined.
|
||||
** Otherwise use fsync() in its place.
|
||||
** We do not trust systems to provide a working fdatasync(). Some do.
|
||||
** 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
|
||||
#endif
|
||||
|
||||
|
@ -2853,6 +2857,19 @@ int sqlite3_fullsync_count = 0;
|
|||
** You are strongly advised *not* to deploy with SQLITE_NO_SYNC
|
||||
** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash
|
||||
** 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){
|
||||
int rc;
|
||||
|
@ -2869,6 +2886,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
|||
UNUSED_PARAMETER(dataOnly);
|
||||
#else
|
||||
UNUSED_PARAMETER(fullSync);
|
||||
UNUSED_PARAMETER(dataOnly);
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
|
||||
#else
|
||||
if( dataOnly ){
|
||||
rc = fdatasync(fd);
|
||||
rc = fdatasync(fd);
|
||||
#if OS_VXWORKS
|
||||
if( rc==-1 && errno==ENOTSUP ){
|
||||
rc = fsync(fd);
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
if( rc==-1 && errno==ENOTSUP ){
|
||||
rc = fsync(fd);
|
||||
}
|
||||
#endif /* OS_VXWORKS */
|
||||
#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */
|
||||
|
||||
if( OS_VXWORKS && rc!= -1 ){
|
||||
|
@ -3984,16 +3998,18 @@ static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
|
|||
sp.tv_sec = microseconds / 1000000;
|
||||
sp.tv_nsec = (microseconds % 1000000) * 1000;
|
||||
nanosleep(&sp, NULL);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return microseconds;
|
||||
#elif defined(HAVE_USLEEP) && HAVE_USLEEP
|
||||
usleep(microseconds);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return microseconds;
|
||||
#else
|
||||
int seconds = (microseconds+999999)/1000000;
|
||||
sleep(seconds);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return seconds*1000000;
|
||||
#endif
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
178
os_win.c
178
os_win.c
|
@ -12,7 +12,7 @@
|
|||
**
|
||||
** 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"
|
||||
#if SQLITE_OS_WIN /* This file is used for windows only */
|
||||
|
@ -75,6 +75,7 @@
|
|||
*/
|
||||
#if SQLITE_OS_WINCE
|
||||
# define AreFileApisANSI() 1
|
||||
# define GetDiskFreeSpaceW() 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -101,6 +102,7 @@ struct winFile {
|
|||
unsigned char locktype; /* Type of lock currently held on this file */
|
||||
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
|
||||
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
|
||||
WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
|
||||
HANDLE hMutex; /* Mutex used to control access to shared lock */
|
||||
|
@ -110,6 +112,13 @@ struct winFile {
|
|||
#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
|
||||
|
@ -135,7 +144,7 @@ static int sqlite3_os_type = 0;
|
|||
**
|
||||
** Here is an interesting observation: Win95, Win98, and WinME lack
|
||||
** 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
|
||||
** WinNT/2K/XP so that we will know whether or not we can safely call
|
||||
** the LockFileEx() API.
|
||||
|
@ -610,6 +619,8 @@ static BOOL winceLockFileEx(
|
|||
static int winClose(sqlite3_file *id){
|
||||
int rc, cnt = 0;
|
||||
winFile *pFile = (winFile*)id;
|
||||
|
||||
assert( id!=0 );
|
||||
OSTRACE2("CLOSE %d\n", pFile->h);
|
||||
do{
|
||||
rc = CloseHandle(pFile->h);
|
||||
|
@ -654,9 +665,10 @@ static int winRead(
|
|||
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(offset & 0xffffffff);
|
||||
DWORD rc;
|
||||
DWORD got;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error;
|
||||
DWORD got;
|
||||
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype);
|
||||
|
@ -691,9 +703,10 @@ static int winWrite(
|
|||
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(offset & 0xffffffff);
|
||||
DWORD rc;
|
||||
DWORD wrote = 0;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error;
|
||||
DWORD wrote = 0;
|
||||
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_WRITE);
|
||||
SimulateDiskfullError(return SQLITE_FULL);
|
||||
|
@ -723,26 +736,26 @@ static int winWrite(
|
|||
** Truncate an open file to a specified size
|
||||
*/
|
||||
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
DWORD rc;
|
||||
LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(nByte & 0xffffffff);
|
||||
DWORD rc;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error = NO_ERROR;
|
||||
DWORD error;
|
||||
|
||||
assert( id!=0 );
|
||||
OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte);
|
||||
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
||||
rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
||||
if( INVALID_SET_FILE_POINTER == rc ){
|
||||
error = GetLastError();
|
||||
if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
if( error == NO_ERROR ){
|
||||
/* SetEndOfFile will fail if nByte is negative */
|
||||
if( SetEndOfFile(pFile->h) ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
error = GetLastError();
|
||||
/* SetEndOfFile will fail if nByte is negative */
|
||||
if( !SetEndOfFile(pFile->h) ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
@ -760,6 +773,8 @@ int sqlite3_fullsync_count = 0;
|
|||
static int winSync(sqlite3_file *id, int flags){
|
||||
#ifndef SQLITE_NO_SYNC
|
||||
winFile *pFile = (winFile*)id;
|
||||
|
||||
assert( id!=0 );
|
||||
OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype);
|
||||
#else
|
||||
UNUSED_PARAMETER(id);
|
||||
|
@ -791,9 +806,12 @@ static int winSync(sqlite3_file *id, int flags){
|
|||
** Determine the current size of a file in bytes
|
||||
*/
|
||||
static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
|
||||
DWORD upperBits;
|
||||
DWORD lowerBits;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD upperBits, lowerBits;
|
||||
DWORD error;
|
||||
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_FSTAT);
|
||||
lowerBits = GetFileSize(pFile->h, &upperBits);
|
||||
if( (lowerBits == INVALID_FILE_SIZE)
|
||||
|
@ -897,7 +915,7 @@ static int winLock(sqlite3_file *id, int locktype){
|
|||
winFile *pFile = (winFile*)id;
|
||||
DWORD error = NO_ERROR;
|
||||
|
||||
assert( pFile!=0 );
|
||||
assert( id!=0 );
|
||||
OSTRACE5("LOCK %d %d was %d(%d)\n",
|
||||
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){
|
||||
int rc;
|
||||
winFile *pFile = (winFile*)id;
|
||||
assert( pFile!=0 );
|
||||
|
||||
assert( id!=0 );
|
||||
if( pFile->locktype>=RESERVED_LOCK ){
|
||||
rc = 1;
|
||||
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.
|
||||
*/
|
||||
static int winSectorSize(sqlite3_file *id){
|
||||
UNUSED_PARAMETER(id);
|
||||
return SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
assert( id!=0 );
|
||||
return (int)(((winFile*)id)->sectorSize);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1245,7 +1264,6 @@ static int getLastErrorMsg(int nBuf, char *zBuf){
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Open a file.
|
||||
*/
|
||||
|
@ -1269,6 +1287,7 @@ static int winOpen(
|
|||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||||
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
|
||||
|
||||
assert( id!=0 );
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
|
||||
/* If the second argument to this function is NULL, generate a
|
||||
|
@ -1348,7 +1367,7 @@ static int winOpen(
|
|||
if( h==INVALID_HANDLE_VALUE ){
|
||||
free(zConverted);
|
||||
if( flags & SQLITE_OPEN_READWRITE ){
|
||||
return winOpen(0, zName, id,
|
||||
return winOpen(pVfs, zName, id,
|
||||
((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
|
||||
}else{
|
||||
return SQLITE_CANTOPEN;
|
||||
|
@ -1365,6 +1384,7 @@ static int winOpen(
|
|||
pFile->pMethod = &winIoMethod;
|
||||
pFile->h = h;
|
||||
pFile->lastErrno = NO_ERROR;
|
||||
pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
|
||||
#if SQLITE_OS_WINCE
|
||||
if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
|
||||
(SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
|
||||
|
@ -1556,6 +1576,73 @@ static int winFullPathname(
|
|||
#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
|
||||
/*
|
||||
** 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
|
||||
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
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
|
@ -1689,25 +1790,14 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
|||
GetSystemTimeAsFileTime( &ft );
|
||||
#endif
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
#if defined(_MSC_VER)
|
||||
timeW = (((sqlite3_int64)ft.dwHighDateTime)*4294967296) + ft.dwLowDateTime;
|
||||
timeF = timeW % 864000000000; /* fractional days (100-nanoseconds) */
|
||||
timeW = timeW / 864000000000; /* whole days */
|
||||
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
|
||||
timeF = timeF + 432000000000; /* add half a day (from 2305813.5) */
|
||||
timeW = timeW + (timeF / 864000000000); /* add whole day if half day made one */
|
||||
timeF = timeF % 864000000000; /* compute new fractional days */
|
||||
*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
|
||||
timeW = (((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime;
|
||||
timeF = timeW % ntuPerDay; /* fractional days (100-nanoseconds) */
|
||||
timeW = timeW / ntuPerDay; /* whole days */
|
||||
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
|
||||
timeF = timeF + ntuPerHalfDay; /* add half a day (from 2305813.5) */
|
||||
timeW = timeW + (timeF/ntuPerDay); /* add whole day if half day made one */
|
||||
timeF = timeF % ntuPerDay; /* compute new fractional days */
|
||||
*prNow = (double)timeW + ((double)timeF / (double)ntuPerDay);
|
||||
#ifdef SQLITE_TEST
|
||||
if( sqlite3_current_time ){
|
||||
*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
|
||||
** a buffer of nBuf bytes. The OS layer should populate the
|
||||
** 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.
|
||||
**
|
||||
** If the error message is too large for the supplied buffer,
|
||||
|
|
101
pager.c
101
pager.c
|
@ -18,7 +18,7 @@
|
|||
** file simultaneously, or one process from reading the database while
|
||||
** 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
|
||||
#include "sqliteInt.h"
|
||||
|
@ -99,12 +99,6 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
|
|||
#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
|
||||
#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
|
||||
*/
|
||||
|
@ -762,7 +756,7 @@ static int writeJournalHdr(Pager *pPager){
|
|||
** A faster alternative is to write 0xFFFFFFFF to the nRec field. When
|
||||
** reading the journal this value tells SQLite to assume that the
|
||||
** 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
|
||||
** 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
|
||||
** 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
|
||||
** API function. The value returned is a copy of the second argument
|
||||
** to this function.
|
||||
|
@ -1156,7 +1150,7 @@ static void pager_unlock(Pager *pPager){
|
|||
** A persistent error indicates that the contents of the pager-cache
|
||||
** cannot be trusted. This state can be cleared by completely discarding
|
||||
** 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
|
||||
** 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.
|
||||
*/
|
||||
pPg = pager_lookup(pPager, pgno);
|
||||
assert( pPg || !MEMDB );
|
||||
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
|
||||
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData),
|
||||
(isMainJrnl?"main-journal":"sub-journal")
|
||||
|
@ -2054,7 +2049,7 @@ end_playback:
|
|||
);
|
||||
|
||||
/* 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
|
||||
** modification may just have been reverted. If this happens in exclusive
|
||||
** mode, then subsequent transactions performed by the connection will not
|
||||
|
@ -3268,7 +3263,7 @@ int sqlite3PagerOpen(
|
|||
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.
|
||||
*/
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -3279,7 +3274,7 @@ int sqlite3PagerOpen(
|
|||
}
|
||||
|
||||
/* Initialize the PCache object. */
|
||||
nExtra = FORCE_ALIGNMENT(nExtra);
|
||||
nExtra = ROUND8(nExtra);
|
||||
sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
|
||||
!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
|
||||
** 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
|
||||
** 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
|
||||
** * 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
|
||||
** 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
|
||||
** is returned.
|
||||
**
|
||||
** This routine does not open the journal file to examine its
|
||||
** content. Hence, the journal might contain the name of a master
|
||||
** journal file that has been deleted, and hence not be hot. Or
|
||||
** the header of the journal might be zeroed out. This routine
|
||||
** does not discover these cases of a non-hot journal - if the
|
||||
** journal file exists and is not empty this routine assumes it
|
||||
** is hot. The pager_playback() routine will discover that the
|
||||
** journal file is not really hot and will no-op.
|
||||
** This routine does not check if there is a master journal filename
|
||||
** at the end of the file. If there is, and that master journal file
|
||||
** does not exist, then the journal file is not really hot. In this
|
||||
** case this routine will return a false-positive. The pager_playback()
|
||||
** routine will discover that the journal file is not really hot and
|
||||
** will not roll it back.
|
||||
**
|
||||
** 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
|
||||
|
@ -3365,29 +3359,52 @@ int sqlite3PagerOpen(
|
|||
static int hasHotJournal(Pager *pPager, int *pExists){
|
||||
sqlite3_vfs * const pVfs = pPager->pVfs;
|
||||
int rc; /* Return code */
|
||||
int exists = 0; /* True if a journal file is present */
|
||||
int locked = 0; /* True if some process holds a RESERVED lock */
|
||||
int exists; /* True if a journal file is present */
|
||||
|
||||
assert( pPager!=0 );
|
||||
assert( pPager->useJournal );
|
||||
assert( isOpen(pPager->fd) );
|
||||
assert( !isOpen(pPager->jfd) );
|
||||
|
||||
*pExists = 0;
|
||||
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc==SQLITE_OK && exists ){
|
||||
int locked; /* True if some process holds a RESERVED lock */
|
||||
rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
|
||||
if( rc==SQLITE_OK && !locked ){
|
||||
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);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( nPage==0 ){
|
||||
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
|
||||
if( nPage==0 ){
|
||||
rc = sqlite3OsDelete(pVfs, pPager->zJournal, 0);
|
||||
}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;
|
||||
}
|
||||
|
||||
|
@ -3413,10 +3430,13 @@ static int readDbPage(PgHdr *pPg){
|
|||
if( !isOpen(pPager->fd) ){
|
||||
assert( pPager->tempFile );
|
||||
memset(pPg->pData, 0, pPager->pageSize);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
iOffset = (pgno-1)*(i64)pPager->pageSize;
|
||||
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
|
||||
if( rc==SQLITE_IOERR_SHORT_READ ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
if( pgno==1 ){
|
||||
u8 *dbFileVers = &((u8*)pPg->pData)[24];
|
||||
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
|
||||
|
@ -3800,7 +3820,7 @@ int sqlite3PagerAcquire(
|
|||
}else{
|
||||
assert( pPg->pPager==pPager );
|
||||
rc = readDbPage(pPg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
|
||||
if( rc!=SQLITE_OK ){
|
||||
pagerDropPage(pPg);
|
||||
return rc;
|
||||
}
|
||||
|
@ -3933,7 +3953,7 @@ static int pager_open_journal(Pager *pPager){
|
|||
sqlite3MemJournalOpen(pPager->jfd);
|
||||
}else{
|
||||
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 ?
|
||||
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
|
||||
(SQLITE_OPEN_MAIN_JOURNAL)
|
||||
|
@ -4135,7 +4155,7 @@ static int pager_write(PgHdr *pPg){
|
|||
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.
|
||||
*/
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -4851,7 +4871,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
|||
** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE.
|
||||
** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with
|
||||
** 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
|
||||
** 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. */
|
||||
Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
|
||||
int rc; /* Return code */
|
||||
Pgno origPgno; /* The original page number */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
origPgno = pPg->pgno;
|
||||
sqlite3PcacheMove(pPg, pgno);
|
||||
if( pPgOld ){
|
||||
sqlite3PcacheDrop(pPgOld);
|
||||
|
@ -5097,6 +5119,19 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
|||
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;
|
||||
}
|
||||
#endif
|
||||
|
|
192
parse.h
192
parse.h
|
@ -13,8 +13,8 @@
|
|||
#define TK_SAVEPOINT 13
|
||||
#define TK_RELEASE 14
|
||||
#define TK_TO 15
|
||||
#define TK_CREATE 16
|
||||
#define TK_TABLE 17
|
||||
#define TK_TABLE 16
|
||||
#define TK_CREATE 17
|
||||
#define TK_IF 18
|
||||
#define TK_NOT 19
|
||||
#define TK_EXISTS 20
|
||||
|
@ -24,100 +24,100 @@
|
|||
#define TK_AS 24
|
||||
#define TK_COMMA 25
|
||||
#define TK_ID 26
|
||||
#define TK_ABORT 27
|
||||
#define TK_AFTER 28
|
||||
#define TK_ANALYZE 29
|
||||
#define TK_ASC 30
|
||||
#define TK_ATTACH 31
|
||||
#define TK_BEFORE 32
|
||||
#define TK_BY 33
|
||||
#define TK_CASCADE 34
|
||||
#define TK_CAST 35
|
||||
#define TK_COLUMNKW 36
|
||||
#define TK_CONFLICT 37
|
||||
#define TK_DATABASE 38
|
||||
#define TK_DESC 39
|
||||
#define TK_DETACH 40
|
||||
#define TK_EACH 41
|
||||
#define TK_FAIL 42
|
||||
#define TK_FOR 43
|
||||
#define TK_IGNORE 44
|
||||
#define TK_INITIALLY 45
|
||||
#define TK_INSTEAD 46
|
||||
#define TK_LIKE_KW 47
|
||||
#define TK_MATCH 48
|
||||
#define TK_KEY 49
|
||||
#define TK_OF 50
|
||||
#define TK_OFFSET 51
|
||||
#define TK_PRAGMA 52
|
||||
#define TK_RAISE 53
|
||||
#define TK_REPLACE 54
|
||||
#define TK_RESTRICT 55
|
||||
#define TK_ROW 56
|
||||
#define TK_TRIGGER 57
|
||||
#define TK_VACUUM 58
|
||||
#define TK_VIEW 59
|
||||
#define TK_VIRTUAL 60
|
||||
#define TK_REINDEX 61
|
||||
#define TK_RENAME 62
|
||||
#define TK_CTIME_KW 63
|
||||
#define TK_ANY 64
|
||||
#define TK_OR 65
|
||||
#define TK_AND 66
|
||||
#define TK_IS 67
|
||||
#define TK_BETWEEN 68
|
||||
#define TK_IN 69
|
||||
#define TK_ISNULL 70
|
||||
#define TK_NOTNULL 71
|
||||
#define TK_NE 72
|
||||
#define TK_EQ 73
|
||||
#define TK_GT 74
|
||||
#define TK_LE 75
|
||||
#define TK_LT 76
|
||||
#define TK_GE 77
|
||||
#define TK_ESCAPE 78
|
||||
#define TK_BITAND 79
|
||||
#define TK_BITOR 80
|
||||
#define TK_LSHIFT 81
|
||||
#define TK_RSHIFT 82
|
||||
#define TK_PLUS 83
|
||||
#define TK_MINUS 84
|
||||
#define TK_STAR 85
|
||||
#define TK_SLASH 86
|
||||
#define TK_REM 87
|
||||
#define TK_CONCAT 88
|
||||
#define TK_COLLATE 89
|
||||
#define TK_UMINUS 90
|
||||
#define TK_UPLUS 91
|
||||
#define TK_BITNOT 92
|
||||
#define TK_STRING 93
|
||||
#define TK_JOIN_KW 94
|
||||
#define TK_CONSTRAINT 95
|
||||
#define TK_DEFAULT 96
|
||||
#define TK_NULL 97
|
||||
#define TK_PRIMARY 98
|
||||
#define TK_UNIQUE 99
|
||||
#define TK_CHECK 100
|
||||
#define TK_REFERENCES 101
|
||||
#define TK_AUTOINCR 102
|
||||
#define TK_ON 103
|
||||
#define TK_DELETE 104
|
||||
#define TK_UPDATE 105
|
||||
#define TK_INSERT 106
|
||||
#define TK_SET 107
|
||||
#define TK_DEFERRABLE 108
|
||||
#define TK_FOREIGN 109
|
||||
#define TK_DROP 110
|
||||
#define TK_UNION 111
|
||||
#define TK_ALL 112
|
||||
#define TK_EXCEPT 113
|
||||
#define TK_INTERSECT 114
|
||||
#define TK_SELECT 115
|
||||
#define TK_DISTINCT 116
|
||||
#define TK_DOT 117
|
||||
#define TK_FROM 118
|
||||
#define TK_JOIN 119
|
||||
#define TK_INDEXED 120
|
||||
#define TK_INDEXED 27
|
||||
#define TK_ABORT 28
|
||||
#define TK_AFTER 29
|
||||
#define TK_ANALYZE 30
|
||||
#define TK_ASC 31
|
||||
#define TK_ATTACH 32
|
||||
#define TK_BEFORE 33
|
||||
#define TK_BY 34
|
||||
#define TK_CASCADE 35
|
||||
#define TK_CAST 36
|
||||
#define TK_COLUMNKW 37
|
||||
#define TK_CONFLICT 38
|
||||
#define TK_DATABASE 39
|
||||
#define TK_DESC 40
|
||||
#define TK_DETACH 41
|
||||
#define TK_EACH 42
|
||||
#define TK_FAIL 43
|
||||
#define TK_FOR 44
|
||||
#define TK_IGNORE 45
|
||||
#define TK_INITIALLY 46
|
||||
#define TK_INSTEAD 47
|
||||
#define TK_LIKE_KW 48
|
||||
#define TK_MATCH 49
|
||||
#define TK_KEY 50
|
||||
#define TK_OF 51
|
||||
#define TK_OFFSET 52
|
||||
#define TK_PRAGMA 53
|
||||
#define TK_RAISE 54
|
||||
#define TK_REPLACE 55
|
||||
#define TK_RESTRICT 56
|
||||
#define TK_ROW 57
|
||||
#define TK_TRIGGER 58
|
||||
#define TK_VACUUM 59
|
||||
#define TK_VIEW 60
|
||||
#define TK_VIRTUAL 61
|
||||
#define TK_REINDEX 62
|
||||
#define TK_RENAME 63
|
||||
#define TK_CTIME_KW 64
|
||||
#define TK_ANY 65
|
||||
#define TK_OR 66
|
||||
#define TK_AND 67
|
||||
#define TK_IS 68
|
||||
#define TK_BETWEEN 69
|
||||
#define TK_IN 70
|
||||
#define TK_ISNULL 71
|
||||
#define TK_NOTNULL 72
|
||||
#define TK_NE 73
|
||||
#define TK_EQ 74
|
||||
#define TK_GT 75
|
||||
#define TK_LE 76
|
||||
#define TK_LT 77
|
||||
#define TK_GE 78
|
||||
#define TK_ESCAPE 79
|
||||
#define TK_BITAND 80
|
||||
#define TK_BITOR 81
|
||||
#define TK_LSHIFT 82
|
||||
#define TK_RSHIFT 83
|
||||
#define TK_PLUS 84
|
||||
#define TK_MINUS 85
|
||||
#define TK_STAR 86
|
||||
#define TK_SLASH 87
|
||||
#define TK_REM 88
|
||||
#define TK_CONCAT 89
|
||||
#define TK_COLLATE 90
|
||||
#define TK_UMINUS 91
|
||||
#define TK_UPLUS 92
|
||||
#define TK_BITNOT 93
|
||||
#define TK_STRING 94
|
||||
#define TK_JOIN_KW 95
|
||||
#define TK_CONSTRAINT 96
|
||||
#define TK_DEFAULT 97
|
||||
#define TK_NULL 98
|
||||
#define TK_PRIMARY 99
|
||||
#define TK_UNIQUE 100
|
||||
#define TK_CHECK 101
|
||||
#define TK_REFERENCES 102
|
||||
#define TK_AUTOINCR 103
|
||||
#define TK_ON 104
|
||||
#define TK_DELETE 105
|
||||
#define TK_UPDATE 106
|
||||
#define TK_INSERT 107
|
||||
#define TK_SET 108
|
||||
#define TK_DEFERRABLE 109
|
||||
#define TK_FOREIGN 110
|
||||
#define TK_DROP 111
|
||||
#define TK_UNION 112
|
||||
#define TK_ALL 113
|
||||
#define TK_EXCEPT 114
|
||||
#define TK_INTERSECT 115
|
||||
#define TK_SELECT 116
|
||||
#define TK_DISTINCT 117
|
||||
#define TK_DOT 118
|
||||
#define TK_FROM 119
|
||||
#define TK_JOIN 120
|
||||
#define TK_USING 121
|
||||
#define TK_ORDER 122
|
||||
#define TK_GROUP 123
|
||||
|
|
4
pcache.c
4
pcache.c
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** 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"
|
||||
|
||||
|
@ -336,11 +336,9 @@ void sqlite3PcacheDrop(PgHdr *p){
|
|||
** make it so.
|
||||
*/
|
||||
void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
PCache *pCache;
|
||||
p->flags &= ~PGHDR_DONT_WRITE;
|
||||
assert( p->nRef>0 );
|
||||
if( 0==(p->flags & PGHDR_DIRTY) ){
|
||||
pCache = p->pCache;
|
||||
p->flags |= PGHDR_DIRTY;
|
||||
pcacheAddToDirtyList( p);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
** If the default page cache implementation is overriden, then neither of
|
||||
** 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"
|
||||
|
@ -129,7 +129,7 @@ static SQLITE_WSD struct PCacheGlobal {
|
|||
*/
|
||||
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
||||
PgFreeslot *p;
|
||||
sz &= ~7;
|
||||
sz = ROUNDDOWN8(sz);
|
||||
pcache1.szSlot = sz;
|
||||
pcache1.pStart = pBuf;
|
||||
pcache1.pFree = 0;
|
||||
|
@ -516,7 +516,7 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
|||
nPinned = pCache->nPage - pCache->nRecyclable;
|
||||
if( createFlag==1 && pCache->bPurgeable && (
|
||||
nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage)
|
||||
|| nPinned>=(pCache->nMax)
|
||||
|| nPinned>=(pCache->nMax * 9 / 10)
|
||||
)){
|
||||
goto fetch_out;
|
||||
}
|
||||
|
|
10
pragma.c
10
pragma.c
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** 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"
|
||||
|
||||
|
@ -172,6 +172,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
|
|||
{ "empty_result_callbacks", SQLITE_NullCallback },
|
||||
{ "legacy_file_format", SQLITE_LegacyFileFmt },
|
||||
{ "fullfsync", SQLITE_FullFSync },
|
||||
{ "reverse_unordered_selects", SQLITE_ReverseOrder },
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ "sql_trace", SQLITE_SqlTrace },
|
||||
{ "vdbe_listing", SQLITE_VdbeListing },
|
||||
|
@ -831,7 +832,6 @@ void sqlite3Pragma(
|
|||
sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC);
|
||||
sqlite3ViewGetColumnNames(pParse, pTab);
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
const Token *pDflt;
|
||||
if( IsHiddenColumn(pCol) ){
|
||||
nHidden++;
|
||||
continue;
|
||||
|
@ -842,9 +842,9 @@ void sqlite3Pragma(
|
|||
pCol->zType ? pCol->zType : "", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4);
|
||||
if( pCol->pDflt ){
|
||||
pDflt = &pCol->pDflt->span;
|
||||
assert( pDflt->z );
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pDflt->z, pDflt->n);
|
||||
const Token *p = &pCol->pDflt->span;
|
||||
assert( p->z );
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)p->z, p->n);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
|
||||
}
|
||||
|
|
63
prepare.c
63
prepare.c
|
@ -13,7 +13,7 @@
|
|||
** interface, and routines that contribute to loading the database schema
|
||||
** 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"
|
||||
|
||||
|
@ -77,21 +77,17 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
|||
*/
|
||||
char *zErr;
|
||||
int rc;
|
||||
u8 lookasideEnabled;
|
||||
assert( db->init.busy );
|
||||
db->init.iDb = iDb;
|
||||
db->init.newTnum = atoi(argv[1]);
|
||||
lookasideEnabled = db->lookaside.bEnabled;
|
||||
db->lookaside.bEnabled = 0;
|
||||
rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
|
||||
db->init.iDb = 0;
|
||||
db->lookaside.bEnabled = lookasideEnabled;
|
||||
assert( rc!=SQLITE_OK || zErr==0 );
|
||||
if( SQLITE_OK!=rc ){
|
||||
pData->rc = rc;
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
db->mallocFailed = 1;
|
||||
}else if( rc!=SQLITE_INTERRUPT ){
|
||||
}else if( rc!=SQLITE_INTERRUPT && (rc&0xff)!=SQLITE_LOCKED ){
|
||||
corruptSchema(pData, argv[0], 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)){
|
||||
/* 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
|
||||
** 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
|
||||
** even when its contents have been corrupted.
|
||||
*/
|
||||
|
@ -540,18 +536,40 @@ static int sqlite3Prepare(
|
|||
assert( !db->mallocFailed );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
|
||||
/* If any attached database schemas are locked, do not proceed with
|
||||
** compilation. Instead return SQLITE_LOCKED immediately.
|
||||
/* Check to verify that it is possible to get a read lock on all
|
||||
** 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++) {
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
assert( sqlite3BtreeHoldsMutex(pBt) );
|
||||
rc = sqlite3BtreeSchemaLocked(pBt);
|
||||
if( rc ){
|
||||
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);
|
||||
return sqlite3ApiExit(db, SQLITE_LOCKED);
|
||||
testcase( db->flags & SQLITE_ReadUncommitted );
|
||||
return sqlite3ApiExit(db, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -621,11 +639,13 @@ static int sqlite3Prepare(
|
|||
rc = SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
if( saveSqlFlag ){
|
||||
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail - zSql));
|
||||
assert( db->init.busy==0 || saveSqlFlag==0 );
|
||||
if( db->init.busy==0 ){
|
||||
Vdbe *pVdbe = sParse.pVdbe;
|
||||
sqlite3VdbeSetSql(pVdbe, zSql, (int)(sParse.zTail-zSql), saveSqlFlag);
|
||||
}
|
||||
if( rc!=SQLITE_OK || db->mallocFailed ){
|
||||
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
|
||||
if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
assert(!(*ppStmt));
|
||||
}else{
|
||||
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
|
||||
|
@ -664,8 +684,11 @@ static int sqlite3LockAndPrepare(
|
|||
|
||||
/*
|
||||
** 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 rc;
|
||||
|
@ -684,7 +707,7 @@ int sqlite3Reprepare(Vdbe *p){
|
|||
db->mallocFailed = 1;
|
||||
}
|
||||
assert( pNew==0 );
|
||||
return 0;
|
||||
return (rc==SQLITE_LOCKED) ? SQLITE_LOCKED : SQLITE_SCHEMA;
|
||||
}else{
|
||||
assert( pNew!=0 );
|
||||
}
|
||||
|
@ -692,7 +715,7 @@ int sqlite3Reprepare(Vdbe *p){
|
|||
sqlite3TransferBindings(pNew, (sqlite3_stmt*)p);
|
||||
sqlite3VdbeResetStepResult((Vdbe*)pNew);
|
||||
sqlite3VdbeFinalize((Vdbe*)pNew);
|
||||
return 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
|
21
resolve.c
21
resolve.c
|
@ -14,7 +14,7 @@
|
|||
** resolve all identifiers by associating them with a particular
|
||||
** 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 <stdlib.h>
|
||||
|
@ -63,8 +63,9 @@ static void resolveAlias(
|
|||
assert( pOrig!=0 );
|
||||
assert( pOrig->flags & EP_Resolved );
|
||||
db = pParse->db;
|
||||
pDup = sqlite3ExprDup(db, pOrig);
|
||||
pDup = sqlite3ExprDup(db, pOrig, 0);
|
||||
if( pDup==0 ) return;
|
||||
sqlite3TokenCopy(db, &pDup->token, &pOrig->token);
|
||||
if( pDup->op!=TK_COLUMN && zType[0]!='G' ){
|
||||
pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
|
||||
if( pDup==0 ) return;
|
||||
|
@ -129,6 +130,7 @@ static int lookupName(
|
|||
NameContext *pTopNC = pNC; /* First namecontext in the list */
|
||||
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 */
|
||||
|
||||
/* Dequote and zero-terminate the names */
|
||||
|
@ -282,8 +284,8 @@ static int lookupName(
|
|||
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
|
||||
Expr *pOrig;
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
|
||||
assert( pExpr->pList==0 );
|
||||
assert( pExpr->pSelect==0 );
|
||||
assert( pExpr->x.pList==0 );
|
||||
assert( pExpr->x.pSelect==0 );
|
||||
pOrig = pEList->a[j].pExpr;
|
||||
if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){
|
||||
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_FUNCTION: {
|
||||
ExprList *pList = pExpr->pList; /* The argument list */
|
||||
int n = pList ? pList->nExpr : 0; /* Number of arguments */
|
||||
ExprList *pList = pExpr->x.pList; /* The argument list */
|
||||
int n = pList ? pList->nExpr : 0; /* Number of arguments */
|
||||
int no_such_func = 0; /* True if no such function exists */
|
||||
int wrong_num_args = 0; /* True if wrong number of arguments */
|
||||
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 */
|
||||
u8 enc = ENC(pParse->db); /* The database encoding */
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
zId = (char*)pExpr->token.z;
|
||||
nId = pExpr->token.n;
|
||||
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
|
||||
|
@ -541,14 +544,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||
case TK_EXISTS:
|
||||
#endif
|
||||
case TK_IN: {
|
||||
if( pExpr->pSelect ){
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
int nRef = pNC->nRef;
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
if( pNC->isCheck ){
|
||||
sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
|
||||
}
|
||||
#endif
|
||||
sqlite3WalkSelect(pWalker, pExpr->pSelect);
|
||||
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
|
||||
assert( pNC->nRef>=nRef );
|
||||
if( nRef!=pNC->nRef ){
|
||||
ExprSetProperty(pExpr, EP_VarSelect);
|
||||
|
@ -736,7 +739,7 @@ static int resolveCompoundOrderBy(
|
|||
}else{
|
||||
iCol = resolveAsName(pParse, pEList, pE);
|
||||
if( iCol==0 ){
|
||||
pDup = sqlite3ExprDup(db, pE);
|
||||
pDup = sqlite3ExprDup(db, pE, 0);
|
||||
if( !db->mallocFailed ){
|
||||
assert(pDup);
|
||||
iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup);
|
||||
|
|
262
select.c
262
select.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** 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"
|
||||
|
||||
|
@ -800,8 +800,7 @@ static void generateSortTail(
|
|||
iTab = pOrderBy->iECursor;
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
pseudoTab = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nColumn);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output, nColumn);
|
||||
}
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
|
||||
codeOffset(v, p, addrContinue);
|
||||
|
@ -987,8 +986,9 @@ static const char *columnType(
|
|||
** statement.
|
||||
*/
|
||||
NameContext sNC;
|
||||
Select *pS = pExpr->pSelect;
|
||||
Select *pS = pExpr->x.pSelect;
|
||||
Expr *p = pS->pEList->a[0].pExpr;
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
sNC.pSrcList = pS->pSrc;
|
||||
sNC.pNext = pNC;
|
||||
sNC.pParse = pNC->pParse;
|
||||
|
@ -1229,7 +1229,7 @@ static int selectColumnsFromExprList(
|
|||
** The column list has only names, not types or collations. This
|
||||
** 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.
|
||||
*/
|
||||
static void selectAddColumnTypeAndCollation(
|
||||
|
@ -1284,7 +1284,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
|||
if( pTab==0 ){
|
||||
return 0;
|
||||
}
|
||||
pTab->db = db;
|
||||
pTab->dbMem = db->lookaside.bEnabled ? db : 0;
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = 0;
|
||||
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
|
@ -2141,7 +2141,7 @@ static int multiSelectOrderBy(
|
|||
/* Reattach the ORDER BY clause to the query.
|
||||
*/
|
||||
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
|
||||
** for the logic that removes duplicate result rows when the
|
||||
|
@ -2392,23 +2392,26 @@ static void substExpr(
|
|||
}else{
|
||||
Expr *pNew;
|
||||
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;
|
||||
assert( pNew!=0 );
|
||||
pExpr->op = pNew->op;
|
||||
assert( pExpr->pLeft==0 );
|
||||
pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft);
|
||||
pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft, 0);
|
||||
assert( pExpr->pRight==0 );
|
||||
pExpr->pRight = sqlite3ExprDup(db, pNew->pRight);
|
||||
assert( pExpr->pList==0 );
|
||||
pExpr->pList = sqlite3ExprListDup(db, pNew->pList);
|
||||
pExpr->pRight = sqlite3ExprDup(db, pNew->pRight, 0);
|
||||
pExpr->iTable = pNew->iTable;
|
||||
pExpr->pTab = pNew->pTab;
|
||||
pExpr->iColumn = pNew->iColumn;
|
||||
pExpr->iAgg = pNew->iAgg;
|
||||
sqlite3TokenCopy(db, &pExpr->token, &pNew->token);
|
||||
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->pAggInfo = pNew->pAggInfo;
|
||||
pNew->pAggInfo = 0;
|
||||
|
@ -2416,8 +2419,11 @@ static void substExpr(
|
|||
}else{
|
||||
substExpr(db, pExpr->pLeft, iTable, pEList);
|
||||
substExpr(db, pExpr->pRight, iTable, pEList);
|
||||
substSelect(db, pExpr->pSelect, iTable, pEList);
|
||||
substExprList(db, pExpr->pList, iTable, pEList);
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
substSelect(db, pExpr->x.pSelect, iTable, pEList);
|
||||
}else{
|
||||
substExprList(db, pExpr->x.pList, iTable, pEList);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void substExprList(
|
||||
|
@ -2729,7 +2735,7 @@ static int flattenSubquery(
|
|||
p->pSrc = 0;
|
||||
p->pPrior = 0;
|
||||
p->pLimit = 0;
|
||||
pNew = sqlite3SelectDup(db, p);
|
||||
pNew = sqlite3SelectDup(db, p, 0);
|
||||
p->pLimit = pLimit;
|
||||
p->pOrderBy = pOrderBy;
|
||||
p->pSrc = pSrc;
|
||||
|
@ -2873,7 +2879,7 @@ static int flattenSubquery(
|
|||
substExprList(db, pParent->pOrderBy, iParent, pSub->pEList);
|
||||
}
|
||||
if( pSub->pWhere ){
|
||||
pWhere = sqlite3ExprDup(db, pSub->pWhere);
|
||||
pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
|
||||
}else{
|
||||
pWhere = 0;
|
||||
}
|
||||
|
@ -2883,9 +2889,9 @@ static int flattenSubquery(
|
|||
pParent->pWhere = pWhere;
|
||||
substExpr(db, pParent->pHaving, iParent, pSub->pEList);
|
||||
pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
|
||||
sqlite3ExprDup(db, pSub->pHaving));
|
||||
sqlite3ExprDup(db, pSub->pHaving, 0));
|
||||
assert( pParent->pGroupBy==0 );
|
||||
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy);
|
||||
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
|
||||
}else{
|
||||
substExpr(db, pParent->pWhere, iParent, pSub->pEList);
|
||||
pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
|
||||
|
@ -2934,7 +2940,8 @@ static u8 minMaxQuery(Select *p){
|
|||
|
||||
if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
|
||||
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( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) 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;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
** 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);
|
||||
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
||||
if( pTab==0 ) return WRC_Abort;
|
||||
pTab->db = db;
|
||||
pTab->dbMem = db->lookaside.bEnabled ? db : 0;
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
|
||||
while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
||||
|
@ -3065,7 +3106,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
|||
** in the inner view.
|
||||
*/
|
||||
if( pFrom->pSelect==0 ){
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect);
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
|
||||
sqlite3WalkSelect(pWalker, pFrom->pSelect);
|
||||
}
|
||||
}
|
||||
|
@ -3364,12 +3405,13 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
|||
sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem);
|
||||
if( pFunc->iDistinct>=0 ){
|
||||
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 "
|
||||
"argument");
|
||||
pFunc->iDistinct = -1;
|
||||
}else{
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->pList);
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
}
|
||||
|
@ -3386,7 +3428,8 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
|
|||
int i;
|
||||
struct AggInfo_func *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,
|
||||
(void*)pF->pFunc, P4_FUNCDEF);
|
||||
}
|
||||
|
@ -3407,7 +3450,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
|||
int nArg;
|
||||
int addrNext = 0;
|
||||
int regAgg;
|
||||
ExprList *pList = pF->pExpr->pList;
|
||||
ExprList *pList = pF->pExpr->x.pList;
|
||||
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
|
||||
if( pList ){
|
||||
nArg = pList->nExpr;
|
||||
regAgg = sqlite3GetTempRange(pParse, nArg);
|
||||
|
@ -3657,7 +3701,7 @@ int sqlite3Select(
|
|||
** GROUP BY might use an index, DISTINCT never does.
|
||||
*/
|
||||
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;
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
isDistinct = 0;
|
||||
|
@ -3780,7 +3824,8 @@ int sqlite3Select(
|
|||
}
|
||||
sAggInfo.nAccumulator = sAggInfo.nColumn;
|
||||
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;
|
||||
|
||||
|
@ -3987,68 +4032,127 @@ int sqlite3Select(
|
|||
|
||||
} /* endif pGroupBy */
|
||||
else {
|
||||
ExprList *pMinMax = 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:
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
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;
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
/* 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
|
||||
** does, then we can assume that it consumes less space on disk and
|
||||
** will therefore be cheaper to scan to determine the query result.
|
||||
** In this case set iRoot to the root page number of the index b-tree
|
||||
** and pKeyInfo to the KeyInfo structure required to navigate the
|
||||
** index.
|
||||
**
|
||||
** In practice the KeyInfo structure will not be used. It is only
|
||||
** passed to keep OP_OpenRead happy.
|
||||
*/
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( !pBest || pIdx->nColumn<pBest->nColumn ){
|
||||
pBest = pIdx;
|
||||
}
|
||||
}
|
||||
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;
|
||||
if( pHaving ){
|
||||
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
|
||||
pDest, addrEnd, addrEnd);
|
||||
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, addrEnd);
|
||||
|
|
932
shell.c
932
shell.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains code to implement the "sqlite" command line
|
||||
** 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)
|
||||
/* This needs to come before any includes for MSVC compiler */
|
||||
|
@ -120,6 +120,861 @@ static void endTimer(void){
|
|||
*/
|
||||
#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
|
||||
** at an error if we are not interactive.
|
||||
|
@ -926,6 +1781,62 @@ static int run_schema_dump_query(
|
|||
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
|
||||
*/
|
||||
|
@ -937,6 +1848,14 @@ static char zHelp[] =
|
|||
".echo ON|OFF Turn command echo on or off\n"
|
||||
".exit Exit this program\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"
|
||||
".help Show this message\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
|
||||
|
||||
#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 ||
|
||||
strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
|
||||
p->showHeader = booleanValue(azArg[1]);
|
||||
|
|
193
sqliteInt.h
193
sqliteInt.h
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** 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_
|
||||
#define _SQLITEINT_H_
|
||||
|
@ -444,6 +444,17 @@ extern const int sqlite3one;
|
|||
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
|
||||
#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
|
||||
** callback for a given sqlite handle.
|
||||
|
@ -675,10 +686,17 @@ struct Schema {
|
|||
** lookaside malloc subsystem. Each available memory allocation in
|
||||
** the lookaside subsystem is stored on a linked list of LookasideSlot
|
||||
** 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 {
|
||||
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() */
|
||||
int nOut; /* Number of buffers currently checked out */
|
||||
int mxOut; /* Highwater mark for nOut */
|
||||
|
@ -807,7 +825,19 @@ struct sqlite3 {
|
|||
#endif
|
||||
Savepoint *pSavepoint; /* List of active 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 */
|
||||
|
||||
#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_Vtab 0x00100000 /* There exists a virtual table */
|
||||
#define SQLITE_CommitBusy 0x00200000 /* In the process of committing */
|
||||
#define SQLITE_ReverseOrder 0x00400000 /* Reverse unordered SELECTs */
|
||||
|
||||
/*
|
||||
** 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_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
|
||||
#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
|
||||
|
@ -1080,7 +1112,7 @@ struct CollSeq {
|
|||
** of a SELECT statement.
|
||||
*/
|
||||
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 */
|
||||
int iPKey; /* If not negative, use aCol[iPKey] as the primary key */
|
||||
int nCol; /* Number of columns in this table */
|
||||
|
@ -1091,7 +1123,6 @@ struct Table {
|
|||
u16 nRef; /* Number of pointers to this Table */
|
||||
u8 tabFlags; /* Mask of TF_* values */
|
||||
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 */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
|
@ -1106,6 +1137,7 @@ struct Table {
|
|||
int nModuleArg; /* Number of arguments to the module */
|
||||
char **azModuleArg; /* Text of all module args. [0] is module name */
|
||||
#endif
|
||||
Trigger *pTrigger; /* List of triggers stored in pSchema */
|
||||
Schema *pSchema; /* Schema that contains this table */
|
||||
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
|
||||
** of this structure.
|
||||
**
|
||||
** 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
|
||||
** code representing the ">=" operator. This same integer code is 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
|
||||
** code representing the ">=" operator. This same integer code is reused
|
||||
** to represent the greater-than-or-equal-to operator in the expression
|
||||
** tree.
|
||||
**
|
||||
** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list
|
||||
** of argument if the expression is a function.
|
||||
** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
|
||||
** 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
|
||||
** that have subexpressions, Expr.token can be the complete text that gave
|
||||
** rise to the Expr. In the latter case, the token is marked as being
|
||||
** a compound token.
|
||||
** Expr.pRight and Expr.pLeft are the left and right subexpressions of a
|
||||
** binary operator. Either or both may be NULL.
|
||||
**
|
||||
** 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.
|
||||
** 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
|
||||
** it can be accessed after all aggregates are computed.
|
||||
**
|
||||
** If the expression is a function, the Expr.iTable is an integer code
|
||||
** representing which function. If the expression is an unbound variable
|
||||
** marker (a question mark character '?' in the original SQL) then the
|
||||
** Expr.iTable holds the index number for that variable.
|
||||
** If the expression is an unbound variable marker (a question mark
|
||||
** character '?' in the original SQL) then the Expr.iTable holds the index
|
||||
** number for that variable.
|
||||
**
|
||||
** If the expression is a subquery then Expr.iColumn holds an integer
|
||||
** 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
|
||||
** 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
|
||||
** is a disk table or the "old.*" pseudo-table, then pTab points to the
|
||||
** 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 {
|
||||
u8 op; /* Operation performed by this node */
|
||||
char affinity; /* The affinity of the column or 0 if not a column */
|
||||
u16 flags; /* Various flags. See below */
|
||||
CollSeq *pColl; /* The collation type of the column or 0 */
|
||||
Expr *pLeft, *pRight; /* Left and right subnodes */
|
||||
ExprList *pList; /* A list of expressions used as function arguments
|
||||
** or in "<expr> IN (<expr-list)" */
|
||||
VVA_ONLY(u8 vvaFlags;) /* Flags used for VV&A only. EVVA_* below. */
|
||||
u16 flags; /* Various flags. EP_* See below */
|
||||
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 */
|
||||
|
||||
/* 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
|
||||
** iColumn-th field of the iTable-th table. */
|
||||
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
|
||||
int iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
|
||||
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. */
|
||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
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_FixedDest 0x0400 /* Result needed in a specific register */
|
||||
#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
|
||||
** Expr.flags field.
|
||||
|
@ -1452,6 +1537,24 @@ struct Expr {
|
|||
#define ExprSetProperty(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
|
||||
** 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 sqlite3ErrorClear(Parse*);
|
||||
void sqlite3Dequote(char*);
|
||||
void sqlite3DequoteExpr(sqlite3*, Expr*);
|
||||
void sqlite3DequoteExpr(Expr*);
|
||||
int sqlite3KeywordCode(const unsigned char*, int);
|
||||
int sqlite3RunParser(Parse*, const char*, char **);
|
||||
void sqlite3FinishCoding(Parse*);
|
||||
|
@ -2379,12 +2482,12 @@ void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
|
|||
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
|
||||
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
|
||||
void sqlite3BeginWriteOperation(Parse*, int, int);
|
||||
Expr *sqlite3ExprDup(sqlite3*,Expr*);
|
||||
void sqlite3TokenCopy(sqlite3*,Token*, Token*);
|
||||
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*);
|
||||
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*);
|
||||
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
|
||||
void sqlite3TokenCopy(sqlite3*,Token*,const Token*);
|
||||
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
|
||||
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
|
||||
IdList *sqlite3IdListDup(sqlite3*,IdList*);
|
||||
Select *sqlite3SelectDup(sqlite3*,Select*);
|
||||
Select *sqlite3SelectDup(sqlite3*,Select*,int);
|
||||
void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
|
||||
FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
|
||||
void sqlite3RegisterBuiltinFunctions(sqlite3*);
|
||||
|
@ -2411,9 +2514,10 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
|
|||
void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*);
|
||||
void sqlite3DropTrigger(Parse*, SrcList*, int);
|
||||
void sqlite3DropTriggerPtr(Parse*, Trigger*);
|
||||
int sqlite3TriggersExist(Table*, int, ExprList*);
|
||||
int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int,
|
||||
int, int, u32*, u32*);
|
||||
Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask);
|
||||
Trigger *sqlite3TriggerList(Parse *, Table *);
|
||||
int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
|
||||
int, int, int, int, u32*, u32*);
|
||||
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
|
||||
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
|
||||
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
|
||||
|
@ -2428,7 +2532,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
|
|||
# define sqlite3DeleteTrigger(A,B)
|
||||
# define sqlite3DropTriggerPtr(A,B)
|
||||
# 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
|
||||
|
||||
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
|
||||
|
@ -2686,6 +2790,17 @@ int sqlite3IsMemJournal(sqlite3_file *);
|
|||
u32 sqlite3Get4byte(const u8*);
|
||||
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
|
||||
#include "sseInt.h"
|
||||
#endif
|
||||
|
|
22
tokenize.c
22
tokenize.c
|
@ -15,7 +15,7 @@
|
|||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
**
|
||||
** $Id: tokenize.c,v 1.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 <stdlib.h>
|
||||
|
@ -381,14 +381,17 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
|||
** error message.
|
||||
*/
|
||||
int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
int nErr = 0;
|
||||
int i;
|
||||
void *pEngine;
|
||||
int tokenType;
|
||||
int lastTokenParsed = -1;
|
||||
sqlite3 *db = pParse->db;
|
||||
int mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
|
||||
int nErr = 0; /* Number of errors encountered */
|
||||
int i; /* Loop counter */
|
||||
void *pEngine; /* The LEMON-generated LALR(1) parser */
|
||||
int tokenType; /* type of the next token */
|
||||
int lastTokenParsed = -1; /* type of the previous token */
|
||||
u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */
|
||||
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 ){
|
||||
db->u1.isInterrupted = 0;
|
||||
}
|
||||
|
@ -408,6 +411,8 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
|||
assert( pParse->nVarExpr==0 );
|
||||
assert( pParse->nVarExprAlloc==0 );
|
||||
assert( pParse->apVarExpr==0 );
|
||||
enableLookaside = db->lookaside.bEnabled;
|
||||
if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
|
||||
while( !db->mallocFailed && zSql[i]!=0 ){
|
||||
assert( i>=0 );
|
||||
pParse->sLastToken.z = (u8*)&zSql[i];
|
||||
|
@ -462,6 +467,7 @@ abort_parse:
|
|||
);
|
||||
#endif /* YYDEBUG */
|
||||
sqlite3ParserFree(pEngine, sqlite3_free);
|
||||
db->lookaside.bEnabled = enableLookaside;
|
||||
if( db->mallocFailed ){
|
||||
pParse->rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
|
150
trigger.c
150
trigger.c
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
** up to the point of the BEGIN before the trigger actions. A Trigger
|
||||
|
@ -182,7 +206,7 @@ void sqlite3BeginTrigger(
|
|||
pTrigger->pTabSchema = pTab->pSchema;
|
||||
pTrigger->op = (u8)op;
|
||||
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);
|
||||
sqlite3TokenCopy(db, &pTrigger->nameToken,pName);
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
|
@ -209,14 +233,16 @@ void sqlite3FinishTrigger(
|
|||
TriggerStep *pStepList, /* The triggered program */
|
||||
Token *pAll /* Token that describes the complete CREATE TRIGGER */
|
||||
){
|
||||
Trigger *pTrig = 0; /* The trigger whose construction is finishing up */
|
||||
sqlite3 *db = pParse->db; /* The database */
|
||||
Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */
|
||||
char *zName; /* Name of trigger */
|
||||
sqlite3 *db = pParse->db; /* The database */
|
||||
DbFixer sFix;
|
||||
int iDb; /* Database containing the trigger */
|
||||
int iDb; /* Database containing the trigger */
|
||||
|
||||
pTrig = pParse->pNewTrigger;
|
||||
pParse->pNewTrigger = 0;
|
||||
if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
|
||||
zName = pTrig->name;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
|
||||
pTrig->step_list = pStepList;
|
||||
while( pStepList ){
|
||||
|
@ -242,32 +268,29 @@ void sqlite3FinishTrigger(
|
|||
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
|
||||
sqlite3NestedParse(pParse,
|
||||
"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);
|
||||
sqlite3DbFree(db, z);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
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 ){
|
||||
int n;
|
||||
Table *pTab;
|
||||
Trigger *pDel;
|
||||
pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash,
|
||||
pTrig->name, sqlite3Strlen30(pTrig->name), pTrig);
|
||||
if( pDel ){
|
||||
assert( pDel==pTrig );
|
||||
Trigger *pLink = pTrig;
|
||||
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
|
||||
pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig);
|
||||
if( pTrig ){
|
||||
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:
|
||||
|
@ -292,17 +315,17 @@ static void sqlitePersistTriggerStep(sqlite3 *db, TriggerStep *p){
|
|||
p->target.dyn = 1;
|
||||
}
|
||||
if( p->pSelect ){
|
||||
Select *pNew = sqlite3SelectDup(db, p->pSelect);
|
||||
Select *pNew = sqlite3SelectDup(db, p->pSelect, 1);
|
||||
sqlite3SelectDelete(db, p->pSelect);
|
||||
p->pSelect = pNew;
|
||||
}
|
||||
if( p->pWhere ){
|
||||
Expr *pNew = sqlite3ExprDup(db, p->pWhere);
|
||||
Expr *pNew = sqlite3ExprDup(db, p->pWhere, EXPRDUP_REDUCE);
|
||||
sqlite3ExprDelete(db, p->pWhere);
|
||||
p->pWhere = pNew;
|
||||
}
|
||||
if( p->pExprList ){
|
||||
ExprList *pNew = sqlite3ExprListDup(db, p->pExprList);
|
||||
ExprList *pNew = sqlite3ExprListDup(db, p->pExprList, 1);
|
||||
sqlite3ExprListDelete(db, p->pExprList);
|
||||
p->pExprList = pNew;
|
||||
}
|
||||
|
@ -546,6 +569,9 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
|||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, 0, 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.
|
||||
*/
|
||||
void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
|
||||
Hash *pHash = &(db->aDb[iDb].pSchema->trigHash);
|
||||
Trigger *pTrigger;
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash),
|
||||
zName, nName, 0);
|
||||
pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0);
|
||||
if( pTrigger ){
|
||||
Table *pTable = tableOfTrigger(pTrigger);
|
||||
assert( pTable!=0 );
|
||||
if( pTable->pTrigger == pTrigger ){
|
||||
pTable->pTrigger = pTrigger->pNext;
|
||||
}else{
|
||||
Trigger *cc = pTable->pTrigger;
|
||||
while( cc ){
|
||||
if( cc->pNext == pTrigger ){
|
||||
cc->pNext = cc->pNext->pNext;
|
||||
break;
|
||||
}
|
||||
cc = cc->pNext;
|
||||
}
|
||||
assert(cc);
|
||||
if( pTrigger->pSchema==pTrigger->pTabSchema ){
|
||||
Table *pTab = tableOfTrigger(pTrigger);
|
||||
Trigger **pp;
|
||||
for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
|
||||
*pp = (*pp)->pNext;
|
||||
}
|
||||
sqlite3DeleteTrigger(db, pTrigger);
|
||||
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
|
||||
** "op" on table pTab. If pChanges is not NULL then it is a list of columns
|
||||
** that are being updated. Triggers only match if the ON clause of the
|
||||
** trigger definition overlaps the set of columns being updated.
|
||||
**
|
||||
** The returned bit vector is some combination of TRIGGER_BEFORE and
|
||||
** TRIGGER_AFTER.
|
||||
** Return a list of all triggers on table pTab if there exists at least
|
||||
** one trigger that must be fired when an operation of type 'op' is
|
||||
** performed on the table, and, if that operation is an UPDATE, if at
|
||||
** least one of the columns in pChanges is being modified.
|
||||
*/
|
||||
int sqlite3TriggersExist(
|
||||
Trigger *sqlite3TriggersExist(
|
||||
Parse *pParse, /* Parse context */
|
||||
Table *pTab, /* The table the contains the triggers */
|
||||
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;
|
||||
|
||||
pTrigger = IsVirtual(pTab) ? 0 : pTab->pTrigger;
|
||||
while( pTrigger ){
|
||||
if( pTrigger->op==op && checkColumnOverLap(pTrigger->pColumns, pChanges) ){
|
||||
mask |= pTrigger->tr_tm;
|
||||
Trigger *pList = sqlite3TriggerList(pParse, pTab);
|
||||
Trigger *p;
|
||||
assert( pList==0 || IsVirtual(pTab)==0 );
|
||||
for(p=pList; p; p=p->pNext){
|
||||
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;
|
||||
switch( pTriggerStep->op ){
|
||||
case TK_SELECT: {
|
||||
Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect);
|
||||
Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0);
|
||||
if( ss ){
|
||||
SelectDest dest;
|
||||
|
||||
|
@ -692,8 +709,8 @@ static int codeTriggerProgram(
|
|||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
|
||||
sqlite3Update(pParse, pSrc,
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList),
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere), orconf);
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -702,8 +719,8 @@ static int codeTriggerProgram(
|
|||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
|
||||
sqlite3Insert(pParse, pSrc,
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList),
|
||||
sqlite3SelectDup(db, pTriggerStep->pSelect),
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
|
||||
sqlite3SelectDup(db, pTriggerStep->pSelect, 0),
|
||||
sqlite3IdListDup(db, pTriggerStep->pIdList), orconf);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
|
||||
break;
|
||||
|
@ -713,7 +730,7 @@ static int codeTriggerProgram(
|
|||
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqlite3DeleteFrom(pParse, pSrc,
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere));
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere, 0));
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -757,6 +774,7 @@ static int codeTriggerProgram(
|
|||
*/
|
||||
int sqlite3CodeRowTrigger(
|
||||
Parse *pParse, /* Parse context */
|
||||
Trigger *pTrigger, /* List of triggers on table pTab */
|
||||
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
|
||||
ExprList *pChanges, /* Changes list for any UPDATE OF triggers */
|
||||
int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
|
||||
|
@ -780,7 +798,7 @@ int sqlite3CodeRowTrigger(
|
|||
|
||||
assert(newIdx != -1 || oldIdx != -1);
|
||||
|
||||
for(p=pTab->pTrigger; p; p=p->pNext){
|
||||
for(p=pTrigger; p; p=p->pNext){
|
||||
int fire_this = 0;
|
||||
|
||||
/* Determine whether we should code this trigger */
|
||||
|
@ -830,7 +848,7 @@ int sqlite3CodeRowTrigger(
|
|||
|
||||
/* code the WHEN clause */
|
||||
endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
|
||||
whenExpr = sqlite3ExprDup(db, p->pWhen);
|
||||
whenExpr = sqlite3ExprDup(db, p->pWhen, 0);
|
||||
if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){
|
||||
pParse->trigStack = trigStackEntry.pNext;
|
||||
sqlite3ExprDelete(db, whenExpr);
|
||||
|
|
42
update.c
42
update.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.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"
|
||||
|
||||
|
@ -107,7 +107,7 @@ void sqlite3Update(
|
|||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
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
|
||||
int iBeginAfterTrigger = 0; /* Address of after trigger program */
|
||||
int iEndAfterTrigger = 0; /* Exit of after trigger program */
|
||||
|
@ -143,10 +143,10 @@ void sqlite3Update(
|
|||
** updated is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_UPDATE, pChanges);
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
|
@ -154,7 +154,7 @@ void sqlite3Update(
|
|||
# define isView 0
|
||||
#endif
|
||||
|
||||
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
|
||||
if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
||||
|
@ -167,7 +167,7 @@ void sqlite3Update(
|
|||
/* If there are FOR EACH ROW triggers, allocate cursors for the
|
||||
** special OLD and NEW tables
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
newIdx = pParse->nTab++;
|
||||
oldIdx = pParse->nTab++;
|
||||
}
|
||||
|
@ -299,27 +299,27 @@ void sqlite3Update(
|
|||
|
||||
/* Generate the code for triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
int iGoto;
|
||||
|
||||
/* Create pseudo-tables for NEW and OLD
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, oldIdx, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
|
||||
|
||||
iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
addr = sqlite3VdbeMakeLabel(v);
|
||||
iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
|
||||
newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr,
|
||||
&old_col_mask, &new_col_mask) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab,
|
||||
newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr,
|
||||
&old_col_mask, &new_col_mask) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
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. */
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeResolveLabel(v, addr);
|
||||
}
|
||||
|
||||
|
@ -412,7 +412,7 @@ void sqlite3Update(
|
|||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
|
||||
}
|
||||
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
int regRowid;
|
||||
int regRow;
|
||||
int regCols;
|
||||
|
@ -541,7 +541,7 @@ void sqlite3Update(
|
|||
/* If there are triggers, close all the cursors after each iteration
|
||||
** through the loop. The fire the after triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
|
||||
sqlite3VdbeJumpHere(v, iEndAfterTrigger);
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ void sqlite3Update(
|
|||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0);
|
||||
}
|
||||
|
@ -633,12 +633,12 @@ static void updateVirtualTable(
|
|||
sqlite3CreateIdExpr(pParse, "_rowid_"), 0);
|
||||
if( pRowid ){
|
||||
pEList = sqlite3ExprListAppend(pParse, pEList,
|
||||
sqlite3ExprDup(db, pRowid), 0);
|
||||
sqlite3ExprDup(db, pRowid, 0), 0);
|
||||
}
|
||||
assert( pTab->iPKey<0 );
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]>=0 ){
|
||||
pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr);
|
||||
pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
|
||||
}else{
|
||||
pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName);
|
||||
}
|
||||
|
|
4
utf.c
4
utf.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains routines used to translate between UTF-8,
|
||||
** 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:
|
||||
**
|
||||
|
@ -417,7 +417,7 @@ int sqlite3Utf8To8(unsigned char *zIn){
|
|||
}
|
||||
}
|
||||
*zOut = 0;
|
||||
return zOut - zStart;
|
||||
return (int)(zOut - zStart);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
10
util.c
10
util.c
|
@ -14,7 +14,7 @@
|
|||
** This file contains functions for allocating memory, comparing
|
||||
** 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 <stdarg.h>
|
||||
|
@ -694,7 +694,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
|
|||
/* a: p2<<28 | p4<<14 | p6 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
a &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
a &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x7f<<14)|(0x7f);
|
||||
b = b<<7;
|
||||
a |= b;
|
||||
|
@ -711,7 +711,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
|
|||
/* b: p3<<28 | p5<<14 | p7 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
b &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
/* moved CSE2 up */
|
||||
/* a &= (0x7f<<14)|(0x7f); */
|
||||
a = a<<7;
|
||||
|
@ -806,8 +806,8 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
|||
/* a: p0<<28 | p2<<14 | p4 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
a &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
a &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b = b<<7;
|
||||
*v = a | b;
|
||||
return 5;
|
||||
|
|
294
vdbe.c
294
vdbe.c
|
@ -43,7 +43,7 @@
|
|||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.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 "vdbeInt.h"
|
||||
|
@ -187,7 +187,7 @@ int sqlite3VdbeOpcodeHasProperty(int opcode, int mask){
|
|||
static VdbeCursor *allocateCursor(
|
||||
Vdbe *p, /* The virtual machine */
|
||||
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 isBtreeCursor /* */
|
||||
){
|
||||
|
@ -213,15 +213,6 @@ static VdbeCursor *allocateCursor(
|
|||
|
||||
int nByte;
|
||||
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 =
|
||||
sizeof(VdbeCursor) +
|
||||
(isBtreeCursor?sqlite3BtreeCursorSize():0) +
|
||||
|
@ -563,7 +554,7 @@ int sqlite3VdbeExec(
|
|||
Mem *pOut = 0; /* Output operand */
|
||||
u8 opProperty;
|
||||
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
|
||||
u64 start; /* CPU clock count at start of opcode */
|
||||
int origPc; /* Program counter at start of opcode */
|
||||
|
@ -819,6 +810,16 @@ case OP_Yield: { /* in1 */
|
|||
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 *
|
||||
**
|
||||
|
@ -967,26 +968,34 @@ case OP_Blob: { /* out2-prerelease */
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Variable P1 P2 * * *
|
||||
/* Opcode: Variable P1 P2 P3 P4 *
|
||||
**
|
||||
** The value of variable P1 is written into register P2. A variable is
|
||||
** an unknown in the original SQL string as handed to sqlite3_compile().
|
||||
** Any occurrence of the '?' character in the original SQL is considered
|
||||
** a variable. Variables in the SQL string are number from left to
|
||||
** right beginning with 1. The values of variables are set using the
|
||||
** sqlite3_bind() API.
|
||||
** Transfer the values of bound parameters P1..P1+P3-1 into registers
|
||||
** P2..P2+P3-1.
|
||||
**
|
||||
** If the parameter is named, then its name appears in P4 and P3==1.
|
||||
** The P4 value is used by sqlite3_bind_parameter_name().
|
||||
*/
|
||||
case OP_Variable: { /* out2-prerelease */
|
||||
case OP_Variable: {
|
||||
int j = pOp->p1 - 1;
|
||||
int k = pOp->p2;
|
||||
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];
|
||||
if( sqlite3VdbeMemTooBig(pVar) ){
|
||||
goto too_big;
|
||||
while( n-- > 0 ){
|
||||
pVar = &p->aVar[j++];
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1002,15 +1011,14 @@ case OP_Move: {
|
|||
int n = pOp->p3;
|
||||
int p1 = pOp->p1;
|
||||
int p2 = pOp->p2;
|
||||
assert( n>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( n>0 && p1>0 && p2>0 );
|
||||
assert( p1+n<=p2 || p2+n<=p1 );
|
||||
|
||||
pIn1 = &p->aMem[p1];
|
||||
pOut = &p->aMem[p2];
|
||||
while( n-- ){
|
||||
assert( pOut<=&p->aMem[p->nMem] );
|
||||
assert( pIn1<=&p->aMem[p->nMem] );
|
||||
zMalloc = pOut->zMalloc;
|
||||
pOut->zMalloc = 0;
|
||||
sqlite3VdbeMemMove(pOut, pIn1);
|
||||
|
@ -1076,7 +1084,24 @@ case OP_ResultRow: {
|
|||
int i;
|
||||
assert( p->nResColumn==pOp->p2 );
|
||||
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 */
|
||||
p->cacheCtr = (p->cacheCtr + 2)|1;
|
||||
|
@ -1095,7 +1120,6 @@ case OP_ResultRow: {
|
|||
|
||||
/* Return SQLITE_ROW
|
||||
*/
|
||||
p->nCallback++;
|
||||
p->pc = pc + 1;
|
||||
rc = SQLITE_ROW;
|
||||
goto vdbe_return;
|
||||
|
@ -1300,7 +1324,7 @@ case OP_Function: {
|
|||
apVal = p->apArg;
|
||||
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 );
|
||||
pArg = &p->aMem[pOp->p2];
|
||||
for(i=0; i<n; i++, pArg++){
|
||||
|
@ -1701,7 +1725,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
|||
|
||||
/* 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.
|
||||
**
|
||||
** The permutation is only valid until the next OP_Permutation, OP_Compare,
|
||||
|
@ -1736,9 +1760,9 @@ case OP_Compare: {
|
|||
assert( n>0 );
|
||||
assert( pKeyInfo!=0 );
|
||||
p1 = pOp->p1;
|
||||
assert( p1>0 && p1+n-1<p->nMem );
|
||||
assert( p1>0 && p1+n<=p->nMem+1 );
|
||||
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++){
|
||||
int idx = aPermute ? aPermute[i] : i;
|
||||
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
|
||||
** opens the cursor.
|
||||
*/
|
||||
#if 0
|
||||
case OP_SetNumColumns: {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Opcode: Column P1 P2 P3 P4 *
|
||||
**
|
||||
|
@ -2253,7 +2279,7 @@ case OP_MakeRecord: {
|
|||
|
||||
nField = pOp->p1;
|
||||
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];
|
||||
nField = pOp->p2;
|
||||
pLast = &pData0[nField-1];
|
||||
|
@ -2330,6 +2356,22 @@ case OP_MakeRecord: {
|
|||
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 * * * *
|
||||
**
|
||||
** Begin an individual statement transaction which is part of a larger
|
||||
|
@ -2361,10 +2403,12 @@ case OP_Statement: {
|
|||
pBt = db->aDb[i].pBt;
|
||||
assert( sqlite3BtreeIsInTrans(pBt) );
|
||||
assert( (p->btreeMask & (1<<i))!=0 );
|
||||
if( !sqlite3BtreeIsInStmt(pBt) ){
|
||||
rc = sqlite3BtreeBeginStmt(pBt);
|
||||
p->openedStatement = 1;
|
||||
if( p->iStatement==0 ){
|
||||
assert( db->nStatement>=0 && db->nSavepoint>=0 );
|
||||
db->nStatement++;
|
||||
p->iStatement = db->nSavepoint + db->nStatement;
|
||||
}
|
||||
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2471,7 +2515,7 @@ case OP_Savepoint: {
|
|||
rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
|
||||
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
|
||||
** 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.
|
||||
*/
|
||||
|
@ -2550,6 +2595,7 @@ case OP_AutoCommit: {
|
|||
goto vdbe_return;
|
||||
}
|
||||
}
|
||||
assert( db->nStatement==0 );
|
||||
sqlite3CloseSavepoints(db);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
rc = SQLITE_DONE;
|
||||
|
@ -2767,9 +2813,11 @@ case OP_VerifyCookie: {
|
|||
** to get a read lock but fails, the script terminates with an
|
||||
** SQLITE_BUSY error code.
|
||||
**
|
||||
** The P4 value is a pointer to a KeyInfo structure that defines the
|
||||
** content and collating sequence of indices. P4 is NULL for cursors
|
||||
** that are not pointing to indices.
|
||||
** The P4 value may be either an integer (P4_INT32) or a pointer to
|
||||
** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
|
||||
** 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.
|
||||
*/
|
||||
|
@ -2779,9 +2827,11 @@ case OP_VerifyCookie: {
|
|||
** page is P2. Or if P5!=0 use the content of register P2 to find the
|
||||
** root page.
|
||||
**
|
||||
** The P4 value is a pointer to a KeyInfo structure that defines the
|
||||
** content and collating sequence of indices. P4 is NULL for cursors
|
||||
** that are not pointing to indices.
|
||||
** The P4 value may be either an integer (P4_INT32) or a pointer to
|
||||
** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
|
||||
** 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
|
||||
** 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_OpenWrite: {
|
||||
int nField = 0;
|
||||
KeyInfo *pKeyInfo = 0;
|
||||
int i = pOp->p1;
|
||||
int p2 = pOp->p2;
|
||||
int iDb = pOp->p3;
|
||||
|
@ -2824,16 +2876,19 @@ case OP_OpenWrite: {
|
|||
}
|
||||
}
|
||||
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;
|
||||
pCur->nullRow = 1;
|
||||
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, pCur->pCursor);
|
||||
if( pOp->p4type==P4_KEYINFO ){
|
||||
pCur->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
pCur->pKeyInfo->enc = ENC(p->db);
|
||||
}else{
|
||||
pCur->pKeyInfo = 0;
|
||||
}
|
||||
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
|
||||
pCur->pKeyInfo = pKeyInfo;
|
||||
|
||||
switch( rc ){
|
||||
case SQLITE_BUSY: {
|
||||
p->pc = pc;
|
||||
|
@ -2908,7 +2963,7 @@ case OP_OpenEphemeral: {
|
|||
SQLITE_OPEN_TRANSIENT_DB;
|
||||
|
||||
assert( i>=0 );
|
||||
pCx = allocateCursor(p, i, pOp, -1, 1);
|
||||
pCx = allocateCursor(p, i, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags,
|
||||
|
@ -2943,7 +2998,7 @@ case OP_OpenEphemeral: {
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: OpenPseudo P1 P2 * * *
|
||||
/* Opcode: OpenPseudo P1 P2 P3 * *
|
||||
**
|
||||
** 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
|
||||
|
@ -2962,12 +3017,15 @@ case OP_OpenEphemeral: {
|
|||
** is stored. In this case, the vdbe program must ensure that the
|
||||
** memory cell containing the row data is not overwritten until the
|
||||
** 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: {
|
||||
int i = pOp->p1;
|
||||
VdbeCursor *pCx;
|
||||
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;
|
||||
pCx->nullRow = 1;
|
||||
pCx->pseudoTable = 1;
|
||||
|
@ -3512,9 +3570,8 @@ case OP_NewRowid: { /* out2-prerelease */
|
|||
#endif
|
||||
|
||||
if( !pC->useRandomRowid ){
|
||||
if( pC->nextRowidValid ){
|
||||
v = pC->nextRowid;
|
||||
}else{
|
||||
v = sqlite3BtreeGetCachedRowid(pC->pCursor);
|
||||
if( v==0 ){
|
||||
rc = sqlite3BtreeLast(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
|
@ -3551,12 +3608,7 @@ case OP_NewRowid: { /* out2-prerelease */
|
|||
}
|
||||
#endif
|
||||
|
||||
if( v<MAX_ROWID ){
|
||||
pC->nextRowidValid = 1;
|
||||
pC->nextRowid = v+1;
|
||||
}else{
|
||||
pC->nextRowidValid = 0;
|
||||
}
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0);
|
||||
}
|
||||
if( pC->useRandomRowid ){
|
||||
assert( pOp->p3==0 ); /* SQLITE_FULL must have occurred prior to this */
|
||||
|
@ -3634,9 +3686,6 @@ case OP_Insert: {
|
|||
iKey = intToKey(pKey->u.i);
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
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 ){
|
||||
pData->z = 0;
|
||||
pData->n = 0;
|
||||
|
@ -3671,6 +3720,7 @@ case OP_Insert: {
|
|||
}else{
|
||||
nZero = 0;
|
||||
}
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
|
||||
rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
|
||||
pData->z, pData->n, nZero,
|
||||
pOp->p5 & OPFLAG_APPEND);
|
||||
|
@ -3733,8 +3783,8 @@ case OP_Delete: {
|
|||
|
||||
rc = sqlite3VdbeCursorMoveto(pC);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
|
||||
rc = sqlite3BtreeDelete(pC->pCursor);
|
||||
pC->nextRowidValid = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
|
||||
/* Invoke the update-hook if required. */
|
||||
|
@ -4066,7 +4116,7 @@ case OP_IdxDelete: {
|
|||
VdbeCursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
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( p->apCsr[i]!=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
|
||||
** 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
|
||||
** 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
|
||||
|
@ -4321,33 +4371,58 @@ case OP_CreateTable: { /* out2-prerelease */
|
|||
** then runs the new virtual machine. It is thus a re-entrant opcode.
|
||||
*/
|
||||
case OP_ParseSchema: {
|
||||
char *zSql;
|
||||
int iDb = pOp->p1;
|
||||
const char *zMaster;
|
||||
InitData initData;
|
||||
|
||||
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);
|
||||
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);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
goto no_mem;
|
||||
}
|
||||
|
@ -4781,7 +4856,7 @@ case OP_TableLock: {
|
|||
assert( (p->btreeMask & (1<<p1))!=0 );
|
||||
assert( isWriteLock==0 || isWriteLock==1 );
|
||||
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
|
||||
if( rc==SQLITE_LOCKED ){
|
||||
if( (rc&0xFF)==SQLITE_LOCKED ){
|
||||
const char *z = pOp->p4.z;
|
||||
sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z);
|
||||
}
|
||||
|
@ -4796,8 +4871,8 @@ case OP_TableLock: {
|
|||
** xBegin method for that table.
|
||||
**
|
||||
** 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
|
||||
** error code to SQLITE_LOCKED.
|
||||
** within a callback to a virtual table xSync() method. If it is, the error
|
||||
** code will be set to SQLITE_LOCKED.
|
||||
*/
|
||||
case OP_VBegin: {
|
||||
sqlite3_vtab *pVtab = pOp->p4.pVtab;
|
||||
|
@ -4863,7 +4938,7 @@ case OP_VOpen: {
|
|||
pVtabCursor->pVtab = pVtab;
|
||||
|
||||
/* Initialise vdbe cursor object */
|
||||
pCur = allocateCursor(p, pOp->p1, &pOp[-1], -1, 0);
|
||||
pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
|
||||
if( pCur ){
|
||||
pCur->pVtabCursor = pVtabCursor;
|
||||
pCur->pModule = pVtabCursor->pVtab->pModule;
|
||||
|
@ -5023,7 +5098,7 @@ case OP_VColumn: {
|
|||
pVtab->zErrMsg = 0;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
case OP_Trace: {
|
||||
if( pOp->p4.z ){
|
||||
char *zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
|
||||
if( zTrace ){
|
||||
if( db->xTrace ){
|
||||
db->xTrace(db->pTraceArg, pOp->p4.z);
|
||||
db->xTrace(db->pTraceArg, zTrace);
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( (db->flags & SQLITE_SqlTrace)!=0 ){
|
||||
sqlite3DebugPrintf("SQL-trace: %s\n", pOp->p4.z);
|
||||
sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
}
|
||||
|
|
4
vdbe.h
4
vdbe.h
|
@ -15,7 +15,7 @@
|
|||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** 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_
|
||||
#define _SQLITE_VDBE_H_
|
||||
|
@ -181,7 +181,7 @@ void sqlite3VdbeSetNumCols(Vdbe*,int);
|
|||
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
|
||||
void sqlite3VdbeCountChanges(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*);
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
|
|
13
vdbeInt.h
13
vdbeInt.h
|
@ -15,7 +15,7 @@
|
|||
** 6000 lines long) it was split up into several smaller files and
|
||||
** 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_
|
||||
#define _VDBEINT_H_
|
||||
|
@ -59,13 +59,11 @@ struct VdbeCursor {
|
|||
BtCursor *pCursor; /* The cursor structure of the backend */
|
||||
int iDb; /* Index of cursor database in db->aDb[] (or -1) */
|
||||
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 rowidIsValid; /* True if lastRowid is valid */
|
||||
Bool atFirst; /* True if pointing to first entry */
|
||||
Bool useRandomRowid; /* Generate new record numbers semi-randomly */
|
||||
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 ephemPseudoTable;
|
||||
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
|
||||
|
@ -279,16 +277,13 @@ struct Vdbe {
|
|||
u32 magic; /* Magic number for sanity checking */
|
||||
int nMem; /* Number of memory locations currently allocated */
|
||||
Mem *aMem; /* The memory locations */
|
||||
int nCallback; /* Number of callbacks invoked so far */
|
||||
int cacheCtr; /* VdbeCursor row cache generation counter */
|
||||
int contextStackTop; /* Index of top element in the context stack */
|
||||
int contextStackDepth; /* The size of the "context" stack */
|
||||
Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/
|
||||
int pc; /* The program counter */
|
||||
int rc; /* Value to return */
|
||||
unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */
|
||||
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 */
|
||||
char **azResColumn; /* Values for one row of result */
|
||||
char *zErrMsg; /* Error message written here */
|
||||
|
@ -300,17 +295,18 @@ struct Vdbe {
|
|||
u8 inVtabMethod; /* See comments above */
|
||||
u8 usesStmtJournal; /* True if uses a statement journal */
|
||||
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 */
|
||||
i64 startTime; /* Time when query started - used for profiling */
|
||||
int btreeMask; /* Bitmask of db->aDb[] entries referenced */
|
||||
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
|
||||
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 */
|
||||
void *pFree; /* Free this when deleting the vdbe */
|
||||
#ifdef SQLITE_DEBUG
|
||||
FILE *trace; /* Write an execution trace here, if not NULL */
|
||||
#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
|
||||
int fetchId; /* Statement number used by sqlite3_fetch_statement */
|
||||
int lru; /* Counter used for LRU cache replacement */
|
||||
|
@ -378,6 +374,7 @@ int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
|||
const char *sqlite3OpcodeName(int);
|
||||
int sqlite3VdbeOpcodeHasProperty(int, int);
|
||||
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
|
||||
int sqlite3VdbeCloseStatement(Vdbe *, int);
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
int sqlite3VdbeReleaseBuffers(Vdbe *p);
|
||||
#endif
|
||||
|
|
54
vdbeapi.c
54
vdbeapi.c
|
@ -13,7 +13,7 @@
|
|||
** This file contains code use to implement APIs that are part of the
|
||||
** 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 "vdbeInt.h"
|
||||
|
@ -204,12 +204,14 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
|
|||
rc = SQLITE_OK;
|
||||
}else{
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
sqlite3 *db = v->db;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = v->db->mutex;
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
stmtLruRemove(v);
|
||||
rc = sqlite3VdbeFinalize(v);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(mutex);
|
||||
}
|
||||
return rc;
|
||||
|
@ -234,6 +236,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
|
|||
stmtLruAdd(v);
|
||||
sqlite3VdbeMakeReady(v, -1, 0, 0, 0);
|
||||
assert( (rc & (v->db->errMask))==rc );
|
||||
rc = sqlite3ApiExit(v->db, rc);
|
||||
sqlite3_mutex_leave(v->db->mutex);
|
||||
}
|
||||
return rc;
|
||||
|
@ -488,34 +491,41 @@ static int sqlite3Step(Vdbe *p){
|
|||
#ifndef SQLITE_OMIT_TRACE
|
||||
/* Invoke the profile callback if there is one
|
||||
*/
|
||||
if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->nOp>0
|
||||
&& p->aOp[0].opcode==OP_Trace && p->aOp[0].p4.z!=0 ){
|
||||
if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){
|
||||
double rNow;
|
||||
u64 elapseTime;
|
||||
|
||||
sqlite3OsCurrentTime(db->pVfs, &rNow);
|
||||
elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0);
|
||||
elapseTime -= p->startTime;
|
||||
db->xProfile(db->pProfileArg, p->aOp[0].p4.z, elapseTime);
|
||||
db->xProfile(db->pProfileArg, p->zSql, elapseTime);
|
||||
}
|
||||
#endif
|
||||
|
||||
db->errCode = rc;
|
||||
/*sqlite3Error(p->db, rc, 0);*/
|
||||
p->rc = sqlite3ApiExit(p->db, p->rc);
|
||||
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;
|
||||
if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
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);
|
||||
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
|
||||
&& cnt++ < 5
|
||||
&& vdbeReprepare(v) ){
|
||||
&& (rc = vdbeReprepare(v))==SQLITE_OK ){
|
||||
sqlite3_reset(pStmt);
|
||||
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.
|
||||
** The error message from the SQL compiler has already been loaded
|
||||
** into the database handle. This block copies the error message
|
||||
|
@ -711,7 +721,7 @@ failed:
|
|||
** context.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
@ -754,7 +764,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
|
|||
}else{
|
||||
/* ((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 };
|
||||
if( pVm->db ){
|
||||
if( pVm && pVm->db ){
|
||||
sqlite3_mutex_enter(pVm->db->mutex);
|
||||
sqlite3Error(pVm->db, SQLITE_RANGE, 0);
|
||||
}
|
||||
|
|
241
vdbeaux.c
241
vdbeaux.c
|
@ -14,7 +14,7 @@
|
|||
** 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.
|
||||
**
|
||||
** $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 "vdbeInt.h"
|
||||
|
@ -52,17 +52,22 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
|
|||
/*
|
||||
** 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;
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
if( !isPrepareV2 ) return;
|
||||
#endif
|
||||
assert( p->zSql==0 );
|
||||
p->zSql = sqlite3DbStrNDup(p->db, z, n);
|
||||
p->isPrepareV2 = isPrepareV2 ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the SQL associated with a prepared statement
|
||||
*/
|
||||
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){
|
||||
Vdbe tmp, *pTmp;
|
||||
char *zTmp;
|
||||
int nTmp;
|
||||
tmp = *pA;
|
||||
*pA = *pB;
|
||||
*pB = tmp;
|
||||
|
@ -84,9 +88,7 @@ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
|
|||
zTmp = pA->zSql;
|
||||
pA->zSql = pB->zSql;
|
||||
pB->zSql = zTmp;
|
||||
nTmp = pA->nSql;
|
||||
pA->nSql = pB->nSql;
|
||||
pB->nSql = nTmp;
|
||||
pB->isPrepareV2 = pA->isPrepareV2;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
|
@ -112,7 +114,7 @@ static int growOpArray(Vdbe *p){
|
|||
int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
|
||||
pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
|
||||
if( pNew ){
|
||||
p->nOpAlloc = nNew;
|
||||
p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
|
||||
p->aOp = pNew;
|
||||
}
|
||||
return (pNew ? SQLITE_OK : SQLITE_NOMEM);
|
||||
|
@ -901,8 +903,8 @@ int sqlite3VdbeList(
|
|||
}
|
||||
|
||||
if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
|
||||
p->db->mallocFailed = 1;
|
||||
return SQLITE_NOMEM;
|
||||
assert( p->db->mallocFailed );
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
|
||||
z = displayP4(pOp, pMem->z, 32);
|
||||
|
@ -918,8 +920,8 @@ int sqlite3VdbeList(
|
|||
|
||||
if( p->explain==1 ){
|
||||
if( sqlite3VdbeMemGrow(pMem, 4, 0) ){
|
||||
p->db->mallocFailed = 1;
|
||||
return SQLITE_NOMEM;
|
||||
assert( p->db->mallocFailed );
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
|
||||
pMem->n = 2;
|
||||
|
@ -998,6 +1000,39 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
|
|||
}
|
||||
#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
|
||||
|
@ -1007,6 +1042,14 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
|
|||
**
|
||||
** This is the only way to move a VDBE from VDBE_MAGIC_INIT to
|
||||
** 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(
|
||||
Vdbe *p, /* The VDBE */
|
||||
|
@ -1039,37 +1082,49 @@ void sqlite3VdbeMakeReady(
|
|||
*/
|
||||
nMem += nCursor;
|
||||
|
||||
/*
|
||||
** Allocation space for registers.
|
||||
/* Allocate space for memory registers, SQL variables, VDBE cursors and
|
||||
** 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. */
|
||||
resolveP2Values(p, &nArg);
|
||||
assert( nVar>=0 );
|
||||
if( isExplain && nMem<10 ){
|
||||
nMem = 10;
|
||||
}
|
||||
p->aMem = sqlite3DbMallocZero(db,
|
||||
nMem*sizeof(Mem) /* aMem */
|
||||
+ nVar*sizeof(Mem) /* aVar */
|
||||
+ nArg*sizeof(Mem*) /* apArg */
|
||||
+ nVar*sizeof(char*) /* azVar */
|
||||
+ nCursor*sizeof(VdbeCursor*)+1 /* apCsr */
|
||||
);
|
||||
if( !db->mallocFailed ){
|
||||
p->aMem--; /* aMem[] goes from 1..nMem */
|
||||
p->nMem = nMem; /* not from 0..nMem-1 */
|
||||
p->aVar = &p->aMem[nMem+1];
|
||||
|
||||
do {
|
||||
memset(zCsr, 0, zEnd-zCsr);
|
||||
nByte = 0;
|
||||
allocSpace((char*)&p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->apCsr,
|
||||
nCursor*sizeof(VdbeCursor*), &zCsr, zEnd, &nByte
|
||||
);
|
||||
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->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++){
|
||||
p->aVar[n].flags = MEM_Null;
|
||||
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++){
|
||||
p->aMem[n].flags = MEM_Null;
|
||||
p->aMem[n].db = db;
|
||||
|
@ -1084,14 +1139,13 @@ void sqlite3VdbeMakeReady(
|
|||
|
||||
p->pc = -1;
|
||||
p->rc = SQLITE_OK;
|
||||
p->uniqueCnt = 0;
|
||||
p->errorAction = OE_Abort;
|
||||
p->explain |= isExplain;
|
||||
p->magic = VDBE_MAGIC_RUN;
|
||||
p->nChange = 0;
|
||||
p->cacheCtr = 1;
|
||||
p->minWriteFileFormat = 255;
|
||||
p->openedStatement = 0;
|
||||
p->iStatement = 0;
|
||||
#ifdef VDBE_PROFILE
|
||||
{
|
||||
int i;
|
||||
|
@ -1405,7 +1459,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
|
|||
** sqlite3BtreeCommitPhaseOne(), then there is a chance that the
|
||||
** master journal file will be orphaned. But we cannot delete it,
|
||||
** 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++){
|
||||
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
|
||||
** 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.
|
||||
*/
|
||||
int sqlite3VdbeHalt(Vdbe *p){
|
||||
int rc; /* Used to store transient return codes */
|
||||
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
|
||||
** 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 */
|
||||
if( p->pc>=0 ){
|
||||
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 */
|
||||
sqlite3BtreeMutexArrayEnter(&p->aMutex);
|
||||
|
@ -1571,11 +1667,11 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
*/
|
||||
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
|
||||
if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
p->rc = SQLITE_BUSY;
|
||||
}else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL)
|
||||
&& p->usesStmtJournal ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
}else{
|
||||
/* We are forced to roll back the active transaction. Before doing
|
||||
** 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
|
||||
** we do either a commit or rollback of the current transaction.
|
||||
/* If the auto-commit flag is set and this is the only active writer
|
||||
** 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
|
||||
** above has occurred.
|
||||
|
@ -1604,7 +1700,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
** successful or hit an 'OR FAIL' constraint. This means a commit
|
||||
** is required.
|
||||
*/
|
||||
int rc = vdbeCommit(db, p);
|
||||
rc = vdbeCommit(db, p);
|
||||
if( rc==SQLITE_BUSY ){
|
||||
sqlite3BtreeMutexArrayLeave(&p->aMutex);
|
||||
return SQLITE_BUSY;
|
||||
|
@ -1617,13 +1713,12 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
}else{
|
||||
sqlite3RollbackAll(db);
|
||||
}
|
||||
}else if( !xFunc ){
|
||||
db->nStatement = 0;
|
||||
}else if( eStatementOp==0 ){
|
||||
if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
|
||||
if( p->openedStatement ){
|
||||
xFunc = sqlite3BtreeCommitStmt;
|
||||
}
|
||||
eStatementOp = SAVEPOINT_RELEASE;
|
||||
}else if( p->errorAction==OE_Abort ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
}else{
|
||||
invalidateCursorsOnModifiedBtrees(db);
|
||||
sqlite3RollbackAll(db);
|
||||
|
@ -1632,33 +1727,26 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
}
|
||||
}
|
||||
|
||||
/* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or
|
||||
** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs
|
||||
** and the return code is still SQLITE_OK, set the return code to the new
|
||||
** error value.
|
||||
/* If eStatementOp is non-zero, then a statement transaction needs to
|
||||
** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
|
||||
** do so. If this operation returns an error, and the current statement
|
||||
** error code is SQLITE_OK or SQLITE_CONSTRAINT, then set the error
|
||||
** code to the new value.
|
||||
*/
|
||||
assert(!xFunc ||
|
||||
xFunc==sqlite3BtreeCommitStmt ||
|
||||
xFunc==sqlite3BtreeRollbackStmt
|
||||
);
|
||||
for(i=0; xFunc && i<db->nDb; i++){
|
||||
int rc;
|
||||
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( eStatementOp ){
|
||||
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
|
||||
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,
|
||||
** set the change counter.
|
||||
/* If this was an INSERT, UPDATE or DELETE and no statement transaction
|
||||
** has been rolled back, update the database connection change-counter.
|
||||
*/
|
||||
if( p->changeCntOn && p->pc>=0 ){
|
||||
if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
|
||||
if( eStatementOp!=SAVEPOINT_ROLLBACK ){
|
||||
sqlite3VdbeSetChanges(db, p->nChange);
|
||||
}else{
|
||||
sqlite3VdbeSetChanges(db, 0);
|
||||
|
@ -1690,6 +1778,15 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1847,17 +1944,15 @@ void sqlite3VdbeDelete(Vdbe *p){
|
|||
sqlite3DbFree(db, pOp->zComment);
|
||||
#endif
|
||||
}
|
||||
sqlite3DbFree(db, p->aOp);
|
||||
}
|
||||
releaseMemArray(p->aVar, p->nVar);
|
||||
sqlite3DbFree(db, p->aLabel);
|
||||
if( p->aMem ){
|
||||
sqlite3DbFree(db, &p->aMem[1]);
|
||||
}
|
||||
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
|
||||
sqlite3DbFree(db, p->aColName);
|
||||
sqlite3DbFree(db, p->zSql);
|
||||
p->magic = VDBE_MAGIC_DEAD;
|
||||
sqlite3DbFree(db, p->aOp);
|
||||
sqlite3DbFree(db, p->pFree);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
|
|
42
vdbeblob.c
42
vdbeblob.c
|
@ -12,7 +12,7 @@
|
|||
**
|
||||
** 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"
|
||||
|
@ -70,17 +70,15 @@ int sqlite3_blob_open(
|
|||
/* One of the following two instructions is replaced by an
|
||||
** OP_Noop before exection.
|
||||
*/
|
||||
{OP_SetNumColumns, 0, 0, 0}, /* 2: Num cols for cursor */
|
||||
{OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
|
||||
{OP_SetNumColumns, 0, 0, 0}, /* 4: Num cols for cursor */
|
||||
{OP_OpenWrite, 0, 0, 0}, /* 5: Open cursor 0 for read/write */
|
||||
{OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
|
||||
{OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
|
||||
|
||||
{OP_Variable, 1, 1, 0}, /* 6: Push the rowid to the stack */
|
||||
{OP_NotExists, 0, 10, 1}, /* 7: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 8 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 9 */
|
||||
{OP_Close, 0, 0, 0}, /* 10 */
|
||||
{OP_Halt, 0, 0, 0}, /* 11 */
|
||||
{OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
|
||||
{OP_NotExists, 0, 8, 1}, /* 5: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 6 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 7 */
|
||||
{OP_Close, 0, 0, 0}, /* 8 */
|
||||
{OP_Halt, 0, 0, 0}, /* 9 */
|
||||
};
|
||||
|
||||
Vdbe *v = 0;
|
||||
|
@ -178,19 +176,19 @@ int sqlite3_blob_open(
|
|||
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
||||
** parameter of the other to pTab->tnum.
|
||||
*/
|
||||
sqlite3VdbeChangeToNoop(v, (flags ? 3 : 5), 1);
|
||||
sqlite3VdbeChangeP2(v, (flags ? 5 : 3), pTab->tnum);
|
||||
sqlite3VdbeChangeP3(v, (flags ? 5 : 3), iDb);
|
||||
sqlite3VdbeChangeToNoop(v, (flags ? 2 : 3), 1);
|
||||
sqlite3VdbeChangeP2(v, (flags ? 3 : 2), pTab->tnum);
|
||||
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
|
||||
** does. An OP_Column to retrieve this imaginary column will
|
||||
** always return an SQL NULL. This is useful because it means
|
||||
** we can invoke OP_Column to fill in the vdbe cursors type
|
||||
** and offset cache without causing any IO.
|
||||
*/
|
||||
sqlite3VdbeChangeP2(v, flags ? 4 : 2, pTab->nCol+1);
|
||||
sqlite3VdbeChangeP2(v, 8, pTab->nCol);
|
||||
sqlite3VdbeChangeP4(v, flags ? 3 : 2, SQLITE_INT_TO_PTR(pTab->nCol+1), P4_INT32);
|
||||
sqlite3VdbeChangeP2(v, 6, pTab->nCol);
|
||||
if( !db->mallocFailed ){
|
||||
sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
|
||||
}
|
||||
|
@ -250,8 +248,8 @@ int sqlite3_blob_open(
|
|||
|
||||
blob_open_out:
|
||||
zErr[sizeof(zErr)-1] = '\0';
|
||||
if( rc!=SQLITE_OK || db->mallocFailed ){
|
||||
sqlite3_finalize((sqlite3_stmt *)v);
|
||||
if( v && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(v);
|
||||
}
|
||||
sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr));
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
|
@ -266,9 +264,13 @@ blob_open_out:
|
|||
int sqlite3_blob_close(sqlite3_blob *pBlob){
|
||||
Incrblob *p = (Incrblob *)pBlob;
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
|
||||
db = p->db;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
rc = sqlite3_finalize(p->pStmt);
|
||||
sqlite3DbFree(p->db, p);
|
||||
sqlite3DbFree(db, p);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
11
vdbemem.c
11
vdbemem.c
|
@ -15,7 +15,7 @@
|
|||
** only within the VDBE. Interface routines refer to a Mem using the
|
||||
** 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 "vdbeInt.h"
|
||||
|
@ -321,6 +321,10 @@ static i64 doubleToInt64(double r){
|
|||
if( r<(double)minInt ){
|
||||
return minInt;
|
||||
}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;
|
||||
}else{
|
||||
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
|
||||
** 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
|
||||
** 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){
|
||||
int flags;
|
||||
|
|
11
vtab.c
11
vtab.c
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** 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
|
||||
#include "sqliteInt.h"
|
||||
|
@ -118,7 +118,8 @@ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
|
|||
*/
|
||||
void sqlite3VtabClear(Table *p){
|
||||
sqlite3_vtab *pVtab = p->pVtab;
|
||||
sqlite3 *db = p->db;
|
||||
Schema *pSchema = p->pSchema;
|
||||
sqlite3 *db = pSchema ? pSchema->db : 0;
|
||||
if( pVtab ){
|
||||
assert( p->pMod && p->pMod->pModule );
|
||||
sqlite3VtabUnlock(db, pVtab);
|
||||
|
@ -571,7 +572,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
|||
}
|
||||
sParse.declareVtab = 0;
|
||||
|
||||
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
|
||||
if( sParse.pVdbe ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
}
|
||||
sqlite3DeleteTable(sParse.pNewTable);
|
||||
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
|
||||
** than zero, then this function is being called from within a
|
||||
** 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) ){
|
||||
return SQLITE_LOCKED;
|
||||
|
|
9
walker.c
9
walker.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains routines used for walking the parser tree for
|
||||
** 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 <stdlib.h>
|
||||
|
@ -45,9 +45,10 @@ int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
|
|||
if( rc==WRC_Continue ){
|
||||
if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
||||
if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->pList) ) return WRC_Abort;
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->pSelect) ){
|
||||
return WRC_Abort;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
||||
}else{
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
||||
}
|
||||
}
|
||||
return rc & WRC_Abort;
|
||||
|
|
158
where.c
158
where.c
|
@ -16,7 +16,7 @@
|
|||
** so is applicable. Because this module is responsible for selecting
|
||||
** 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"
|
||||
|
||||
|
@ -431,8 +431,11 @@ static Bitmask exprTableUsage(WhereMaskSet *pMaskSet, Expr *p){
|
|||
}
|
||||
mask = exprTableUsage(pMaskSet, p->pRight);
|
||||
mask |= exprTableUsage(pMaskSet, p->pLeft);
|
||||
mask |= exprListTableUsage(pMaskSet, p->pList);
|
||||
mask |= exprSelectTableUsage(pMaskSet, p->pSelect);
|
||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
mask |= exprSelectTableUsage(pMaskSet, p->x.pSelect);
|
||||
}else{
|
||||
mask |= exprListTableUsage(pMaskSet, p->x.pList);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){
|
||||
|
@ -634,7 +637,7 @@ static int isLikeOrGlob(
|
|||
#ifdef SQLITE_EBCDIC
|
||||
if( *pnoCase ) return 0;
|
||||
#endif
|
||||
pList = pExpr->pList;
|
||||
pList = pExpr->x.pList;
|
||||
pRight = pList->a[0].pExpr;
|
||||
if( pRight->op!=TK_STRING ){
|
||||
return 0;
|
||||
|
@ -653,7 +656,7 @@ static int isLikeOrGlob(
|
|||
(pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){
|
||||
return 0;
|
||||
}
|
||||
sqlite3DequoteExpr(db, pRight);
|
||||
sqlite3DequoteExpr(pRight);
|
||||
z = (char *)pRight->token.z;
|
||||
cnt = 0;
|
||||
if( z ){
|
||||
|
@ -689,7 +692,7 @@ static int isMatchOfColumn(
|
|||
sqlite3StrNICmp((const char*)pExpr->token.z,"match",5)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
pList = pExpr->pList;
|
||||
pList = pExpr->x.pList;
|
||||
if( pList->nExpr!=2 ){
|
||||
return 0;
|
||||
}
|
||||
|
@ -953,17 +956,18 @@ static void exprAnalyzeOrTerm(
|
|||
assert( pOrTerm->eOperator==WO_EQ );
|
||||
assert( pOrTerm->leftCursor==iCursor );
|
||||
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);
|
||||
pLeft = pOrTerm->pExpr->pLeft;
|
||||
}
|
||||
assert( pLeft!=0 );
|
||||
pDup = sqlite3ExprDup(db, pLeft);
|
||||
pDup = sqlite3ExprDup(db, pLeft, 0);
|
||||
pNew = sqlite3Expr(db, TK_IN, pDup, 0, 0);
|
||||
if( pNew ){
|
||||
int idxNew;
|
||||
transferJoinMarkings(pNew, pExpr);
|
||||
pNew->pList = pList;
|
||||
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
|
||||
pNew->x.pList = pList;
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
|
@ -1026,8 +1030,11 @@ static void exprAnalyze(
|
|||
op = pExpr->op;
|
||||
if( op==TK_IN ){
|
||||
assert( pExpr->pRight==0 );
|
||||
pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->pList)
|
||||
| exprSelectTableUsage(pMaskSet, pExpr->pSelect);
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
pTerm->prereqRight = exprSelectTableUsage(pMaskSet, pExpr->x.pSelect);
|
||||
}else{
|
||||
pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->x.pList);
|
||||
}
|
||||
}else if( op==TK_ISNULL ){
|
||||
pTerm->prereqRight = 0;
|
||||
}else{
|
||||
|
@ -1057,7 +1064,7 @@ static void exprAnalyze(
|
|||
Expr *pDup;
|
||||
if( pTerm->leftCursor>=0 ){
|
||||
int idxNew;
|
||||
pDup = sqlite3ExprDup(db, pExpr);
|
||||
pDup = sqlite3ExprDup(db, pExpr, 0);
|
||||
if( db->mallocFailed ){
|
||||
sqlite3ExprDelete(db, pDup);
|
||||
return;
|
||||
|
@ -1100,7 +1107,7 @@ static void exprAnalyze(
|
|||
** BETWEEN term is skipped.
|
||||
*/
|
||||
else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){
|
||||
ExprList *pList = pExpr->pList;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
int i;
|
||||
static const u8 ops[] = {TK_GE, TK_LE};
|
||||
assert( pList!=0 );
|
||||
|
@ -1108,8 +1115,8 @@ static void exprAnalyze(
|
|||
for(i=0; i<2; i++){
|
||||
Expr *pNewExpr;
|
||||
int idxNew;
|
||||
pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft),
|
||||
sqlite3ExprDup(db, pList->a[i].pExpr), 0);
|
||||
pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft, 0),
|
||||
sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0);
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
|
@ -1148,18 +1155,18 @@ static void exprAnalyze(
|
|||
Expr *pNewExpr1, *pNewExpr2;
|
||||
int idxNew1, idxNew2;
|
||||
|
||||
pLeft = pExpr->pList->a[1].pExpr;
|
||||
pRight = pExpr->pList->a[0].pExpr;
|
||||
pLeft = pExpr->x.pList->a[1].pExpr;
|
||||
pRight = pExpr->x.pList->a[0].pExpr;
|
||||
pStr1 = sqlite3PExpr(pParse, TK_STRING, 0, 0, 0);
|
||||
if( pStr1 ){
|
||||
sqlite3TokenCopy(db, &pStr1->token, &pRight->token);
|
||||
pStr1->token.n = nPattern;
|
||||
pStr1->flags = EP_Dequoted;
|
||||
}
|
||||
pStr2 = sqlite3ExprDup(db, pStr1);
|
||||
pStr2 = sqlite3ExprDup(db, pStr1, 0);
|
||||
if( !db->mallocFailed ){
|
||||
u8 c, *pC;
|
||||
assert( pStr2->token.dyn );
|
||||
/* assert( pStr2->token.dyn ); */
|
||||
pC = (u8*)&pStr2->token.z[nPattern-1];
|
||||
c = *pC;
|
||||
if( noCase ){
|
||||
|
@ -1168,11 +1175,11 @@ static void exprAnalyze(
|
|||
}
|
||||
*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);
|
||||
testcase( idxNew1==0 );
|
||||
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);
|
||||
testcase( idxNew2==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew2);
|
||||
|
@ -1198,13 +1205,13 @@ static void exprAnalyze(
|
|||
WhereTerm *pNewTerm;
|
||||
Bitmask prereqColumn, prereqExpr;
|
||||
|
||||
pRight = pExpr->pList->a[0].pExpr;
|
||||
pLeft = pExpr->pList->a[1].pExpr;
|
||||
pRight = pExpr->x.pList->a[0].pExpr;
|
||||
pLeft = pExpr->x.pList->a[1].pExpr;
|
||||
prereqExpr = exprTableUsage(pMaskSet, pRight);
|
||||
prereqColumn = exprTableUsage(pMaskSet, pLeft);
|
||||
if( (prereqExpr & prereqColumn)==0 ){
|
||||
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);
|
||||
testcase( idxNew==0 );
|
||||
pNewTerm = &pWC->a[idxNew];
|
||||
|
@ -1708,15 +1715,15 @@ static double bestVirtualIndex(
|
|||
** * Whether or not there must be separate lookups in the
|
||||
** index and in the main table.
|
||||
**
|
||||
** If there was an INDEXED BY clause attached to the table in the SELECT
|
||||
** statement, then this function only considers plans using the
|
||||
** named index. If one cannot be found, then the returned cost is
|
||||
** SQLITE_BIG_DBL. If a plan can be found that uses the named index,
|
||||
** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in
|
||||
** the SQL statement, then this function only considers plans using the
|
||||
** named index. If no such plan is found, then the returned cost is
|
||||
** SQLITE_BIG_DBL. If a plan is found that uses the named index,
|
||||
** then the cost is calculated in the usual way.
|
||||
**
|
||||
** If a NOT INDEXED clause was attached to the table in the SELECT
|
||||
** statement, then no indexes are considered. However, the selected
|
||||
** plan may still take advantage of the tables built-in rowid
|
||||
** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table
|
||||
** in the SELECT statement, then no indexes are considered. However, the
|
||||
** selected plan may still take advantage of the tables built-in rowid
|
||||
** index.
|
||||
*/
|
||||
static void bestIndex(
|
||||
|
@ -1776,10 +1783,12 @@ static void bestIndex(
|
|||
pCost->rCost = 0;
|
||||
pCost->nRow = 1;
|
||||
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
|
||||
** elements. */
|
||||
pCost->rCost = pCost->nRow = pExpr->pList->nExpr;
|
||||
pCost->rCost = pCost->nRow = pExpr->x.pList->nExpr;
|
||||
pCost->rCost *= estLog(pCost->rCost);
|
||||
}else{
|
||||
/* Rowid IN (SELECT): cost is NlogN where N is the number of rows
|
||||
|
@ -1828,7 +1837,15 @@ static void bestIndex(
|
|||
cost += cost*estLog(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 ){
|
||||
pCost->rCost = cost;
|
||||
pCost->nRow = nRow;
|
||||
|
@ -1909,12 +1926,18 @@ static void bestIndex(
|
|||
pProbe = pSrc->pIndex;
|
||||
}
|
||||
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));
|
||||
|
||||
/* 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;
|
||||
for(i=0; i<pProbe->nColumn; i++){
|
||||
|
@ -1925,23 +1948,33 @@ static void bestIndex(
|
|||
if( pTerm->eOperator & WO_IN ){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
wsFlags |= WHERE_COLUMN_IN;
|
||||
if( pExpr->pSelect!=0 ){
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
inMultiplier *= 25;
|
||||
}else if( pExpr->pList ){
|
||||
inMultiplier *= pExpr->pList->nExpr + 1;
|
||||
inMultIsEst = 1;
|
||||
}else if( pExpr->x.pList ){
|
||||
inMultiplier *= pExpr->x.pList->nExpr + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
if( pProbe->onError!=OE_None && (wsFlags & WHERE_COLUMN_IN)==0
|
||||
&& nEq==pProbe->nColumn ){
|
||||
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 ){
|
||||
int j = pProbe->aiColumn[nEq];
|
||||
|
@ -1958,7 +1991,8 @@ static void bestIndex(
|
|||
cost /= 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);
|
||||
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
|
||||
|
@ -2738,11 +2778,13 @@ static Bitmask codeOneLoopStart(
|
|||
/* Case 5: There is no usable index. We must do a complete
|
||||
** 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( bRev==0 );
|
||||
pLevel->op = OP_Next;
|
||||
pLevel->op = aStep[bRev];
|
||||
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;
|
||||
codeRowSetEarly = 0;
|
||||
}
|
||||
|
@ -2836,7 +2878,7 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
|
|||
assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed );
|
||||
if( pInfo->needToFreeIdxStr ){
|
||||
sqlite3_free(pInfo->idxStr);
|
||||
}
|
||||
}
|
||||
sqlite3DbFree(db, pInfo);
|
||||
}
|
||||
}
|
||||
|
@ -2943,6 +2985,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||
int regRowSet /* Register hold RowSet if WHERE_FILL_ROWSET is set */
|
||||
){
|
||||
int i; /* Loop counter */
|
||||
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
|
||||
WhereInfo *pWInfo; /* Will become the return value of this function */
|
||||
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
|
||||
Bitmask notReady; /* Cursors that are not yet positioned */
|
||||
|
@ -2968,15 +3011,19 @@ WhereInfo *sqlite3WhereBegin(
|
|||
}
|
||||
|
||||
/* 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;
|
||||
pWInfo = sqlite3DbMallocZero(db,
|
||||
sizeof(WhereInfo)
|
||||
+ (pTabList->nSrc-1)*sizeof(WhereLevel)
|
||||
+ sizeof(WhereClause)
|
||||
+ sizeof(WhereMaskSet)
|
||||
);
|
||||
nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel));
|
||||
pWInfo = sqlite3DbMallocZero(db,
|
||||
nByteWInfo +
|
||||
sizeof(WhereClause) +
|
||||
sizeof(WhereMaskSet)
|
||||
);
|
||||
if( db->mallocFailed ){
|
||||
goto whereBeginError;
|
||||
}
|
||||
|
@ -2985,7 +3032,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||
pWInfo->pTabList = pTabList;
|
||||
pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
|
||||
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;
|
||||
pMaskSet = (WhereMaskSet*)&pWC[1];
|
||||
|
||||
|
@ -3231,7 +3278,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||
Bitmask b = pTabItem->colUsed;
|
||||
int n = 0;
|
||||
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 );
|
||||
}
|
||||
}else{
|
||||
|
@ -3244,7 +3291,6 @@ WhereInfo *sqlite3WhereBegin(
|
|||
int iIdxCur = pLevel->iIdxCur;
|
||||
assert( pIx->pSchema==pTab->pSchema );
|
||||
assert( iIdxCur>=0 );
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIx->nColumn+1);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb,
|
||||
(char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIx->zName));
|
||||
|
|
Loading…
Add table
Reference in a new issue