Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -495,13 +495,13 @@ sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl # Rules to build the LEMON compiler generator # -lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c +lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/src/lempar.c $(BCC) -o $@ $(TOP)/tool/lemon.c - cp $(TOP)/tool/lempar.c . + cp $(TOP)/src/lempar.c . # Rule to build the amalgamation # sqlite3.lo: sqlite3.c Index: Makefile.vxworks ================================================================== --- Makefile.vxworks +++ Makefile.vxworks @@ -487,13 +487,13 @@ fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl tclsh $(TOP)/ext/fts3/mkfts3amal.tcl # Rules to build the LEMON compiler generator # -lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c +lemon: $(TOP)/tool/lemon.c $(TOP)/src/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c - cp $(TOP)/tool/lempar.c . + cp $(TOP)/src/lempar.c . # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -3.6.15 +3.6.16 Index: configure ================================================================== --- configure +++ configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for sqlite 3.6.15. +# Generated by GNU Autoconf 2.62 for sqlite 3.6.16. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. @@ -741,12 +741,12 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.6.15' -PACKAGE_STRING='sqlite 3.6.15' +PACKAGE_VERSION='3.6.16' +PACKAGE_STRING='sqlite 3.6.16' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include @@ -1485,11 +1485,11 @@ # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.6.15 to adapt to many kinds of systems. +\`configure' configures sqlite 3.6.16 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. @@ -1550,11 +1550,11 @@ _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.6.15:";; + short | recursive ) echo "Configuration of sqlite 3.6.16:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options @@ -1668,11 +1668,11 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.6.15 +sqlite configure 3.6.16 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation @@ -1682,11 +1682,11 @@ fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.6.15, which was +It was created by sqlite $as_me 3.6.16, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ _ACEOF @@ -2063,11 +2063,11 @@ please regen with autoconf" >&2;} { (exit 1); exit 1; }; } fi # The following RCS revision string applies to configure.in -# $Revision: 1.72 $ +# $Revision: 1.73 $ ######### # Programs needed # case `pwd` in @@ -13970,11 +13970,11 @@ # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.6.15, which was +This file was extended by sqlite $as_me 3.6.16, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS @@ -14023,11 +14023,11 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.6.15 +sqlite config.status 3.6.16 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -343,13 +343,13 @@ fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl tclsh $(TOP)/ext/fts3/mkfts3amal.tcl # Rules to build the LEMON compiler generator # -lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c +lemon: $(TOP)/tool/lemon.c $(TOP)/src/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c - cp $(TOP)/tool/lempar.c . + cp $(TOP)/src/lempar.c . # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o Index: src/auth.c ================================================================== --- src/auth.c +++ src/auth.c @@ -12,11 +12,11 @@ ** This file contains code used to implement the sqlite3_set_authorizer() ** API. This facility is an optional feature of the library. Embedded ** systems that do not need this facility may omit it by recompiling ** the library with -DSQLITE_OMIT_AUTHORIZATION=1 ** -** $Id: auth.c,v 1.31 2009/05/04 18:01:40 drh Exp $ +** $Id: auth.c,v 1.32 2009/07/02 18:40:35 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** All of the code in this file may be omitted by defining a single @@ -110,11 +110,10 @@ int rc; Table *pTab = 0; /* The table being read */ const char *zCol; /* Name of the column of the table */ int iSrc; /* Index in pTabList->a[] of table being read */ const char *zDBase; /* Name of database being accessed */ - TriggerStack *pStack; /* The stack of current triggers */ int iDb; /* The index of the database the expression refers to */ if( db->xAuth==0 ) return; assert( pExpr->op==TK_COLUMN ); iDb = sqlite3SchemaToIndex(pParse->db, pSchema); @@ -122,21 +121,22 @@ /* An attempt to read a column out of a subquery or other ** temporary table. */ return; } if( pTabList ){ - for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ - if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; + for(iSrc=0; iSrcnSrc; iSrc++){ + if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ + pTab = pTabList->a[iSrc].pTab; + break; + } } - assert( iSrcnSrc ); - pTab = pTabList->a[iSrc].pTab; - }else{ - pStack = pParse->trigStack; + } + if( !pTab ){ + TriggerStack *pStack = pParse->trigStack; if( ALWAYS(pStack) ){ /* This must be an attempt to read the NEW or OLD pseudo-tables - ** of a trigger. - */ + ** of a trigger. */ assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx ); pTab = pStack->pTab; } } if( NEVER(pTab==0) ) return; Index: src/backup.c ================================================================== --- src/backup.c +++ src/backup.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** This file contains the implementation of the sqlite3_backup_XXX() ** API functions and the related features. ** -** $Id: backup.c,v 1.17 2009/06/03 11:25:07 danielk1977 Exp $ +** $Id: backup.c,v 1.19 2009/07/06 19:03:13 drh Exp $ */ #include "sqliteInt.h" #include "btreeInt.h" /* Macro to find the minimum of two numeric values. @@ -316,11 +316,11 @@ /* Lock the destination database, if it is not locked already. */ if( SQLITE_OK==rc && p->bDestLocked==0 && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) ){ p->bDestLocked = 1; - rc = sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); + sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); } /* If there is no open read-transaction on the source database, open ** one now. If a transaction is opened here, then it will be closed ** before this function exits. @@ -356,21 +356,22 @@ }else if( !p->isAttached ){ attachBackupObject(p); } } - if( rc==SQLITE_DONE ){ + /* Update the schema version field in the destination database. This + ** is to make sure that the schema-version really does change in + ** the case where the source and destination databases have the + ** same schema version. + */ + if( rc==SQLITE_DONE + && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK + ){ const int nSrcPagesize = sqlite3BtreeGetPageSize(p->pSrc); const int nDestPagesize = sqlite3BtreeGetPageSize(p->pDest); int nDestTruncate; - /* Update the schema version field in the destination database. This - ** is to make sure that the schema-version really does change in - ** the case where the source and destination databases have the - ** same schema version. - */ - sqlite3BtreeUpdateMeta(p->pDest, 1, p->iDestSchema+1); if( p->pDestDb ){ sqlite3ResetInternalSchema(p->pDestDb, 0); } /* Set nDestTruncate to the final number of pages in the destination Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -7,11 +7,11 @@ ** 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. ** ************************************************************************* -** $Id: btree.c,v 1.639 2009/06/23 11:22:29 danielk1977 Exp $ +** $Id: btree.c,v 1.672 2009/07/09 13:25:32 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. ** Including a description of file format and an overview of operation. */ @@ -65,15 +65,10 @@ return SQLITE_OK; } #endif -/* -** Forward declaration -*/ -static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64); - #ifdef SQLITE_OMIT_SHARED_CACHE /* ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(), ** and clearAllSharedCacheTableLocks() @@ -84,13 +79,118 @@ ** So define the lock related functions as no-ops. */ #define querySharedCacheTableLock(a,b,c) SQLITE_OK #define setSharedCacheTableLock(a,b,c) SQLITE_OK #define clearAllSharedCacheTableLocks(a) + #define downgradeAllSharedCacheTableLocks(a) + #define hasSharedCacheTableLock(a,b,c,d) 1 + #define hasReadConflicts(a, b) 0 #endif #ifndef SQLITE_OMIT_SHARED_CACHE + +#ifdef SQLITE_DEBUG +/* +** This function is only used as part of an assert() statement. It checks +** that connection p holds the required locks to read or write to the +** b-tree with root page iRoot. If so, true is returned. Otherwise, false. +** For example, when writing to a table b-tree with root-page iRoot via +** Btree connection pBtree: +** +** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); +** +** When writing to an index b-tree that resides in a sharable database, the +** caller should have first obtained a lock specifying the root page of +** the corresponding table b-tree. This makes things a bit more complicated, +** as this module treats each b-tree as a separate structure. To determine +** the table b-tree corresponding to the index b-tree being written, this +** function has to search through the database schema. +** +** Instead of a lock on the b-tree rooted at page iRoot, the caller may +** hold a write-lock on the schema table (root page 1). This is also +** acceptable. +*/ +static int hasSharedCacheTableLock( + Btree *pBtree, /* Handle that must hold lock */ + Pgno iRoot, /* Root page of b-tree */ + int isIndex, /* True if iRoot is the root of an index b-tree */ + int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */ +){ + Schema *pSchema = (Schema *)pBtree->pBt->pSchema; + Pgno iTab = 0; + BtLock *pLock; + + /* If this b-tree database is not shareable, or if the client is reading + ** and has the read-uncommitted flag set, then no lock is required. + ** In these cases return true immediately. If the client is reading + ** or writing an index b-tree, but the schema is not loaded, then return + ** true also. In this case the lock is required, but it is too difficult + ** to check if the client actually holds it. This doesn't happen very + ** often. */ + if( (pBtree->sharable==0) + || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted)) + || (isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0 )) + ){ + return 1; + } + + /* Figure out the root-page that the lock should be held on. For table + ** b-trees, this is just the root page of the b-tree being read or + ** written. For index b-trees, it is the root page of the associated + ** table. */ + if( isIndex ){ + HashElem *p; + for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ + Index *pIdx = (Index *)sqliteHashData(p); + if( pIdx->tnum==iRoot ){ + iTab = pIdx->pTable->tnum; + } + } + }else{ + iTab = iRoot; + } + + /* Search for the required lock. Either a write-lock on root-page iTab, a + ** write-lock on the schema table, or (if the client is reading) a + ** read-lock on iTab will suffice. Return 1 if any of these are found. */ + for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ + if( pLock->pBtree==pBtree + && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) + && pLock->eLock>=eLockType + ){ + return 1; + } + } + + /* Failed to find the required lock. */ + return 0; +} + +/* +** This function is also used as part of assert() statements only. It +** returns true if there exist one or more cursors open on the table +** with root page iRoot that do not belong to either connection pBtree +** or some other connection that has the read-uncommitted flag set. +** +** For example, before writing to page iRoot: +** +** assert( !hasReadConflicts(pBtree, iRoot) ); +*/ +static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ + BtCursor *p; + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ + if( p->pgnoRoot==iRoot + && p->pBtree!=pBtree + && 0==(p->pBtree->db->flags & SQLITE_ReadUncommitted) + ){ + return 1; + } + } + return 0; +} +#endif /* #ifdef SQLITE_DEBUG */ + /* ** 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 ** setSharedCacheTableLock()), or SQLITE_LOCKED if not. @@ -100,10 +200,11 @@ BtLock *pIter; assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); + assert( !(p->db->flags&SQLITE_ReadUncommitted)||eLock==WRITE_LOCK||iTab==1 ); /* If requesting a write-lock, then the Btree must have an open write ** transaction on this file. And, obviously, for this to be so there ** must be an open write transaction on the file itself. */ @@ -121,51 +222,29 @@ if( pBt->pWriter!=p && pBt->isExclusive ){ sqlite3ConnectionBlocked(p->db, pBt->pWriter->db); return SQLITE_LOCKED_SHAREDCACHE; } - /* 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 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 - ** 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) || - eLock==WRITE_LOCK || - iTab==MASTER_ROOT - ){ - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - /* The condition (pIter->eLock!=eLock) in the following if(...) - ** statement is a simplification of: - ** - ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) - ** - ** since we know that if eLock==WRITE_LOCK, then no other connection - ** may hold a WRITE_LOCK on any table in this file (since there can - ** only be a single writer). - */ - assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); - assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); - if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ - sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); - if( eLock==WRITE_LOCK ){ - assert( p==pBt->pWriter ); - pBt->isPending = 1; - } - return SQLITE_LOCKED_SHAREDCACHE; - } + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + /* The condition (pIter->eLock!=eLock) in the following if(...) + ** statement is a simplification of: + ** + ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) + ** + ** since we know that if eLock==WRITE_LOCK, then no other connection + ** may hold a WRITE_LOCK on any table in this file (since there can + ** only be a single writer). + */ + assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); + assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); + if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ + sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); + if( eLock==WRITE_LOCK ){ + assert( p==pBt->pWriter ); + pBt->isPending = 1; + } + return SQLITE_LOCKED_SHAREDCACHE; } } return SQLITE_OK; } #endif /* !SQLITE_OMIT_SHARED_CACHE */ @@ -174,12 +253,21 @@ /* ** Add a lock on the table with root-page iTable to the shared-btree used ** by Btree handle p. Parameter eLock must be either READ_LOCK or ** WRITE_LOCK. ** -** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and -** SQLITE_NOMEM may also be returned. +** This function assumes the following: +** +** (a) The specified b-tree connection handle is connected to a sharable +** b-tree database (one with the BtShared.sharable) flag set, and +** +** (b) No other b-tree connection handle holds a lock that conflicts +** with the requested lock (i.e. querySharedCacheTableLock() has +** already been called and returned SQLITE_OK). +** +** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM +** is returned if a malloc attempt fails. */ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pLock = 0; BtLock *pIter; @@ -186,30 +274,20 @@ assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); - /* This is a no-op if the shared-cache is not enabled */ - if( !p->sharable ){ - return SQLITE_OK; - } - - assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); - - /* 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) && - (eLock==READ_LOCK) && - iTable!=MASTER_ROOT - ){ - return SQLITE_OK; - } + /* A connection with the read-uncommitted flag set will never try to + ** obtain a read-lock using this function. The only read-lock obtained + ** by a connection in read-uncommitted mode is on the sqlite_master + ** table, and that lock is obtained in BtreeBeginTrans(). */ + assert( 0==(p->db->flags&SQLITE_ReadUncommitted) || eLock==WRITE_LOCK ); + + /* This function should only be called on a sharable b-tree after it + ** has been determined that no other b-tree holds a conflicting lock. */ + assert( p->sharable ); + assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); /* First search the list for an existing lock on this table. */ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->iTable==iTable && pIter->pBtree==p ){ pLock = pIter; @@ -265,11 +343,14 @@ BtLock *pLock = *ppIter; assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree ); assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; - sqlite3_free(pLock); + assert( pLock->iTable!=1 || pLock==&p->lock ); + if( pLock->iTable!=1 ){ + sqlite3_free(pLock); + } }else{ ppIter = &pLock->pNext; } } @@ -289,10 +370,28 @@ ** be zero already. So this next line is harmless in that case. */ pBt->isPending = 0; } } + +/* +** This function changes all write-locks held by connection p to read-locks. +*/ +static void downgradeAllSharedCacheTableLocks(Btree *p){ + BtShared *pBt = p->pBt; + if( pBt->pWriter==p ){ + BtLock *pLock; + pBt->pWriter = 0; + pBt->isExclusive = 0; + pBt->isPending = 0; + for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ + assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); + pLock->eLock = READ_LOCK; + } + } +} + #endif /* SQLITE_OMIT_SHARED_CACHE */ static void releasePage(MemPage *pPage); /* Forward reference */ /* @@ -324,13 +423,45 @@ assert( sqlite3_mutex_held(pBt->mutex) ); for(p=pBt->pCursor; p; p=p->pNext){ invalidateOverflowCache(p); } } + +/* +** This function is called before modifying the contents of a table +** b-tree to invalidate any incrblob cursors that are open on the +** row or one of the rows being modified. Argument pgnoRoot is the +** root-page of the table b-tree. +** +** If argument isClearTable is true, then the entire contents of the +** table is about to be deleted. In this case invalidate all incrblob +** cursors open on any row within the table with root-page pgnoRoot. +** +** Otherwise, if argument isClearTable is false, then the row with +** rowid iRow is being replaced or deleted. In this case invalidate +** only those incrblob cursors open on this specific row. +*/ +static void invalidateIncrblobCursors( + Btree *pBtree, /* The database file to check */ + Pgno pgnoRoot, /* Look for read cursors on this btree */ + i64 iRow, /* The rowid that might be changing */ + int isClearTable /* True if all rows are being deleted */ +){ + BtCursor *p; + BtShared *pBt = pBtree->pBt; + assert( sqlite3BtreeHoldsMutex(pBtree) ); + for(p=pBt->pCursor; p; p=p->pNext){ + if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){ + p->eState = CURSOR_INVALID; + } + } +} + #else #define invalidateOverflowCache(x) #define invalidateAllOverflowCache(x) + #define invalidateIncrblobCursors(w,x,y,z) #endif /* ** Set bit pgno of the BtShared.pHasContent bitvec. This is called ** when a page that previously contained data becomes a free-list leaf @@ -481,27 +612,58 @@ assert( cursorHoldsMutex(pCur) ); sqlite3_free(pCur->pKey); pCur->pKey = 0; pCur->eState = CURSOR_INVALID; } + +/* +** In this version of BtreeMoveto, pKey is a packed index record +** such as is generated by the OP_MakeRecord opcode. Unpack the +** record and then call BtreeMovetoUnpacked() to do the work. +*/ +static int btreeMoveto( + BtCursor *pCur, /* Cursor open on the btree to be searched */ + const void *pKey, /* Packed key if the btree is an index */ + i64 nKey, /* Integer key for tables. Size of pKey for indices */ + int bias, /* Bias search to the high end */ + int *pRes /* Write search results here */ +){ + int rc; /* Status code */ + UnpackedRecord *pIdxKey; /* Unpacked index key */ + char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ + + if( pKey ){ + assert( nKey==(i64)(int)nKey ); + pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, + aSpace, sizeof(aSpace)); + if( pIdxKey==0 ) return SQLITE_NOMEM; + }else{ + pIdxKey = 0; + } + rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); + if( pKey ){ + sqlite3VdbeDeleteUnpackedRecord(pIdxKey); + } + return rc; +} /* ** Restore the cursor to the position it was in (or as close to as possible) ** when saveCursorPosition() was called. Note that this call deletes the ** saved position info stored by saveCursorPosition(), so there can be ** at most one effective restoreCursorPosition() call after each ** saveCursorPosition(). */ -int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur){ +static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; assert( cursorHoldsMutex(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ return pCur->skip; } pCur->eState = CURSOR_INVALID; - rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip); + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip); if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); } @@ -508,11 +670,11 @@ return rc; } #define restoreCursorPosition(p) \ (p->eState>=CURSOR_REQUIRESEEK ? \ - sqlite3BtreeRestoreCursorPosition(p) : \ + btreeRestoreCursorPosition(p) : \ SQLITE_OK) /* ** Determine whether or not a cursor has moved from the position it ** was last placed at. Cursors can move when the row they are pointing @@ -583,11 +745,12 @@ if( rc!=SQLITE_OK ){ return rc; } offset = PTRMAP_PTROFFSET(iPtrmap, key); if( offset<0 ){ - return SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; } pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); @@ -596,10 +759,11 @@ pPtrmap[offset] = eType; put4byte(&pPtrmap[offset+1], parent); } } +ptrmap_exit: sqlite3PagerUnref(pDbPage); return rc; } /* @@ -636,10 +800,11 @@ } #else /* if defined SQLITE_OMIT_AUTOVACUUM */ #define ptrmapPut(w,x,y,z) SQLITE_OK #define ptrmapGet(w,x,y,z) SQLITE_OK + #define ptrmapPutOvflPtr(x, y) SQLITE_OK #endif /* ** Given a btree page and a cell index (0 means the first cell on ** the page, 1 means the second cell, and so forth) return a pointer @@ -650,11 +815,11 @@ #define findCell(P,I) \ ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)]))) /* ** This a more complex version of findCell() that works for -** pages that do contain overflow cells. See insert +** pages that do contain overflow cells. */ static u8 *findOverflowCell(MemPage *pPage, int iCell){ int i; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); for(i=pPage->nOverflow-1; i>=0; i--){ @@ -672,18 +837,18 @@ return findCell(pPage, iCell); } /* ** Parse a cell content block and fill in the CellInfo structure. There -** are two versions of this function. sqlite3BtreeParseCell() takes a -** cell index as the second argument and sqlite3BtreeParseCellPtr() +** are two versions of this function. btreeParseCell() takes a +** cell index as the second argument and btreeParseCellPtr() ** takes a pointer to the body of the cell as its second argument. ** ** Within this file, the parseCell() macro can be called instead of -** sqlite3BtreeParseCellPtr(). Using some compilers, this will be faster. +** btreeParseCellPtr(). Using some compilers, this will be faster. */ -void sqlite3BtreeParseCellPtr( +static void btreeParseCellPtr( MemPage *pPage, /* Page containing the cell */ u8 *pCell, /* Pointer to the cell text. */ CellInfo *pInfo /* Fill in this structure */ ){ u16 n; /* Number bytes in cell content header */ @@ -708,10 +873,12 @@ n += getVarint32(&pCell[n], nPayload); pInfo->nKey = nPayload; } pInfo->nPayload = nPayload; pInfo->nHeader = n; + testcase( nPayload==pPage->maxLocal ); + testcase( nPayload==pPage->maxLocal+1 ); if( likely(nPayload<=pPage->maxLocal) ){ /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. */ int nSize; /* Total size of cell content in bytes */ @@ -737,10 +904,12 @@ int surplus; /* Overflow payload available for local storage */ minLocal = pPage->minLocal; maxLocal = pPage->maxLocal; surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4); + testcase( surplus==maxLocal ); + testcase( surplus==maxLocal+1 ); if( surplus <= maxLocal ){ pInfo->nLocal = (u16)surplus; }else{ pInfo->nLocal = (u16)minLocal; } @@ -747,12 +916,12 @@ pInfo->iOverflow = (u16)(pInfo->nLocal + n); pInfo->nSize = pInfo->iOverflow + 4; } } #define parseCell(pPage, iCell, pInfo) \ - sqlite3BtreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) -void sqlite3BtreeParseCell( + btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) +static void btreeParseCell( MemPage *pPage, /* Page containing the cell */ int iCell, /* The cell index. First cell is 0 */ CellInfo *pInfo /* Fill in this structure */ ){ parseCell(pPage, iCell, pInfo); @@ -772,11 +941,11 @@ /* The value returned by this function should always be the same as ** the (CellInfo.nSize) value found by doing a full parse of the ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of ** this function verifies that this invariant is not violated. */ CellInfo debuginfo; - sqlite3BtreeParseCellPtr(pPage, pCell, &debuginfo); + btreeParseCellPtr(pPage, pCell, &debuginfo); #endif if( pPage->intKey ){ u8 *pEnd; if( pPage->hasData ){ @@ -792,13 +961,17 @@ while( (*pIter++)&0x80 && pItermaxLocal ); + testcase( nSize==pPage->maxLocal+1 ); if( nSize>pPage->maxLocal ){ int minLocal = pPage->minLocal; nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); + testcase( nSize==pPage->maxLocal+1 ); if( nSize>pPage->maxLocal ){ nSize = minLocal; } nSize += 4; } @@ -825,11 +998,11 @@ ** for the overflow page. */ static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){ CellInfo info; assert( pCell!=0 ); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload ); if( info.iOverflow ){ Pgno ovfl = get4byte(&pCell[info.iOverflow]); return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno); } @@ -845,19 +1018,21 @@ ** pointer array and the cell content area. */ static int defragmentPage(MemPage *pPage){ int i; /* Loop counter */ int pc; /* Address of a i-th cell */ - int addr; /* Offset of first byte after cell pointer array */ int hdr; /* Offset to the page header */ int size; /* Size of a cell */ int usableSize; /* Number of usable bytes on a page */ int cellOffset; /* Offset to the cell pointer array */ int cbrk; /* Offset to the cell content area */ int nCell; /* Number of cells on the page */ unsigned char *data; /* The page data */ unsigned char *temp; /* Temp area for cell content */ + int iCellFirst; /* First allowable cell index */ + int iCellLast; /* Last possible cell index */ + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt!=0 ); assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); assert( pPage->nOverflow==0 ); @@ -870,89 +1045,111 @@ assert( nCell==get2byte(&data[hdr+3]) ); usableSize = pPage->pBt->usableSize; cbrk = get2byte(&data[hdr+5]); memcpy(&temp[cbrk], &data[cbrk], usableSize - cbrk); cbrk = usableSize; + iCellFirst = cellOffset + 2*nCell; + iCellLast = usableSize - 4; for(i=0; i=usableSize ){ + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); +#if !defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) + /* These conditions have already been verified in btreeInitPage() + ** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined + */ + if( pciCellLast ){ return SQLITE_CORRUPT_BKPT; } +#endif + assert( pc>=iCellFirst && pc<=iCellLast ); size = cellSizePtr(pPage, &temp[pc]); cbrk -= size; - if( cbrkusableSize ){ +#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) + if( cbrkusableSize ){ return SQLITE_CORRUPT_BKPT; } - assert( cbrk+size<=usableSize && cbrk>=0 ); +#endif + assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); + testcase( cbrk+size==usableSize ); + testcase( pc+size==usableSize ); memcpy(&data[cbrk], &temp[pc], size); put2byte(pAddr, cbrk); } - assert( cbrk>=cellOffset+2*nCell ); + assert( cbrk>=iCellFirst ); put2byte(&data[hdr+5], cbrk); data[hdr+1] = 0; data[hdr+2] = 0; data[hdr+7] = 0; - addr = cellOffset+2*nCell; - memset(&data[addr], 0, cbrk-addr); + memset(&data[iCellFirst], 0, cbrk-iCellFirst); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - if( cbrk-addr!=pPage->nFree ){ + if( cbrk-iCellFirst!=pPage->nFree ){ return SQLITE_CORRUPT_BKPT; } return SQLITE_OK; } /* ** Allocate nByte bytes of space from within the B-Tree page passed -** as the first argument. Return the index into pPage->aData[] of the -** first byte of allocated space. -** -** The caller guarantees that the space between the end of the cell-offset -** array and the start of the cell-content area is at least nByte bytes -** in size. So this routine can never fail. -** -** If there are already 60 or more bytes of fragments within the page, -** the page is defragmented before returning. If this were not done there -** is a chance that the number of fragmented bytes could eventually -** overflow the single-byte field of the page-header in which this value -** is stored. -*/ -static int allocateSpace(MemPage *pPage, int nByte){ +** as the first argument. Write into *pIdx the index into pPage->aData[] +** of the first byte of allocated space. Return either SQLITE_OK or +** an error code (usually SQLITE_CORRUPT). +** +** The caller guarantees that there is sufficient space to make the +** allocation. This routine might need to defragment in order to bring +** all the space together, however. This routine will avoid using +** the first two bytes past the cell pointer area since presumably this +** allocation is being made in order to insert a new cell, so we will +** also end up needing a new cell pointer. +*/ +static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ u8 * const data = pPage->aData; /* Local cache of pPage->aData */ int nFrag; /* Number of fragmented bytes on pPage */ - int top; + int top; /* First byte of cell content area */ + int gap; /* First byte of gap between cell pointers and cell content */ + int rc; /* Integer return code */ assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( nByte>=0 ); /* Minimum cell size is 4 */ assert( pPage->nFree>=nByte ); assert( pPage->nOverflow==0 ); - /* Assert that the space between the cell-offset array and the - ** cell-content area is greater than nByte bytes. - */ - assert( nByte <= ( - get2byte(&data[hdr+5])-(hdr+8+(pPage->leaf?0:4)+2*get2byte(&data[hdr+3])) - )); - - pPage->nFree -= (u16)nByte; nFrag = data[hdr+7]; + assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); + gap = pPage->cellOffset + 2*pPage->nCell; + top = get2byte(&data[hdr+5]); + if( gap>top ) return SQLITE_CORRUPT_BKPT; + testcase( gap+2==top ); + testcase( gap+1==top ); + testcase( gap==top ); + if( nFrag>=60 ){ - defragmentPage(pPage); - }else{ + /* Always defragment highly fragmented pages */ + rc = defragmentPage(pPage); + if( rc ) return rc; + top = get2byte(&data[hdr+5]); + }else if( gap+2<=top ){ /* Search the freelist looking for a free slot big enough to satisfy ** the request. The allocation is made from the first free slot in ** the list that is large enough to accomadate it. */ int pc, addr; for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){ int size = get2byte(&data[pc+2]); /* Size of free slot */ if( size>=nByte ){ int x = size - nByte; + testcase( x==4 ); + testcase( x==3 ); if( x<4 ){ /* Remove the slot from the free-list. Update the number of ** fragmented bytes within the page. */ memcpy(&data[addr], &data[pc], 2); data[hdr+7] = (u8)(nFrag + x); @@ -959,21 +1156,35 @@ }else{ /* The slot remains on the free-list. Reduce its size to account ** for the portion used by the new allocation. */ put2byte(&data[pc+2], x); } - return pc + x; + *pIdx = pc + x; + return SQLITE_OK; } } } + + /* Check to make sure there is enough space in the gap to satisfy + ** the allocation. If not, defragment. + */ + testcase( gap+2+nByte==top ); + if( gap+2+nByte>top ){ + rc = defragmentPage(pPage); + if( rc ) return rc; + top = get2byte(&data[hdr+5]); + assert( gap+nByte<=top ); + } + /* Allocate memory from the gap in between the cell pointer array ** and the cell content area. */ - top = get2byte(&data[hdr+5]) - nByte; + top -= nByte; put2byte(&data[hdr+5], top); - return top; + *pIdx = top; + return SQLITE_OK; } /* ** Return a section of the pPage->aData to the freelist. ** The first byte of the new free block is pPage->aDisk[start] @@ -982,10 +1193,11 @@ ** Most of the effort here is involved in coalesing adjacent ** free blocks into a single big free block. */ static int freeSpace(MemPage *pPage, int start, int size){ int addr, pbegin, hdr; + int iLast; /* Largest possible freeblock offset */ unsigned char *data = pPage->aData; assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) ); @@ -997,43 +1209,52 @@ /* Overwrite deleted information with zeros when the SECURE_DELETE ** option is enabled at compile-time */ memset(&data[start], 0, size); #endif - /* Add the space back into the linked list of freeblocks */ + /* Add the space back into the linked list of freeblocks. Note that + ** even though the freeblock list was checked by btreeInitPage(), + ** btreeInitPage() did not detect overlapping cells or + ** freeblocks that overlapped cells. Nor does it detect when the + ** cell content area exceeds the value in the page header. If these + ** situations arise, then subsequent insert operations might corrupt + ** the freelist. So we do need to check for corruption while scanning + ** the freelist. + */ hdr = pPage->hdrOffset; addr = hdr + 1; + iLast = pPage->pBt->usableSize - 4; + assert( start<=iLast ); while( (pbegin = get2byte(&data[addr]))0 ){ - assert( pbegin<=pPage->pBt->usableSize-4 ); - if( pbegin<=addr ) { + if( pbeginpPage->pBt->usableSize-4 ) { + if( pbegin>iLast ){ return SQLITE_CORRUPT_BKPT; } assert( pbegin>addr || pbegin==0 ); put2byte(&data[addr], start); put2byte(&data[start], pbegin); put2byte(&data[start+2], size); - pPage->nFree += (u16)size; + pPage->nFree = pPage->nFree + (u16)size; /* Coalesce adjacent free blocks */ - addr = pPage->hdrOffset + 1; + addr = hdr + 1; while( (pbegin = get2byte(&data[addr]))>0 ){ int pnext, psize, x; assert( pbegin>addr ); assert( pbegin<=pPage->pBt->usableSize-4 ); pnext = get2byte(&data[pbegin]); psize = get2byte(&data[pbegin+2]); if( pbegin + psize + 3 >= pnext && pnext>0 ){ int frag = pnext - (pbegin+psize); - if( (frag<0) || (frag>(int)data[pPage->hdrOffset+7]) ){ + if( (frag<0) || (frag>(int)data[hdr+7]) ){ return SQLITE_CORRUPT_BKPT; } - data[pPage->hdrOffset+7] -= (u8)frag; + data[hdr+7] -= (u8)frag; x = get2byte(&data[pnext]); put2byte(&data[pbegin], x); x = pnext + get2byte(&data[pnext+2]) - pbegin; put2byte(&data[pbegin+2], x); }else{ @@ -1097,11 +1318,11 @@ ** not contain a well-formed database page, then return ** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not ** guarantee that the page is well-formed. It only shows that ** we failed to detect any corruption. */ -int sqlite3BtreeInitPage(MemPage *pPage){ +static int btreeInitPage(MemPage *pPage){ assert( pPage->pBt!=0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); @@ -1114,10 +1335,12 @@ BtShared *pBt; /* The main btree structure */ u16 usableSize; /* Amount of usable space on each page */ u16 cellOffset; /* Offset from start of page to first cell pointer */ u16 nFree; /* Number of unused bytes on the page */ u16 top; /* First byte of the cell content area */ + int iCellFirst; /* First allowable cell or freeblock offset */ + int iCellLast; /* Last possible cell or freeblock offset */ pBt = pPage->pBt; hdr = pPage->hdrOffset; data = pPage->aData; @@ -1131,54 +1354,57 @@ pPage->nCell = get2byte(&data[hdr+3]); if( pPage->nCell>MX_CELL(pBt) ){ /* To many cells for a single page. The page must be corrupt */ return SQLITE_CORRUPT_BKPT; } + testcase( pPage->nCell==MX_CELL(pBt) ); /* A malformed database page might cause use to read past the end ** of page when parsing a cell. ** ** The following block of code checks early to see if a cell extends ** past the end of a page boundary and causes SQLITE_CORRUPT to be ** returned if it does. */ + iCellFirst = cellOffset + 2*pPage->nCell; + iCellLast = usableSize - 4; #if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) { - int iCellFirst; /* First allowable cell index */ - int iCellLast; /* Last possible cell index */ int i; /* Index into the cell pointer array */ int sz; /* Size of a cell */ - iCellFirst = cellOffset + 2*pPage->nCell; - iCellLast = usableSize - 4; if( !pPage->leaf ) iCellLast--; for(i=0; inCell; i++){ pc = get2byte(&data[cellOffset+i*2]); + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); if( pciCellLast ){ return SQLITE_CORRUPT_BKPT; } sz = cellSizePtr(pPage, &data[pc]); + testcase( pc+sz==usableSize ); if( pc+sz>usableSize ){ return SQLITE_CORRUPT_BKPT; } } + if( !pPage->leaf ) iCellLast++; } #endif /* Compute the total free space on the page */ pc = get2byte(&data[hdr+1]); nFree = data[hdr+7] + top; while( pc>0 ){ u16 next, size; - if( pc>usableSize-4 ){ + if( pciCellLast ){ /* Free block is off the page */ return SQLITE_CORRUPT_BKPT; } next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); if( next>0 && next<=pc+size+3 ){ - /* Free blocks must be in accending order */ + /* Free blocks must be in ascending order */ return SQLITE_CORRUPT_BKPT; } nFree = nFree + size; pc = next; } @@ -1191,32 +1417,11 @@ ** area, according to the page header, lies within the page. */ if( nFree>usableSize ){ return SQLITE_CORRUPT_BKPT; } - pPage->nFree = nFree - (cellOffset + 2*pPage->nCell); - -#if 0 - /* Check that all the offsets in the cell offset array are within range. - ** - ** Omitting this consistency check and using the pPage->maskPage mask - ** to prevent overrunning the page buffer in findCell() results in a - ** 2.5% performance gain. - */ - { - u8 *pOff; /* Iterator used to check all cell offsets are in range */ - u8 *pEnd; /* Pointer to end of cell offset array */ - u8 mask; /* Mask of bits that must be zero in MSB of cell offsets */ - mask = ~(((u8)(pBt->pageSize>>8))-1); - pEnd = &data[cellOffset + pPage->nCell*2]; - for(pOff=&data[cellOffset]; pOff!=pEnd && !((*pOff)&mask); pOff+=2); - if( pOff!=pEnd ){ - return SQLITE_CORRUPT_BKPT; - } - } -#endif - + pPage->nFree = nFree - iCellFirst; pPage->isInit = 1; } return SQLITE_OK; } @@ -1276,11 +1481,11 @@ ** to fetch the content. Just fill in the content with zeros for now. ** If in the future we call sqlite3PagerWrite() on this page, that ** means we have started to be concerned about content and the disk ** read should occur at that point. */ -int sqlite3BtreeGetPage( +static int btreeGetPage( BtShared *pBt, /* The btree */ Pgno pgno, /* Number of the page to fetch */ MemPage **ppPage, /* Return the page in this parameter */ int noContent /* Do not load page content if true */ ){ @@ -1323,11 +1528,11 @@ } /* ** Get a page from the pager and initialize it. This routine ** is just a convenience wrapper around separate calls to -** sqlite3BtreeGetPage() and sqlite3BtreeInitPage(). +** btreeGetPage() and btreeInitPage(). */ static int getAndInitPage( BtShared *pBt, /* The database file */ Pgno pgno, /* Number of the page to get */ MemPage **ppPage /* Write the page pointer here */ @@ -1349,19 +1554,20 @@ if( pPage ){ /* Page is already in cache */ rc = SQLITE_OK; }else{ /* Page not in cache. Acquire it. */ + testcase( pgno==pagerPagecount(pBt) ); if( pgno>pagerPagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } - rc = sqlite3BtreeGetPage(pBt, pgno, ppPage, 0); + rc = btreeGetPage(pBt, pgno, ppPage, 0); if( rc ) return rc; pPage = *ppPage; } if( !pPage->isInit ){ - rc = sqlite3BtreeInitPage(pPage); + rc = btreeInitPage(pPage); } if( rc!=SQLITE_OK ){ releasePage(pPage); *ppPage = 0; } @@ -1368,11 +1574,11 @@ return rc; } /* ** Release a MemPage. This should be called once for each prior -** call to sqlite3BtreeGetPage. +** call to btreeGetPage. */ static void releasePage(MemPage *pPage){ if( pPage ){ assert( pPage->nOverflow==0 || sqlite3PagerPageRefcount(pPage->pDbPage)>1 ); assert( pPage->aData ); @@ -1400,15 +1606,15 @@ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); pPage->isInit = 0; if( sqlite3PagerPageRefcount(pData)>1 ){ /* 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. + ** call to btreeInitPage() 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 + ** btreeInitPage() be called on every btree page so we make ** the call for every page that comes in for re-initing. */ - sqlite3BtreeInitPage(pPage); + btreeInitPage(pPage); } } } /* @@ -1472,10 +1678,14 @@ if( !p ){ return SQLITE_NOMEM; } p->inTrans = TRANS_NONE; p->db = db; +#ifndef SQLITE_OMIT_SHARED_CACHE + p->lock.pBtree = p; + p->lock.iTable = 1; +#endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* ** If this Btree is a candidate for shared cache, try to find an ** existing BtShared object that we can share with @@ -1983,11 +2193,11 @@ MemPage *pPage1; int nPage; assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); - rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0); + rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ @@ -2077,50 +2287,26 @@ releasePage(pPage1); pBt->pPage1 = 0; return rc; } -/* -** This routine works like lockBtree() except that it also invokes the -** busy callback if there is lock contention. -*/ -static int lockBtreeWithRetry(Btree *pRef){ - int rc = SQLITE_OK; - - assert( sqlite3BtreeHoldsMutex(pRef) ); - if( pRef->inTrans==TRANS_NONE ){ - u8 inTransaction = pRef->pBt->inTransaction; - btreeIntegrity(pRef); - rc = sqlite3BtreeBeginTrans(pRef, 0); - pRef->pBt->inTransaction = inTransaction; - pRef->inTrans = TRANS_NONE; - if( rc==SQLITE_OK ){ - pRef->pBt->nTransaction--; - } - btreeIntegrity(pRef); - } - return rc; -} - - /* ** If there are no outstanding cursors and we are not in the middle ** of a transaction but there is a read lock on the database, then ** this routine unrefs the first page of the database file which ** has the effect of releasing the read lock. ** -** If there are any outstanding cursors, this routine is a no-op. -** ** If there is a transaction in progress, this routine is a no-op. */ static void unlockBtreeIfUnused(BtShared *pBt){ assert( sqlite3_mutex_held(pBt->mutex) ); - if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ - if( sqlite3PagerRefcount(pBt->pPager)>=1 ){ - assert( pBt->pPage1->aData ); - releasePage(pBt->pPage1); - } + assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE ); + if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ + assert( pBt->pPage1->aData ); + assert( sqlite3PagerRefcount(pBt->pPager)==1 ); + assert( pBt->pPage1->aData ); + releasePage(pBt->pPage1); pBt->pPage1 = 0; } } /* @@ -2242,10 +2428,17 @@ sqlite3ConnectionBlocked(p->db, pBlock); rc = SQLITE_LOCKED_SHAREDCACHE; goto trans_begun; } #endif + + /* Any read-only or read-write transaction implies a read-lock on + ** page 1. So if some other shared-cache client already has a write-lock + ** on page 1, the transaction cannot be opened. */ + if( SQLITE_OK!=(rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK)) ){ + goto trans_begun; + } do { /* Call lockBtree() until either pBt->pPage1 is populated or ** lockBtree() returns something other than SQLITE_OK. lockBtree() ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after @@ -2273,10 +2466,18 @@ btreeInvokeBusyHandler(pBt) ); if( rc==SQLITE_OK ){ if( p->inTrans==TRANS_NONE ){ pBt->nTransaction++; +#ifndef SQLITE_OMIT_SHARED_CACHE + if( p->sharable ){ + assert( p->lock.pBtree==p && p->lock.iTable==1 ); + p->lock.eLock = READ_LOCK; + p->lock.pNext = pBt->pLock; + pBt->pLock = &p->lock; + } +#endif } p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); if( p->inTrans>pBt->inTransaction ){ pBt->inTransaction = p->inTrans; } @@ -2318,11 +2519,11 @@ BtShared *pBt = pPage->pBt; u8 isInitOrig = pPage->isInit; Pgno pgno = pPage->pgno; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - rc = sqlite3BtreeInitPage(pPage); + rc = btreeInitPage(pPage); if( rc!=SQLITE_OK ){ goto set_child_ptrmaps_out; } nCell = pPage->nCell; @@ -2350,14 +2551,13 @@ pPage->isInit = isInitOrig; return rc; } /* -** Somewhere on pPage, which is guaranteed to be a btree page, not an overflow -** page, is a pointer to page iFrom. Modify this pointer so that it points to -** iTo. Parameter eType describes the type of pointer to be modified, as -** follows: +** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so +** that it points to iTo. Parameter eType describes the type of pointer to +** be modified, as follows: ** ** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child ** page of pPage. ** ** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow @@ -2378,18 +2578,18 @@ }else{ u8 isInitOrig = pPage->isInit; int i; int nCell; - sqlite3BtreeInitPage(pPage); + btreeInitPage(pPage); nCell = pPage->nCell; for(i=0; ipDbPage); if( rc!=SQLITE_OK ){ @@ -2553,11 +2753,11 @@ } } else { Pgno iFreePg; /* Index of free page to move pLastPg to */ MemPage *pLastPg; - rc = sqlite3BtreeGetPage(pBt, iLastPg, &pLastPg, 0); + rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); if( rc!=SQLITE_OK ){ return rc; } /* If nFin is zero, this loop runs exactly once and page pLastPg @@ -2592,11 +2792,11 @@ 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); + int rc = btreeGetPage(pBt, iLastPg, &pPg, 0); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); @@ -2745,10 +2945,52 @@ rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0); sqlite3BtreeLeave(p); } return rc; } + +/* +** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback() +** at the conclusion of a transaction. +*/ +static void btreeEndTransaction(Btree *p){ + BtShared *pBt = p->pBt; + BtCursor *pCsr; + assert( sqlite3BtreeHoldsMutex(p) ); + + /* Search for a cursor held open by this b-tree connection. If one exists, + ** then the transaction will be downgraded to a read-only transaction + ** instead of actually concluded. A subsequent call to CommitPhaseTwo() + ** or Rollback() will finish the transaction and unlock the database. */ + for(pCsr=pBt->pCursor; pCsr && pCsr->pBtree!=p; pCsr=pCsr->pNext); + assert( pCsr==0 || p->inTrans>TRANS_NONE ); + + btreeClearHasContent(pBt); + if( pCsr ){ + downgradeAllSharedCacheTableLocks(p); + p->inTrans = TRANS_READ; + }else{ + /* If the handle had any kind of transaction open, decrement the + ** transaction count of the shared btree. If the transaction count + ** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused() + ** call below will unlock the pager. */ + if( p->inTrans!=TRANS_NONE ){ + clearAllSharedCacheTableLocks(p); + pBt->nTransaction--; + if( 0==pBt->nTransaction ){ + pBt->inTransaction = TRANS_NONE; + } + } + + /* Set the current transaction state to TRANS_NONE and unlock the + ** pager if this call closed the only read or write transaction. */ + p->inTrans = TRANS_NONE; + unlockBtreeIfUnused(pBt); + } + + btreeIntegrity(p); +} /* ** Commit the transaction currently in progress. ** ** This routine implements the second phase of a 2-phase commit. The @@ -2782,31 +3024,11 @@ return rc; } pBt->inTransaction = TRANS_READ; } - /* If the handle has any kind of transaction open, decrement the transaction - ** count of the shared btree. If the transaction count reaches 0, set - ** the shared state to TRANS_NONE. The unlockBtreeIfUnused() call below - ** will unlock the pager. - */ - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - /* Set the current transaction state to TRANS_NONE and unlock - ** the pager if this call closed the only read or write transaction. - */ - btreeClearHasContent(pBt); - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - - btreeIntegrity(p); + btreeEndTransaction(p); sqlite3BtreeLeave(p); return SQLITE_OK; } /* @@ -2915,33 +3137,20 @@ if( rc2!=SQLITE_OK ){ rc = rc2; } /* The rollback may have destroyed the pPage1->aData value. So - ** call sqlite3BtreeGetPage() on page 1 again to make + ** call btreeGetPage() on page 1 again to make ** sure pPage1->aData is set correctly. */ - if( sqlite3BtreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ + if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); pBt->inTransaction = TRANS_READ; } - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - assert( pBt->nTransaction>0 ); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - btreeClearHasContent(pBt); - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - - btreeIntegrity(p); + btreeEndTransaction(p); sqlite3BtreeLeave(p); return rc; } /* @@ -3013,12 +3222,14 @@ return rc; } /* ** Create a new cursor for the BTree whose root is on the page -** iTable. The act of acquiring a cursor gets a read lock on -** the database file. +** iTable. If a read-only cursor is requested, it is assumed that +** the caller already has at least a read-only transaction open +** on the database already. If a write-cursor is requested, then +** the caller is assumed to have an open write transaction. ** ** If wrFlag==0, then the cursor can only be used for reading. ** If wrFlag==1, then the cursor can be used for reading or for ** writing if other conditions for writing are also met. These ** are the conditions that must be met in order for writing to @@ -3048,52 +3259,38 @@ int iTable, /* Root page of table to open */ int wrFlag, /* 1 to write. 0 read-only */ struct KeyInfo *pKeyInfo, /* First arg to comparison function */ BtCursor *pCur /* Space for new cursor */ ){ - int rc; - Pgno nPage; - BtShared *pBt = p->pBt; + BtShared *pBt = p->pBt; /* Shared b-tree handle */ assert( sqlite3BtreeHoldsMutex(p) ); assert( wrFlag==0 || wrFlag==1 ); - if( wrFlag ){ - assert( !pBt->readOnly ); - if( NEVER(pBt->readOnly) ){ - return SQLITE_READONLY; - } - rc = checkForReadConflicts(p, iTable, 0, 0); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; - } - } - - if( pBt->pPage1==0 ){ - rc = lockBtreeWithRetry(p); - if( rc!=SQLITE_OK ){ - return rc; - } - } - pCur->pgnoRoot = (Pgno)iTable; - rc = sqlite3PagerPagecount(pBt->pPager, (int *)&nPage); - if( rc!=SQLITE_OK ){ - return rc; - } - if( iTable==1 && nPage==0 ){ - rc = SQLITE_EMPTY; - goto create_cursor_exception; - } - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); - if( rc!=SQLITE_OK ){ - goto create_cursor_exception; + + /* The following assert statements verify that if this is a sharable + ** b-tree database, the connection is holding the required table locks, + ** and that no other connection has any open cursor that conflicts with + ** this lock. */ + assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) ); + assert( wrFlag==0 || !hasReadConflicts(p, iTable) ); + + /* Assert that the caller has opened the required transaction. */ + assert( p->inTrans>TRANS_NONE ); + assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); + assert( pBt->pPage1 && pBt->pPage1->aData ); + + if( NEVER(wrFlag && pBt->readOnly) ){ + return SQLITE_READONLY; + } + if( iTable==1 && pagerPagecount(pBt)==0 ){ + return SQLITE_EMPTY; } /* Now that no other errors can occur, finish filling in the BtCursor - ** variables, link the cursor into the BtShared list and set *ppCur (the - ** output argument to this function). - */ + ** variables and link the cursor into the BtShared list. */ + pCur->pgnoRoot = (Pgno)iTable; + pCur->iPage = -1; pCur->pKeyInfo = pKeyInfo; pCur->pBtree = p; pCur->pBt = pBt; pCur->wrFlag = (u8)wrFlag; pCur->pNext = pBt->pCursor; @@ -3101,17 +3298,11 @@ pCur->pNext->pPrev = pCur; } pBt->pCursor = pCur; pCur->eState = CURSOR_INVALID; pCur->cachedRowid = 0; - return SQLITE_OK; - -create_cursor_exception: - releasePage(pCur->apPage[0]); - unlockBtreeIfUnused(pBt); - return rc; } int sqlite3BtreeCursor( Btree *p, /* The btree */ int iTable, /* Root page of table to open */ int wrFlag, /* 1 to write. 0 read-only */ @@ -3195,50 +3386,17 @@ sqlite3BtreeLeave(pBtree); } return SQLITE_OK; } -#ifdef SQLITE_TEST -/* -** Make a temporary cursor by filling in the fields of pTempCur. -** The temporary cursor is not on the cursor list for the Btree. -*/ -void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){ - int i; - assert( cursorHoldsMutex(pCur) ); - memcpy(pTempCur, pCur, sizeof(BtCursor)); - pTempCur->pNext = 0; - pTempCur->pPrev = 0; - for(i=0; i<=pTempCur->iPage; i++){ - sqlite3PagerRef(pTempCur->apPage[i]->pDbPage); - } - assert( pTempCur->pKey==0 ); -} -#endif /* SQLITE_TEST */ - -#ifdef SQLITE_TEST -/* -** Delete a temporary cursor such as was made by the CreateTemporaryCursor() -** function above. -*/ -void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){ - int i; - assert( cursorHoldsMutex(pCur) ); - for(i=0; i<=pCur->iPage; i++){ - sqlite3PagerUnref(pCur->apPage[i]->pDbPage); - } - sqlite3_free(pCur->pKey); -} -#endif /* SQLITE_TEST */ - /* ** Make sure the BtCursor* given in the argument has a valid ** BtCursor.info structure. If it is not already valid, call -** sqlite3BtreeParseCell() to fill it in. +** btreeParseCell() to fill it in. ** ** BtCursor.info is a cache of the information in the current cell. -** Using this cache reduces the number of calls to sqlite3BtreeParseCell(). +** Using this cache reduces the number of calls to btreeParseCell(). ** ** 2007-06-25: There is a bug in some versions of MSVC that cause the ** compiler to crash when getCellInfo() is implemented as a macro. ** But there is a measureable speed advantage to using the macro on gcc ** (when less compiler optimizations like -Os or -O0 are used and the @@ -3248,11 +3406,11 @@ #ifndef NDEBUG static void assertCellInfo(BtCursor *pCur){ CellInfo info; int iPage = pCur->iPage; memset(&info, 0, sizeof(info)); - sqlite3BtreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); + btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); } #else #define assertCellInfo(x) #endif @@ -3259,11 +3417,11 @@ #ifdef _MSC_VER /* Use a real function in MSVC to work around bugs in that compiler. */ static void getCellInfo(BtCursor *pCur){ if( pCur->info.nSize==0 ){ int iPage = pCur->iPage; - sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); + btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); pCur->validNKey = 1; }else{ assertCellInfo(pCur); } } @@ -3270,11 +3428,11 @@ #else /* if not _MSC_VER */ /* Use a macro in all other compilers so that the function is inlined */ #define getCellInfo(pCur) \ if( pCur->info.nSize==0 ){ \ int iPage = pCur->iPage; \ - sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ + btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ pCur->validNKey = 1; \ }else{ \ assertCellInfo(pCur); \ } #endif /* _MSC_VER */ @@ -3347,12 +3505,12 @@ ** on *ppPage to free the reference. In no reference was obtained (because ** the pointer-map was used to obtain the value for *pPgnoNext), then ** *ppPage is set to zero. */ static int getOverflowPage( - BtShared *pBt, - Pgno ovfl, /* Overflow page */ + BtShared *pBt, /* The database file */ + Pgno ovfl, /* Current overflow page number */ MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ Pgno *pPgnoNext /* OUT: Next overflow page number */ ){ Pgno next = 0; MemPage *pPage = 0; @@ -3386,11 +3544,11 @@ } } #endif if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetPage(pBt, ovfl, &pPage, 0); + rc = btreeGetPage(pBt, ovfl, &pPage, 0); assert(rc==SQLITE_OK || pPage==0); if( next==0 && rc==SQLITE_OK ){ next = get4byte(pPage->aData); } } @@ -3799,11 +3957,11 @@ ** pCur->idx is set to the cell index that contains the pointer ** to the page we are coming from. If we are coming from the ** right-most child page then pCur->idx is set to one more than ** the largest cell index. */ -void sqlite3BtreeMoveToParent(BtCursor *pCur){ +static void moveToParent(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); assert( pCur->apPage[pCur->iPage] ); assertParentIndex( @@ -3840,22 +3998,32 @@ if( pCur->iPage>=0 ){ int i; for(i=1; i<=pCur->iPage; i++){ releasePage(pCur->apPage[i]); } + pCur->iPage = 0; }else{ if( SQLITE_OK!=(rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0])) ){ pCur->eState = CURSOR_INVALID; return rc; } + pCur->iPage = 0; + + /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor + ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is + ** NULL, the caller expects a table b-tree. If this is not the case, + ** return an SQLITE_CORRUPT error. */ + assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 ); + if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){ + return SQLITE_CORRUPT_BKPT; + } } pRoot = pCur->apPage[0]; assert( pRoot->pgno==pCur->pgnoRoot ); - pCur->iPage = 0; pCur->aiIdx[0] = 0; pCur->info.nSize = 0; pCur->atLast = 0; pCur->validNKey = 0; @@ -4026,10 +4194,11 @@ ){ int rc; assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); /* If the cursor is already positioned at the point we are trying ** to move to, then just return without doing any work */ if( pCur->eState==CURSOR_VALID && pCur->validNKey && pCur->apPage[0]->intKey @@ -4121,11 +4290,11 @@ ** this case the whole cell needs to be parsed, a buffer allocated ** and accessPayload() used to retrieve the record into the ** buffer before VdbeRecordCompare() can be called. */ void *pCellKey; u8 * const pCellBody = pCell - pPage->childPtrSize; - sqlite3BtreeParseCellPtr(pPage, pCellBody, &pCur->info); + btreeParseCellPtr(pPage, pCellBody, &pCur->info); nCell = (int)pCur->info.nKey; pCellKey = sqlite3Malloc( nCell ); if( pCellKey==0 ){ rc = SQLITE_NOMEM; goto moveto_finish; @@ -4166,11 +4335,11 @@ }else{ chldPg = get4byte(findCell(pPage, lwr)); } if( chldPg==0 ){ assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - if( pRes ) *pRes = c; + *pRes = c; rc = SQLITE_OK; goto moveto_finish; } pCur->aiIdx[pCur->iPage] = (u16)lwr; pCur->info.nSize = 0; @@ -4177,42 +4346,10 @@ pCur->validNKey = 0; rc = moveToChild(pCur, chldPg); if( rc ) goto moveto_finish; } moveto_finish: - return rc; -} - -/* -** In this version of BtreeMoveto, pKey is a packed index record -** such as is generated by the OP_MakeRecord opcode. Unpack the -** record and then call BtreeMovetoUnpacked() to do the work. -*/ -int sqlite3BtreeMoveto( - BtCursor *pCur, /* Cursor open on the btree to be searched */ - const void *pKey, /* Packed key if the btree is an index */ - i64 nKey, /* Integer key for tables. Size of pKey for indices */ - int bias, /* Bias search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; /* Status code */ - UnpackedRecord *pIdxKey; /* Unpacked index key */ - char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ - - - if( pKey ){ - assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, - aSpace, sizeof(aSpace)); - if( pIdxKey==0 ) return SQLITE_NOMEM; - }else{ - pIdxKey = 0; - } - rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); - if( pKey ){ - sqlite3VdbeDeleteUnpackedRecord(pIdxKey); - } return rc; } /* @@ -4277,11 +4414,11 @@ if( pCur->iPage==0 ){ *pRes = 1; pCur->eState = CURSOR_INVALID; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); pPage = pCur->apPage[pCur->iPage]; }while( pCur->aiIdx[pCur->iPage]>=pPage->nCell ); *pRes = 0; if( pPage->intKey ){ rc = sqlite3BtreeNext(pCur, pRes); @@ -4340,11 +4477,11 @@ if( pCur->iPage==0 ){ pCur->eState = CURSOR_INVALID; *pRes = 1; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); } pCur->info.nSize = 0; pCur->validNKey = 0; pCur->aiIdx[pCur->iPage]--; @@ -4397,11 +4534,12 @@ assert( sqlite3_mutex_held(pBt->mutex) ); pPage1 = pBt->pPage1; mxPage = pagerPagecount(pBt); n = get4byte(&pPage1->aData[36]); - if( n>mxPage ){ + testcase( n==mxPage-1 ); + if( n>=mxPage ){ return SQLITE_CORRUPT_BKPT; } if( n>0 ){ /* There are pages on the freelist. Reuse one of those pages. */ Pgno iTrunk; @@ -4441,21 +4579,23 @@ if( pPrevTrunk ){ iTrunk = get4byte(&pPrevTrunk->aData[0]); }else{ iTrunk = get4byte(&pPage1->aData[32]); } + testcase( iTrunk==mxPage ); if( iTrunk>mxPage ){ rc = SQLITE_CORRUPT_BKPT; }else{ - rc = sqlite3BtreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); } if( rc ){ pTrunk = 0; goto end_allocate_page; } k = get4byte(&pTrunk->aData[4]); + testcase( k==(u32)(pBt->usableSize/4 - 2) ); if( k==0 && !searchList ){ /* The trunk has no leaves and the list is not being searched. ** So extract the trunk page itself and use it as the newly ** allocated page */ assert( pPrevTrunk==0 ); @@ -4499,11 +4639,12 @@ Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); if( iNewTrunk>mxPage ){ rc = SQLITE_CORRUPT_BKPT; goto end_allocate_page; } - rc = sqlite3BtreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); + testcase( iNewTrunk==mxPage ); + rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); if( rc!=SQLITE_OK ){ goto end_allocate_page; } rc = sqlite3PagerWrite(pNewTrunk->pDbPage); if( rc!=SQLITE_OK ){ @@ -4554,34 +4695,29 @@ }else{ closest = 0; } iPage = get4byte(&aData[8+closest*4]); + testcase( iPage==mxPage ); if( iPage>mxPage ){ rc = SQLITE_CORRUPT_BKPT; goto end_allocate_page; } + testcase( iPage==mxPage ); if( !searchList || iPage==nearby ){ int noContent; - Pgno nPage; *pPgno = iPage; - nPage = pagerPagecount(pBt); - if( iPage>nPage ){ - /* Free page off the end of the file */ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; - } TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" ": %d more free pages\n", *pPgno, closest+1, k, pTrunk->pgno, n-1)); if( closestpDbPage) ); noContent = !btreeGetHasContent(pBt, *pPgno); - rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, noContent); + rc = btreeGetPage(pBt, *pPgno, ppPage, noContent); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); } @@ -4609,11 +4745,11 @@ ** 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); + rc = btreeGetPage(pBt, *pPgno, &pPg, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); } if( rc ) return rc; @@ -4621,11 +4757,11 @@ if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ (*pPgno)++; } } #endif assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, 0); + rc = btreeGetPage(pBt, *pPgno, ppPage, 0); if( rc ) return rc; rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); } @@ -4688,11 +4824,11 @@ #ifdef SQLITE_SECURE_DELETE /* If the SQLITE_SECURE_DELETE compile-time option is enabled, then ** always fully overwrite deleted information with zeros. */ - if( (!pPage && (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0))) + if( (!pPage && (rc = btreeGetPage(pBt, iPage, &pPage, 0))) || (rc = sqlite3PagerWrite(pPage->pDbPage)) ){ goto freepage_out; } memset(pPage->aData, 0, pPage->pBt->pageSize); @@ -4715,11 +4851,11 @@ */ if( nFree!=0 ){ int nLeaf; /* Initial number of leaf cells on trunk page */ iTrunk = get4byte(&pPage1->aData[32]); - rc = sqlite3BtreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); if( rc!=SQLITE_OK ){ goto freepage_out; } nLeaf = get4byte(&pTrunk->aData[4]); @@ -4762,11 +4898,11 @@ ** the page being freed as a leaf page of the first trunk in the free-list. ** Possibly because the free-list is empty, or possibly because the ** first trunk in the free-list is full. Either way, the page being freed ** will become the new first trunk page in the free-list. */ - if( ((!pPage) && (0 != (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0)))) + if( ((!pPage) && (0 != (rc = btreeGetPage(pBt, iPage, &pPage, 0)))) || (0 != (rc = sqlite3PagerWrite(pPage->pDbPage))) ){ goto freepage_out; } put4byte(pPage->aData, iTrunk); @@ -4796,11 +4932,11 @@ int rc; int nOvfl; u16 ovflPageSize; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); if( info.iOverflow==0 ){ return SQLITE_OK; /* No overflow pages. Return without doing anything */ } ovflPgno = get4byte(&pCell[info.iOverflow]); assert( pBt->usableSize > 4 ); @@ -4879,11 +5015,11 @@ nHeader += putVarint(&pCell[nHeader], nData+nZero); }else{ nData = nZero = 0; } nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); assert( info.nHeader==nHeader ); assert( info.nKey==nKey ); assert( info.nData==(u32)(nData+nZero) ); /* Fill in the payload */ @@ -5057,14 +5193,12 @@ u8 *pTemp, /* Temp storage space for pCell, if needed */ Pgno iChild /* If non-zero, replace first 4 bytes with this value */ ){ int idx; /* Where to write new cell content in data[] */ int j; /* Loop counter */ - int top; /* First byte of content for any cell in data[] */ int end; /* First byte past the last cell pointer in data[] */ int ins; /* Index in data[] where new cell pointer is inserted */ - int hdr; /* Offset into data[] of the page header */ int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); @@ -5091,47 +5225,37 @@ if( rc!=SQLITE_OK ){ return rc; } assert( sqlite3PagerIswriteable(pPage->pDbPage) ); data = pPage->aData; - hdr = pPage->hdrOffset; - top = get2byte(&data[hdr+5]); cellOffset = pPage->cellOffset; - end = cellOffset + 2*pPage->nCell + 2; + end = cellOffset + 2*pPage->nCell; ins = cellOffset + 2*i; - if( end > top - sz ){ - rc = defragmentPage(pPage); - if( rc!=SQLITE_OK ){ - return rc; - } - top = get2byte(&data[hdr+5]); - assert( end + sz <= top ); - } - idx = allocateSpace(pPage, sz); - assert( idx>0 ); - assert( end <= get2byte(&data[hdr+5]) ); - if (idx+sz > pPage->pBt->usableSize) { + rc = allocateSpace(pPage, sz, &idx); + if( rc ) return rc; + assert( idx>=end+2 ); + if( idx+sz > pPage->pBt->usableSize ){ return SQLITE_CORRUPT_BKPT; } pPage->nCell++; - pPage->nFree -= 2; + pPage->nFree -= (u16)(2 + sz); memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); if( iChild ){ put4byte(&data[idx], iChild); } - for(j=end-2, ptr=&data[j]; j>ins; j-=2, ptr-=2){ + for(j=end, ptr=&data[j]; j>ins; j-=2, ptr-=2){ ptr[0] = ptr[-2]; ptr[1] = ptr[-1]; } put2byte(&data[ins], idx); - put2byte(&data[hdr+3], pPage->nCell); + put2byte(&data[pPage->hdrOffset+3], pPage->nCell); #ifndef SQLITE_OMIT_AUTOVACUUM if( pPage->pBt->autoVacuum ){ /* The cell may contain a pointer to an overflow page. If so, write ** the entry for the overflow page into the pointer map. */ - rc = ptrmapPutOvflPtr(pPage, pCell); + return ptrmapPutOvflPtr(pPage, pCell); } #endif } return SQLITE_OK; @@ -5314,11 +5438,11 @@ for(j=0; jnCell; j++){ CellInfo info; u8 *z; z = findCell(pPage, j); - sqlite3BtreeParseCellPtr(pPage, z, &info); + btreeParseCellPtr(pPage, z, &info); if( info.iOverflow ){ Pgno ovfl = get4byte(&z[info.iOverflow]); ptrmapGet(pBt, ovfl, &e, &n); assert( n==pPage->pgno && e==PTRMAP_OVERFLOW1 ); } @@ -5336,10 +5460,59 @@ } return 1; } #endif +/* +** This function is used to copy the contents of the b-tree node stored +** on page pFrom to page pTo. If page pFrom was not a leaf page, then +** the pointer-map entries for each child page are updated so that the +** parent page stored in the pointer map is page pTo. If pFrom contained +** any cells with overflow page pointers, then the corresponding pointer +** map entries are also updated so that the parent page is page pTo. +** +** If pFrom is currently carrying any overflow cells (entries in the +** MemPage.aOvfl[] array), they are not copied to pTo. +** +** Before returning, page pTo is reinitialized using btreeInitPage(). +** +** The performance of this function is not critical. It is only used by +** the balance_shallower() and balance_deeper() procedures, neither of +** which are called often under normal circumstances. +*/ +static int copyNodeContent(MemPage *pFrom, MemPage *pTo){ + BtShared * const pBt = pFrom->pBt; + u8 * const aFrom = pFrom->aData; + u8 * const aTo = pTo->aData; + int const iFromHdr = pFrom->hdrOffset; + int const iToHdr = ((pTo->pgno==1) ? 100 : 0); + int rc = SQLITE_OK; + int iData; + + assert( pFrom->isInit ); + assert( pFrom->nFree>=iToHdr ); + assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); + + /* Copy the b-tree node content from page pFrom to page pTo. */ + iData = get2byte(&aFrom[iFromHdr+5]); + memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); + memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); + + /* Reinitialize page pTo so that the contents of the MemPage structure + ** match the new data. The initialization of pTo "cannot" fail, as the + ** data copied from pFrom is known to be valid. */ + pTo->isInit = 0; + TESTONLY(rc = ) btreeInitPage(pTo); + assert( rc==SQLITE_OK ); + + /* If this is an auto-vacuum database, update the pointer-map entries + ** for any b-tree or overflow pages that pTo now contains the pointers to. */ + if( ISAUTOVACUUM ){ + rc = setChildPtrmaps(pTo); + } + return rc; +} /* ** This routine redistributes cells on the iParentIdx'th child of pParent ** (hereafter "the page") and up to 2 siblings so that all pages have about the ** same amount of free space. Usually a single sibling on either side of the @@ -5380,21 +5553,22 @@ ** SQLITE_NOMEM. */ static int balance_nonroot( MemPage *pParent, /* Parent page of siblings being balanced */ int iParentIdx, /* Index of "the page" in pParent */ - u8 *aOvflSpace /* page-size bytes of space for parent ovfl */ + u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */ + int isRoot /* True if pParent is a root-page */ ){ BtShared *pBt; /* The whole database */ int nCell = 0; /* Number of cells in apCell[] */ int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */ int nNew = 0; /* Number of pages in apNew[] */ int nOld; /* Number of pages in apOld[] */ int i, j, k; /* Loop counters */ int nxDiv; /* Next divider slot in pParent->aCell[] */ int rc = SQLITE_OK; /* The return code */ - int leafCorrection; /* 4 if pPage is a leaf. 0 if not */ + u16 leafCorrection; /* 4 if pPage is a leaf. 0 if not */ int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ int usableSpace; /* Bytes in pPage beyond the header */ int pageFlags; /* Value of pPage->aData[0] */ int subtotal; /* Subtotal of bytes in cells on one page */ int iSpace1 = 0; /* First unused byte of aSpace1[] */ @@ -5559,11 +5733,11 @@ apCell[nCell] = findOverflowCell(pOld, j); szCell[nCell] = cellSizePtr(pOld, apCell[nCell]); nCell++; } if( ipageSize/4 ); assert( iSpace1<=pBt->pageSize ); memcpy(pTemp, apDiv[i], sz); apCell[nCell] = pTemp+leafCorrection; assert( leafCorrection==0 || leafCorrection==4 ); - szCell[nCell] -= (u16)leafCorrection; + szCell[nCell] = szCell[nCell] - leafCorrection; if( !pOld->leaf ){ assert( leafCorrection==0 ); assert( pOld->hdrOffset==0 ); /* The right pointer of the child page pOld becomes the left ** pointer of the divider cell */ @@ -5768,11 +5942,12 @@ j = cntNew[i]; /* If the sibling page assembled above was not the right-most sibling, ** insert a divider cell into the parent page. */ - if( iaData[8]; memcpy(&apNew[nNew-1]->aData[8], zChild, 4); } - /* Fix the pointer-map entries for all the cells that were shifted around. - ** There are several different types of pointer-map entries that need to - ** be dealt with by this routine. Some of these have been set already, but - ** many have not. The following is a summary: - ** - ** 1) The entries associated with new sibling pages that were not - ** siblings when this function was called. These have already - ** been set. We don't need to worry about old siblings that were - ** moved to the free-list - the freePage() code has taken care - ** of those. - ** - ** 2) The pointer-map entries associated with the first overflow - ** page in any overflow chains used by new divider cells. These - ** have also already been taken care of by the insertCell() code. - ** - ** 3) If the sibling pages are not leaves, then the child pages of - ** cells stored on the sibling pages may need to be updated. - ** - ** 4) If the sibling pages are not internal intkey nodes, then any - ** overflow pages used by these cells may need to be updated - ** (internal intkey nodes never contain pointers to overflow pages). - ** - ** 5) If the sibling pages are not leaves, then the pointer-map - ** entries for the right-child pages of each sibling may need - ** to be updated. - ** - ** Cases 1 and 2 are dealt with above by other code. The following - ** block deals with cases 3 and 4. Since setting a pointer map entry - ** is a relatively expensive operation, this code only sets pointer - ** map entries for child or overflow pages that have actually moved - ** between pages. */ - if( ISAUTOVACUUM ){ + if( isRoot && pParent->nCell==0 && pParent->hdrOffset<=apNew[0]->nFree ){ + /* The root page of the b-tree now contains no cells. The only sibling + ** page is the right-child of the parent. Copy the contents of the + ** child page into the parent, decreasing the overall height of the + ** b-tree structure by one. This is described as the "balance-shallower" + ** sub-algorithm in some documentation. + ** + ** If this is an auto-vacuum database, the call to copyNodeContent() + ** sets all pointer-map entries corresponding to database image pages + ** for which the pointer is stored within the content being copied. + ** + ** The second assert below verifies that the child page is defragmented + ** (it must be, as it was just reconstructed using assemblePage()). This + ** is important if the parent page happens to be page 1 of the database + ** image. */ + assert( nNew==1 ); + assert( apNew[0]->nFree == + (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) + ); + if( SQLITE_OK==(rc = copyNodeContent(apNew[0], pParent)) ){ + rc = freePage(apNew[0]); + } + }else if( ISAUTOVACUUM ){ + /* Fix the pointer-map entries for all the cells that were shifted around. + ** There are several different types of pointer-map entries that need to + ** be dealt with by this routine. Some of these have been set already, but + ** many have not. The following is a summary: + ** + ** 1) The entries associated with new sibling pages that were not + ** siblings when this function was called. These have already + ** been set. We don't need to worry about old siblings that were + ** moved to the free-list - the freePage() code has taken care + ** of those. + ** + ** 2) The pointer-map entries associated with the first overflow + ** page in any overflow chains used by new divider cells. These + ** have also already been taken care of by the insertCell() code. + ** + ** 3) If the sibling pages are not leaves, then the child pages of + ** cells stored on the sibling pages may need to be updated. + ** + ** 4) If the sibling pages are not internal intkey nodes, then any + ** overflow pages used by these cells may need to be updated + ** (internal intkey nodes never contain pointers to overflow pages). + ** + ** 5) If the sibling pages are not leaves, then the pointer-map + ** entries for the right-child pages of each sibling may need + ** to be updated. + ** + ** Cases 1 and 2 are dealt with above by other code. The next + ** block deals with cases 3 and 4 and the one after that, case 5. Since + ** setting a pointer map entry is a relatively expensive operation, this + ** code only sets pointer map entries for child or overflow pages that have + ** actually moved between pages. */ MemPage *pNew = apNew[0]; MemPage *pOld = apCopy[0]; int nOverflow = pOld->nOverflow; int iNextOld = pOld->nCell + nOverflow; int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1); @@ -5937,11 +6134,11 @@ } assert( pParent->isInit ); TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", nOld, nNew, nCell)); - + /* ** Cleanup before returning. */ balance_cleanup: sqlite3ScratchFree(apCell); @@ -5950,113 +6147,10 @@ } for(i=0; ipBt; - u8 * const aFrom = pFrom->aData; - u8 * const aTo = pTo->aData; - int const iFromHdr = pFrom->hdrOffset; - int const iToHdr = ((pTo->pgno==1) ? 100 : 0); - int rc = SQLITE_OK; - int iData; - - assert( pFrom->isInit ); - assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); - - /* Copy the b-tree node content from page pFrom to page pTo. */ - iData = get2byte(&aFrom[iFromHdr+5]); - memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); - memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); - - /* Reinitialize page pTo so that the contents of the MemPage structure - ** match the new data. The initialization of pTo "cannot" fail, as the - ** data copied from pFrom is known to be valid. */ - pTo->isInit = 0; - TESTONLY(rc = ) sqlite3BtreeInitPage(pTo); - assert( rc==SQLITE_OK ); - - /* If this is an auto-vacuum database, update the pointer-map entries - ** for any b-tree or overflow pages that pTo now contains the pointers to. */ - if( ISAUTOVACUUM ){ - rc = setChildPtrmaps(pTo); - } - return rc; -} - -/* -** This routine is called on the root page of a btree when the root -** page contains no cells. This is an opportunity to make the tree -** shallower by one level. -*/ -static int balance_shallower(MemPage *pRoot){ - /* The root page is empty but has one child. Transfer the - ** information from that one child into the root page if it - ** will fit. This reduces the depth of the tree by one. - ** - ** If the root page is page 1, it has less space available than - ** its child (due to the 100 byte header that occurs at the beginning - ** of the database fle), so it might not be able to hold all of the - ** information currently contained in the child. If this is the - ** case, then do not do the transfer. Leave page 1 empty except - ** for the right-pointer to the child page. The child page becomes - ** the virtual root of the tree. - */ - int rc = SQLITE_OK; /* Return code */ - int const hdr = pRoot->hdrOffset; /* Offset of root page header */ - MemPage *pChild; /* Only child of pRoot */ - Pgno const pgnoChild = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); - - assert( pRoot->nCell==0 ); - assert( sqlite3_mutex_held(pRoot->pBt->mutex) ); - assert( !pRoot->leaf ); - assert( pgnoChild>0 ); - assert( pgnoChild<=pagerPagecount(pRoot->pBt) ); - assert( hdr==0 || pRoot->pgno==1 ); - - rc = sqlite3BtreeGetPage(pRoot->pBt, pgnoChild, &pChild, 0); - if( rc==SQLITE_OK ){ - if( pChild->nFree>=hdr ){ - if( hdr ){ - rc = defragmentPage(pChild); - } - if( rc==SQLITE_OK ){ - rc = copyNodeContent(pChild, pRoot); - } - if( rc==SQLITE_OK ){ - rc = freePage(pChild); - } - }else{ - /* The child has more information that will fit on the root. - ** The tree is already balanced. Do nothing. */ - TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno)); - } - releasePage(pChild); - } - return rc; } /* @@ -6124,18 +6218,12 @@ ** some way. This function figures out if this modification means the ** tree needs to be balanced, and if so calls the appropriate balancing ** routine. Balancing routines are: ** ** balance_quick() -** balance_shallower() ** balance_deeper() ** balance_nonroot() -** -** If built with SQLITE_DEBUG, pCur->pagesShuffled is set to true if -** balance_shallower(), balance_deeper() or balance_nonroot() is called. -** If none of these functions are invoked, pCur->pagesShuffled is left -** unmodified. */ static int balance(BtCursor *pCur){ int rc = SQLITE_OK; const int nMin = pCur->pBt->usableSize * 2 / 3; u8 aBalanceQuickSpace[13]; @@ -6161,25 +6249,11 @@ pCur->iPage = 1; pCur->aiIdx[0] = 0; pCur->aiIdx[1] = 0; assert( pCur->apPage[1]->nOverflow ); } - VVA_ONLY( pCur->pagesShuffled = 1 ); }else{ - /* The root page of the b-tree is now empty. If the root-page is not - ** also a leaf page, it will have a single child page. Call - ** balance_shallower to attempt to copy the contents of the single - ** child-page into the root page (this may not be possible if the - ** root page is page 1). - ** - ** Whether or not this is possible , the tree is now balanced. - ** Therefore is no next iteration of the do-loop. - */ - if( pPage->nCell==0 && !pPage->leaf ){ - rc = balance_shallower(pPage); - VVA_ONLY( pCur->pagesShuffled = 1 ); - } break; } }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ break; }else{ @@ -6229,11 +6303,11 @@ ** the previous call, as the overflow cell data will have been ** copied either into the body of a database page or into the new ** pSpace buffer passed to the latter call to balance_nonroot(). */ u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize); - rc = balance_nonroot(pParent, iIdx, pSpace); + rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1); if( pFree ){ /* If pFree is not NULL, it points to the pSpace buffer used ** by a previous call to balance_nonroot(). Its contents are ** now stored either on real database pages or within the ** new pSpace buffer, so it may be safely freed here. */ @@ -6242,11 +6316,10 @@ /* The pSpace buffer will be freed after the next call to ** balance_nonroot(), or just before this function returns, whichever ** comes first. */ pFree = pSpace; - VVA_ONLY( pCur->pagesShuffled = 1 ); } } pPage->nOverflow = 0; @@ -6260,79 +6333,10 @@ sqlite3PageFree(pFree); } return rc; } -/* -** This routine checks all cursors that point to table pgnoRoot. -** If any of those cursors were opened with wrFlag==0 in a different -** database connection (a database connection that shares the pager -** cache with the current connection) and that other connection -** is not in the ReadUncommmitted state, then this routine returns -** SQLITE_LOCKED. -** -** 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 -** on which they are opened is deleted or modified. Cursors are invalidated -** according to the following rules: -** -** 1) When BtreeClearTable() is called to completely delete the contents -** of a B-Tree table, pExclude is set to zero and parameter iRow is -** set to non-zero. In this case all incremental blob cursors open -** on the table rooted at pgnoRoot are invalidated. -** -** 2) When BtreeInsert(), BtreeDelete() or BtreePutData() is called to -** modify a table row via an SQL statement, pExclude is set to the -** write cursor used to do the modification and parameter iRow is set -** to the integer row id of the B-Tree entry being modified. Unless -** pExclude is itself an incremental blob cursor, then all incremental -** blob cursors open on row iRow of the B-Tree are invalidated. -** -** 3) If both pExclude and iRow are set to zero, no incremental blob -** cursors are invalidated. -*/ -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; - sqlite3 *db = pBtree->db; - assert( sqlite3BtreeHoldsMutex(pBtree) ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p==pExclude ) continue; - if( p->pgnoRoot!=pgnoRoot ) continue; -#ifndef SQLITE_OMIT_INCRBLOB - if( p->isIncrblobHandle && ( - (!pExclude && iRow) - || (pExclude && !pExclude->isIncrblobHandle && p->info.nKey==iRow) - )){ - p->eState = CURSOR_INVALID; - } -#endif - if( p->eState!=CURSOR_VALID ) continue; - if( p->wrFlag==0 -#ifndef SQLITE_OMIT_INCRBLOB - || p->isIncrblobHandle -#endif - ){ - sqlite3 *dbOther = p->pBtree->db; - assert(dbOther); - if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){ - sqlite3ConnectionBlocked(db, dbOther); - return SQLITE_LOCKED_SHAREDCACHE; - } - } - } - return SQLITE_OK; -} /* ** Insert a new record into the BTree. The key is given by (pKey,nKey) ** and the data is given by (pData,nData). The cursor is used only to ** define what table the record should be inserted into. The cursor @@ -6340,11 +6344,11 @@ ** ** For an INTKEY table, only the nKey value of the key is used. pKey is ** ignored. For a ZERODATA table, the pData and nData are both ignored. ** ** If the seekResult parameter is non-zero, then a successful call to -** sqlite3BtreeMoveto() to seek cursor pCur to (pKey, nKey) has already +** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already ** been performed. seekResult is the search result returned (a negative ** number if pCur points at an entry that is smaller than (pKey, nKey), or ** a positive value if pCur points at an etry that is larger than ** (pKey, nKey)). ** @@ -6356,11 +6360,11 @@ BtCursor *pCur, /* Insert data into the table of this cursor */ const void *pKey, i64 nKey, /* The key of the new record */ const void *pData, int nData, /* The data of the new record */ int nZero, /* Number of extra 0 bytes to append to data */ int appendBias, /* True if this is likely an append */ - int seekResult /* Result of prior sqlite3BtreeMoveto() call */ + int seekResult /* Result of prior MovetoUnpacked() call */ ){ int rc; int loc = seekResult; int szNew; int idx; @@ -6372,34 +6376,37 @@ assert( cursorHoldsMutex(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); assert( pCur->wrFlag ); - 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; + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + + /* If this is an insert into a table b-tree, invalidate any incrblob + ** cursors open on the row being replaced (assuming this is a replace + ** operation - if it is not, the following is a no-op). */ + if( pCur->pKeyInfo==0 ){ + invalidateIncrblobCursors(p, pCur->pgnoRoot, nKey, 0); } + if( pCur->eState==CURSOR_FAULT ){ return pCur->skip; } /* Save the positions of any other cursors open on this table. ** - ** In some cases, the call to sqlite3BtreeMoveto() below is a no-op. For + ** In some cases, the call to btreeMoveto() below is a no-op. For ** example, when inserting data into a table with auto-generated integer ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the ** integer key to use. It then calls this function to actually insert the - ** data into the intkey B-Tree. In this case sqlite3BtreeMoveto() recognizes + ** data into the intkey B-Tree. In this case btreeMoveto() recognizes ** that the cursor is already where it needs to be and returns without ** doing any work. To avoid thwarting these optimizations, it is important ** not to clear the cursor here. */ if( SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || (!loc && - SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc)) + SQLITE_OK!=(rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc)) )){ return rc; } assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); @@ -6498,20 +6505,23 @@ assert( cursorHoldsMutex(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); assert( pCur->wrFlag ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + assert( !hasReadConflicts(p, pCur->pgnoRoot) ); + if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) || NEVER(pCur->eState!=CURSOR_VALID) ){ return SQLITE_ERROR; /* Something has gone awry. */ } - rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; /* The table pCur points to has a read lock */ + /* If this is a delete operation to remove a row from a table b-tree, + ** invalidate any incrblob cursors open on the row being deleted. */ + if( pCur->pKeyInfo==0 ){ + invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0); } iCellDepth = pCur->iPage; iCellIdx = pCur->aiIdx[iCellDepth]; pPage = pCur->apPage[iCellDepth]; @@ -6638,14 +6648,11 @@ /* Read the value of meta[3] from the database to determine where the ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ - rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); - if( rc!=SQLITE_OK ){ - return rc; - } + sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); pgnoRoot++; /* The new root-page may not be allocated on a pointer-map page, or the ** PENDING_BYTE page. */ @@ -6675,11 +6682,11 @@ Pgno iPtrPage; releasePage(pPageMove); /* Move the page currently at pgnoRoot to pgnoMove. */ - rc = sqlite3BtreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ @@ -6696,11 +6703,11 @@ /* Obtain the page at pgnoRoot */ if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3BtreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pRoot->pDbPage); if( rc!=SQLITE_OK ){ @@ -6807,15 +6814,17 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ int rc; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); assert( p->inTrans==TRANS_WRITE ); - if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){ - /* nothing to do */ - }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ - /* nothing to do */ - }else{ + + /* Invalidate all incrblob cursors open on table iTable (assuming iTable + ** is the root of a table b-tree - if it is not, the following call is + ** a no-op). */ + invalidateIncrblobCursors(p, iTable, 0, 1); + + if( SQLITE_OK==(rc = saveAllCursors(pBt, (Pgno)iTable, 0)) ){ rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); } sqlite3BtreeLeave(p); return rc; } @@ -6857,11 +6866,11 @@ if( pBt->pCursor ){ sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db); return SQLITE_LOCKED_SHAREDCACHE; } - rc = sqlite3BtreeGetPage(pBt, (Pgno)iTable, &pPage, 0); + rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); if( rc ) return rc; rc = sqlite3BtreeClearTable(p, iTable, 0); if( rc ){ releasePage(pPage); return rc; @@ -6874,15 +6883,11 @@ rc = freePage(pPage); releasePage(pPage); #else if( pBt->autoVacuum ){ Pgno maxRootPgno; - rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); - if( rc!=SQLITE_OK ){ - releasePage(pPage); - return rc; - } + sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); if( iTable==maxRootPgno ){ /* If the table being dropped is the table with the largest root-page ** number in the database, put the root page on the free list. */ @@ -6896,20 +6901,20 @@ ** number in the database. So move the page that does into the ** gap left by the deleted root-page. */ MemPage *pMove; releasePage(pPage); - rc = sqlite3BtreeGetPage(pBt, maxRootPgno, &pMove, 0); + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); if( rc!=SQLITE_OK ){ return rc; } rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); releasePage(pMove); if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3BtreeGetPage(pBt, maxRootPgno, &pMove, 0); + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); if( rc!=SQLITE_OK ){ return rc; } rc = freePage(pMove); releasePage(pMove); @@ -6954,84 +6959,40 @@ return rc; } /* +** This function may only be called if the b-tree connection already +** has a read or write transaction open on the database. +** ** Read the meta-information out of a database file. Meta[0] ** is the number of free pages currently in the database. Meta[1] ** through meta[15] are available for use by higher layers. Meta[0] ** is read-only, the others are read/write. ** ** The schema layer numbers meta values differently. At the schema ** layer (and the SetCookie and ReadCookie opcodes) the number of ** free pages is not visible. So Cookie[0] is the same as Meta[1]. */ -int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ - DbPage *pDbPage = 0; - int rc; - unsigned char *pP1; +void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - - /* 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 querySharedCacheTableLock() - ** and setSharedCacheTableLock()). - */ - rc = querySharedCacheTableLock(p, 1, READ_LOCK); - if( rc!=SQLITE_OK ){ - sqlite3BtreeLeave(p); - return rc; - } - + assert( p->inTrans>TRANS_NONE ); + assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); + assert( pBt->pPage1 ); assert( idx>=0 && idx<=15 ); - if( pBt->pPage1 ){ - /* The b-tree is already holding a reference to page 1 of the database - ** file. In this case the required meta-data value can be read directly - ** from the page data of this reference. This is slightly faster than - ** requesting a new reference from the pager layer. - */ - pP1 = (unsigned char *)pBt->pPage1->aData; - }else{ - /* The b-tree does not have a reference to page 1 of the database file. - ** Obtain one from the pager layer. - */ - rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage); - if( rc ){ - sqlite3BtreeLeave(p); - return rc; - } - pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage); - } - *pMeta = get4byte(&pP1[36 + idx*4]); - - /* If the b-tree is not holding a reference to page 1, then one was - ** requested from the pager layer in the above block. Release it now. - */ - if( !pBt->pPage1 ){ - sqlite3PagerUnref(pDbPage); - } - - /* If autovacuumed is disabled in this build but we are trying to - ** access an autovacuumed database, then make the database readonly. - */ + + *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); + + /* If auto-vacuum is disabled in this build and this is an auto-vacuum + ** database, mark the database as read-only. */ #ifdef SQLITE_OMIT_AUTOVACUUM if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1; #endif - /* If there is currently an open transaction, grab a read-lock - ** on page 1 of the database file. This is done to make sure that - ** no other connection can modify the meta value just read from - ** the database until the transaction is concluded. - */ - if( p->inTrans>0 ){ - rc = setSharedCacheTableLock(p, 1, READ_LOCK); - } sqlite3BtreeLeave(p); - return rc; } /* ** Write meta-information back into the database. Meta[0] is ** read-only and may not be written. @@ -7058,27 +7019,10 @@ } sqlite3BtreeLeave(p); return rc; } -/* -** Return the flag byte at the beginning of the page that the cursor -** is currently pointing to. -*/ -int sqlite3BtreeFlags(BtCursor *pCur){ - /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call - ** restoreCursorPosition() here. - */ - MemPage *pPage; - restoreCursorPosition(pCur); - pPage = pCur->apPage[pCur->iPage]; - assert( cursorHoldsMutex(pCur) ); - assert( pPage!=0 ); - assert( pPage->pBt==pCur->pBt ); - 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. ** @@ -7122,11 +7066,11 @@ if( pCur->iPage==0 ){ /* All pages of the b-tree have been visited. Return successfully. */ *pnEntry = nEntry; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); pCur->aiIdx[pCur->iPage]++; pPage = pCur->apPage[pCur->iPage]; } @@ -7349,20 +7293,20 @@ */ pBt = pCheck->pBt; usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage, zParentContext) ) return 0; - if( (rc = sqlite3BtreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ + if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1; checkAppendMsg(pCheck, zContext, "unable to get the page. error code=%d", rc); return 0; } - if( (rc = sqlite3BtreeInitPage(pPage))!=0 ){ + if( (rc = btreeInitPage(pPage))!=0 ){ assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ checkAppendMsg(pCheck, zContext, - "sqlite3BtreeInitPage() returns error code %d", rc); + "btreeInitPage() returns error code %d", rc); releasePage(pPage); return 0; } /* Check out all the cells. @@ -7376,11 +7320,11 @@ /* Check payload overflow pages */ sqlite3_snprintf(sizeof(zContext), zContext, "On tree page %d cell %d: ", iPage, i); pCell = findCell(pPage,i); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); sz = info.nData; if( !pPage->intKey ) sz += (int)info.nKey; assert( sz==info.nPayload ); if( (sz>info.nLocal) && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize]) @@ -7493,10 +7437,13 @@ #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* ** This routine does a complete check of the given BTree file. aRoot[] is ** an array of pages numbers were each page number is the root page of ** a table. nRoot is the number of entries in aRoot. +** +** A read-only or read-write transaction must be opened before calling +** this function. ** ** Write the number of error seen in *pnErr. Except for some memory ** allocation errors, an error message held in memory obtained from ** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is ** returned. If a memory allocation error occurs, NULL is returned. @@ -7513,31 +7460,25 @@ IntegrityCk sCheck; BtShared *pBt = p->pBt; char zErr[100]; sqlite3BtreeEnter(p); + assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); nRef = sqlite3PagerRefcount(pBt->pPager); - if( lockBtreeWithRetry(p)!=SQLITE_OK ){ - *pnErr = 1; - sqlite3BtreeLeave(p); - return sqlite3DbStrDup(0, "cannot acquire a read lock on the database"); - } sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; sCheck.nPage = pagerPagecount(sCheck.pBt); sCheck.mxErr = mxErr; sCheck.nErr = 0; sCheck.mallocFailed = 0; *pnErr = 0; if( sCheck.nPage==0 ){ - unlockBtreeIfUnused(pBt); sqlite3BtreeLeave(p); return 0; } sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); if( !sCheck.anRef ){ - unlockBtreeIfUnused(pBt); *pnErr = 1; sqlite3BtreeLeave(p); return 0; } for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; } @@ -7588,11 +7529,10 @@ /* Make sure this analysis did not leave any unref() pages. ** This is an internal consistency check; an integrity check ** of the integrity check. */ - unlockBtreeIfUnused(pBt); if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){ checkAppendMsg(&sCheck, 0, "Outstanding page count goes from %d to %d during this analysis", nRef, sqlite3PagerRefcount(pBt->pPager) ); @@ -7713,14 +7653,16 @@ ** lock is a write lock if isWritelock is true or a read lock ** if it is false. */ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ int rc = SQLITE_OK; + assert( p->inTrans!=TRANS_NONE ); if( p->sharable ){ u8 lockType = READ_LOCK + isWriteLock; assert( READ_LOCK+1==WRITE_LOCK ); assert( isWriteLock==0 || isWriteLock==1 ); + sqlite3BtreeEnter(p); rc = querySharedCacheTableLock(p, iTab, lockType); if( rc==SQLITE_OK ){ rc = setSharedCacheTableLock(p, iTab, lockType); } @@ -7733,45 +7675,45 @@ #ifndef SQLITE_OMIT_INCRBLOB /* ** Argument pCsr must be a cursor opened for writing on an ** INTKEY table currently pointing at a valid table entry. ** This function modifies the data stored as part of that entry. -** Only the data content may only be modified, it is not possible -** to change the length of the data stored. +** +** Only the data content may only be modified, it is not possible to +** change the length of the data stored. If this function is called with +** parameters that attempt to write past the end of the existing data, +** no modifications are made and SQLITE_CORRUPT is returned. */ 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); + assert( pCsr->isIncrblobHandle ); - restoreCursorPosition(pCsr); + rc = restoreCursorPosition(pCsr); + if( rc!=SQLITE_OK ){ + return rc; + } assert( pCsr->eState!=CURSOR_REQUIRESEEK ); if( pCsr->eState!=CURSOR_VALID ){ return SQLITE_ABORT; } - /* Check some preconditions: + /* Check some assumptions: ** (a) the cursor is open for writing, - ** (b) there is no read-lock on the table being modified and - ** (c) the cursor points at a valid row of an intKey table. + ** (b) there is a read/write transaction open, + ** (c) the connection holds a write-lock on the table (if required), + ** (d) there are no conflicting read-locks, and + ** (e) the cursor points at a valid row of an intKey table. */ if( !pCsr->wrFlag ){ return SQLITE_READONLY; } - assert( !pCsr->pBt->readOnly - && pCsr->pBt->inTransaction==TRANS_WRITE ); - 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; - } + assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE ); + assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); + assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); + assert( pCsr->apPage[pCsr->iPage]->intKey ); return accessPayload(pCsr, offset, amt, (unsigned char *)z, 0, 1); } /* Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -11,11 +11,11 @@ ************************************************************************* ** This header file defines the interface that the sqlite B-Tree file ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.116 2009/06/03 11:25:07 danielk1977 Exp $ +** @(#) $Id: btree.h,v 1.119 2009/07/09 05:07:38 danielk1977 Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ /* TODO: This definition is just included so other modules compile. It @@ -95,12 +95,12 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInReadTrans(Btree*); int sqlite3BtreeIsInBackup(Btree*); void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); -int sqlite3BtreeSchemaLocked(Btree *); -int sqlite3BtreeLockTable(Btree *, int, u8); +int sqlite3BtreeSchemaLocked(Btree *pBtree); +int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); int sqlite3BtreeSavepoint(Btree *, int, int); const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); int sqlite3BtreeCopyFile(Btree *, Btree *); @@ -116,11 +116,11 @@ int sqlite3BtreeDropTable(Btree*, int, int*); int sqlite3BtreeClearTable(Btree*, int, int*); void sqlite3BtreeTripAllCursors(Btree*, int); -int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); +void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); /* ** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta ** should be one of the following values. The integer values are assigned @@ -150,17 +150,10 @@ BtCursor *pCursor /* Space to write cursor structure */ ); int sqlite3BtreeCursorSize(void); int sqlite3BtreeCloseCursor(BtCursor*); -int sqlite3BtreeMoveto( - BtCursor*, - const void *pKey, - i64 nKey, - int bias, - int *pRes -); int sqlite3BtreeMovetoUnpacked( BtCursor*, UnpackedRecord *pUnKey, i64 intKey, int bias, @@ -173,11 +166,10 @@ int nZero, int bias, int seekResult); int sqlite3BtreeFirst(BtCursor*, int *pRes); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int *pRes); int sqlite3BtreeEof(BtCursor*); -int sqlite3BtreeFlags(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int *pRes); int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); Index: src/btreeInt.h ================================================================== --- src/btreeInt.h +++ src/btreeInt.h @@ -7,11 +7,11 @@ ** 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. ** ************************************************************************* -** $Id: btreeInt.h,v 1.48 2009/06/22 12:05:10 drh Exp $ +** $Id: btreeInt.h,v 1.51 2009/07/09 05:07:38 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: @@ -300,10 +300,28 @@ ** to the end. EXTRA_SIZE is the number of bytes of space needed to hold ** that extra information. */ #define EXTRA_SIZE sizeof(MemPage) +/* +** A linked list of the following structures is stored at BtShared.pLock. +** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor +** is opened on the table with root page BtShared.iTable. Locks are removed +** from this list when a transaction is committed or rolled back, or when +** a btree handle is closed. +*/ +struct BtLock { + Btree *pBtree; /* Btree handle holding this lock */ + Pgno iTable; /* Root page of table */ + u8 eLock; /* READ_LOCK or WRITE_LOCK */ + BtLock *pNext; /* Next in BtShared.pLock list */ +}; + +/* Candidate values for BtLock.eLock */ +#define READ_LOCK 1 +#define WRITE_LOCK 2 + /* A Btree handle ** ** A database connection contains a pointer to an instance of ** this object for every database file that it has open. This structure ** is opaque to the database connection. The database connection cannot @@ -331,10 +349,13 @@ u8 locked; /* True if db currently has pBt locked */ int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ int nBackup; /* Number of backup operations reading this btree */ Btree *pNext; /* List of other sharable Btrees from the same db */ Btree *pPrev; /* Back pointer of the same list */ +#ifndef SQLITE_OMIT_SHARED_CACHE + BtLock lock; /* Object used to lock page 1 */ +#endif }; /* ** Btree.inTrans may take one of the following values. ** @@ -474,13 +495,10 @@ int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */ #ifndef SQLITE_OMIT_INCRBLOB u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ Pgno *aOverflow; /* Cache of overflow page locations */ #endif -#ifndef NDEBUG - u8 pagesShuffled; /* True if Btree pages are rearranged by balance()*/ -#endif i16 iPage; /* Index of current page in apPage */ MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */ u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */ }; @@ -517,28 +535,10 @@ /* ** The database page the PENDING_BYTE occupies. This page is never used. */ # define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) -/* -** A linked list of the following structures is stored at BtShared.pLock. -** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor -** is opened on the table with root page BtShared.iTable. Locks are removed -** from this list when a transaction is committed or rolled back, or when -** a btree handle is closed. -*/ -struct BtLock { - Btree *pBtree; /* Btree handle holding this lock */ - Pgno iTable; /* Root page of table */ - u8 eLock; /* READ_LOCK or WRITE_LOCK */ - BtLock *pNext; /* Next in BtShared.pLock list */ -}; - -/* Candidate values for BtLock.eLock */ -#define READ_LOCK 1 -#define WRITE_LOCK 2 - /* ** These macros define the location of the pointer-map entry for a ** database page. The first argument to each is the number of usable ** bytes on each page of the database (often 1024). The second is the ** page number to look up in the pointer map. @@ -637,19 +637,5 @@ #define get2byte(x) ((x)[0]<<8 | (x)[1]) #define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) #define get4byte sqlite3Get4byte #define put4byte sqlite3Put4byte -/* -** Internal routines that should be accessed by the btree layer only. -*/ -int sqlite3BtreeGetPage(BtShared*, Pgno, MemPage**, int); -int sqlite3BtreeInitPage(MemPage *pPage); -void sqlite3BtreeParseCellPtr(MemPage*, u8*, CellInfo*); -void sqlite3BtreeParseCell(MemPage*, int, CellInfo*); -int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur); -void sqlite3BtreeMoveToParent(BtCursor *pCur); - -#ifdef SQLITE_TEST -void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur); -void sqlite3BtreeReleaseTempCursor(BtCursor *pCur); -#endif Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -20,11 +20,11 @@ ** creating ID lists ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.552 2009/06/18 17:22:39 drh Exp $ +** $Id: build.c,v 1.556 2009/07/01 16:12:08 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** This routine is called when a new SQL statement is beginning to @@ -172,10 +172,16 @@ /* Once all the cookies have been verified and transactions opened, ** obtain the required table-locks. This is a no-op unless the ** shared-cache feature is enabled. */ codeTableLocks(pParse); + + /* Initialize any AUTOINCREMENT data structures required. + */ + sqlite3AutoincrementBegin(pParse); + + /* Finally, jump back to the beginning of the executable code. */ sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto); } } @@ -370,14 +376,11 @@ int len; Hash *pHash = &db->aDb[iDb].pSchema->idxHash; len = sqlite3Strlen30(zIdxName); pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0); - /* Justification of ALWAYS(): This routine is only called from the - ** OP_DropIndex opcode. And there is no way that opcode will ever run - ** unless the corresponding index is in the symbol table. */ - if( ALWAYS(pIndex) ){ + if( pIndex ){ if( pIndex->pTable->pIndex==pIndex ){ pIndex->pTable->pIndex = pIndex->pNext; }else{ Index *p; /* Justification of ALWAYS(); The index must be on the list of @@ -3203,16 +3206,19 @@ Expr *pOn, /* The ON clause of a join */ IdList *pUsing /* The USING clause of a join */ ){ struct SrcList_item *pItem; sqlite3 *db = pParse->db; + if( !p && (pOn || pUsing) ){ + sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", + (pOn ? "ON" : "USING") + ); + goto append_from_error; + } p = sqlite3SrcListAppend(db, p, pTable, pDatabase); if( p==0 || NEVER(p->nSrc==0) ){ - sqlite3ExprDelete(db, pOn); - sqlite3IdListDelete(db, pUsing); - sqlite3SelectDelete(db, pSubquery); - return p; + goto append_from_error; } pItem = &p->a[p->nSrc-1]; assert( pAlias!=0 ); if( pAlias->n ){ pItem->zAlias = sqlite3NameFromToken(db, pAlias); @@ -3219,10 +3225,17 @@ } pItem->pSelect = pSubquery; pItem->pOn = pOn; pItem->pUsing = pUsing; return p; + + append_from_error: + assert( p==0 ); + sqlite3ExprDelete(db, pOn); + sqlite3IdListDelete(db, pUsing); + sqlite3SelectDelete(db, pSubquery); + return 0; } /* ** Add an INDEXED BY or NOT INDEXED clause to the most recently added ** element of the source-list passed as the second argument. Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.203 2009/05/28 01:00:55 drh Exp $ +** $Id: delete.c,v 1.204 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" /* ** Look up every table that is named in pSrc. If any table is not found, @@ -472,10 +472,18 @@ sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum); } sqlite3VdbeAddOp1(v, OP_Close, iCur); } } + + /* Update the sqlite_sequence table by storing the content of the + ** maximum rowid counter values recorded while inserting into + ** autoincrement tables. + */ + if( pParse->nested==0 && pParse->trigStack==0 ){ + sqlite3AutoincrementEnd(pParse); + } /* ** Return the number of rows that were deleted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.268 2009/05/29 19:00:13 drh Exp $ +** $Id: insert.c,v 1.269 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" /* ** Generate code that will open a table for reading. @@ -160,58 +160,87 @@ return 0; } #ifndef SQLITE_OMIT_AUTOINCREMENT /* -** Write out code to initialize the autoincrement logic. This code -** looks up the current autoincrement value in the sqlite_sequence -** table and stores that value in a register. Code generated by -** autoIncStep() will keep that register holding the largest -** rowid value. Code generated by autoIncEnd() will write the new -** largest value of the counter back into the sqlite_sequence table. -** -** This routine returns the index of the mem[] cell that contains -** the maximum rowid counter. -** -** Three consecutive registers are allocated by this routine. The -** first two hold the name of the target table and the maximum rowid -** inserted into the target table, respectively. -** The third holds the rowid in sqlite_sequence where we will -** write back the revised maximum rowid. This routine returns the -** index of the second of these three registers. +** Locate or create an AutoincInfo structure associated with table pTab +** which is in database iDb. Return the register number for the register +** that holds the maximum rowid. +** +** There is at most one AutoincInfo structure per table even if the +** same table is autoincremented multiple times due to inserts within +** triggers. A new AutoincInfo structure is created if this is the +** first use of table pTab. On 2nd and subsequent uses, the original +** AutoincInfo structure is used. +** +** Three memory locations are allocated: +** +** (1) Register to hold the name of the pTab table. +** (2) Register to hold the maximum ROWID of pTab. +** (3) Register to hold the rowid in sqlite_sequence of pTab +** +** The 2nd register is the one that is returned. That is all the +** insert routine needs to know about. */ static int autoIncBegin( Parse *pParse, /* Parsing context */ int iDb, /* Index of the database holding pTab */ Table *pTab /* The table we are writing to */ ){ int memId = 0; /* Register holding maximum rowid */ if( pTab->tabFlags & TF_Autoincrement ){ - Vdbe *v = pParse->pVdbe; - Db *pDb = &pParse->db->aDb[iDb]; - int iCur = pParse->nTab++; - int addr; /* Address of the top of the loop */ - assert( v ); - pParse->nMem++; /* Holds name of table */ - memId = ++pParse->nMem; - pParse->nMem++; - sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenRead); - addr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, pTab->zName, 0); - sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addr+9); - sqlite3VdbeAddOp3(v, OP_Column, iCur, 0, memId); - sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); - sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, memId+1); - sqlite3VdbeAddOp3(v, OP_Column, iCur, 1, memId); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9); - sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+2); - sqlite3VdbeAddOp2(v, OP_Integer, 0, memId); - sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); + AutoincInfo *pInfo; + + pInfo = pParse->pAinc; + while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } + if( pInfo==0 ){ + pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo)); + if( pInfo==0 ) return 0; + pInfo->pNext = pParse->pAinc; + pParse->pAinc = pInfo; + pInfo->pTab = pTab; + pInfo->iDb = iDb; + pParse->nMem++; /* Register to hold name of table */ + pInfo->regCtr = ++pParse->nMem; /* Max rowid register */ + pParse->nMem++; /* Rowid in sqlite_sequence */ + } + memId = pInfo->regCtr; } return memId; } + +/* +** This routine generates code that will initialize all of the +** register used by the autoincrement tracker. +*/ +void sqlite3AutoincrementBegin(Parse *pParse){ + AutoincInfo *p; /* Information about an AUTOINCREMENT */ + sqlite3 *db = pParse->db; /* The database connection */ + Db *pDb; /* Database only autoinc table */ + int memId; /* Register holding max rowid */ + int addr; /* A VDBE address */ + Vdbe *v = pParse->pVdbe; /* VDBE under construction */ + + assert( v ); /* We failed long ago if this is not so */ + for(p = pParse->pAinc; p; p = p->pNext){ + pDb = &db->aDb[p->iDb]; + memId = p->regCtr; + sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); + addr = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); + sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); + sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId); + sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); + sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); + sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9); + sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); + sqlite3VdbeAddOp2(v, OP_Integer, 0, memId); + sqlite3VdbeAddOp0(v, OP_Close); + } +} /* ** Update the maximum rowid for an autoincrement calculation. ** ** This routine should be called when the top of the stack holds a @@ -224,46 +253,56 @@ sqlite3VdbeAddOp2(pParse->pVdbe, OP_MemMax, memId, regRowid); } } /* -** After doing one or more inserts, the maximum rowid is stored -** in reg[memId]. Generate code to write this value back into the -** the sqlite_sequence table. +** This routine generates the code needed to write autoincrement +** maximum rowid values back into the sqlite_sequence register. +** Every statement that might do an INSERT into an autoincrement +** table (either directly or through triggers) needs to call this +** routine just before the "exit" code. */ -static void autoIncEnd( - Parse *pParse, /* The parsing context */ - int iDb, /* Index of the database holding pTab */ - Table *pTab, /* Table we are inserting into */ - int memId /* Memory cell holding the maximum rowid */ -){ - if( pTab->tabFlags & TF_Autoincrement ){ - int iCur = pParse->nTab++; - Vdbe *v = pParse->pVdbe; - Db *pDb = &pParse->db->aDb[iDb]; - int j1; - int iRec = ++pParse->nMem; /* Memory cell used for record */ - - assert( v ); - sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); +void sqlite3AutoincrementEnd(Parse *pParse){ + AutoincInfo *p; + Vdbe *v = pParse->pVdbe; + sqlite3 *db = pParse->db; + + assert( v ); + for(p = pParse->pAinc; p; p = p->pNext){ + Db *pDb = &db->aDb[p->iDb]; + int j1, j2, j3, j4, j5; + int iRec; + int memId = p->regCtr; + + iRec = sqlite3GetTempReg(pParse); + sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); - sqlite3VdbeAddOp2(v, OP_NewRowid, iCur, memId+1); + j2 = sqlite3VdbeAddOp0(v, OP_Rewind); + j3 = sqlite3VdbeAddOp3(v, OP_Column, 0, 0, iRec); + j4 = sqlite3VdbeAddOp3(v, OP_Eq, memId-1, 0, iRec); + sqlite3VdbeAddOp2(v, OP_Next, 0, j3); + sqlite3VdbeJumpHere(v, j2); + sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1); + j5 = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeJumpHere(v, j4); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, j5); sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec); - sqlite3VdbeAddOp3(v, OP_Insert, iCur, iRec, memId+1); + sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - sqlite3VdbeAddOp1(v, OP_Close, iCur); + sqlite3VdbeAddOp0(v, OP_Close); + sqlite3ReleaseTempReg(pParse, iRec); } } #else /* ** If SQLITE_OMIT_AUTOINCREMENT is defined, then the three routines ** above are all no-ops */ # define autoIncBegin(A,B,C) (0) # define autoIncStep(A,B,C) -# define autoIncEnd(A,B,C,D) #endif /* SQLITE_OMIT_AUTOINCREMENT */ /* Forward declaration */ static int xferOptimization( @@ -505,11 +544,11 @@ ** This is the 2nd template. */ if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ assert( !pTrigger ); assert( pList==0 ); - goto insert_cleanup; + goto insert_end; } #endif /* SQLITE_OMIT_XFER_OPT */ /* If this is an AUTOINCREMENT table, look up the sequence number in the ** sqlite_sequence table and store it in memory cell regAutoinc. @@ -987,15 +1026,18 @@ for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ sqlite3VdbeAddOp1(v, OP_Close, idx+baseCur); } } +insert_end: /* Update the sqlite_sequence table by storing the content of the - ** counter value in memory regAutoinc back into the sqlite_sequence - ** table. + ** maximum rowid counter values recorded while inserting into + ** autoincrement tables. */ - autoIncEnd(pParse, iDb, pTab, regAutoinc); + if( pParse->nested==0 && pParse->trigStack==0 ){ + sqlite3AutoincrementEnd(pParse); + } /* ** Return the number of rows inserted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. @@ -1714,11 +1756,10 @@ sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData); sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); sqlite3VdbeChangeP4(v, -1, pDest->zName, 0); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); - autoIncEnd(pParse, iDbDest, pDest, regAutoinc); for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } assert( pSrcIdx ); Index: src/legacy.c ================================================================== --- src/legacy.c +++ src/legacy.c @@ -12,11 +12,11 @@ ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: legacy.c,v 1.33 2009/05/05 20:02:48 drh Exp $ +** $Id: legacy.c,v 1.34 2009/07/03 19:18:43 drh Exp $ */ #include "sqliteInt.h" /* @@ -130,14 +130,16 @@ if( rc!=SQLITE_OK && ALWAYS(rc==sqlite3_errcode(db)) && pzErrMsg ){ int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db)); *pzErrMsg = sqlite3Malloc(nErrMsg); if( *pzErrMsg ){ memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg); + }else{ + rc = SQLITE_NOMEM; } }else if( pzErrMsg ){ *pzErrMsg = 0; } assert( (rc&db->errMask)==rc ); sqlite3_mutex_leave(db->mutex); return rc; } ADDED src/lempar.c Index: src/lempar.c ================================================================== --- /dev/null +++ src/lempar.c @@ -0,0 +1,854 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +** +** This version of "lempar.c" is modified, slightly, for use by SQLite. +** The only modifications are the addition of a couple of NEVER() +** macros to disable tests that are needed in the case of a general +** LALR(1) grammar but which are always false in the +** specific grammar used by SQLite. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +%% +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +%% +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +%% +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +%% +#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { +%% +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ +%% + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + /* There is no mechanism by which the parser stack can be popped below + ** empty in SQLite. */ + if( NEVER(pParser->yyidx<0) ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + /* In SQLite, we never try to destroy a parser that was not successfully + ** created in the first place. */ + if( NEVER(pParser==0) ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + /* The user of ";" instead of "\000" as a statement terminator in SQLite + ** means that we always have a look-ahead token. */ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( j>=0 && j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_MAX ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_MAX ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +%% + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -12,11 +12,11 @@ ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.558 2009/06/19 14:06:03 drh Exp $ +** $Id: main.c,v 1.560 2009/06/26 15:14:55 drh Exp $ */ #include "sqliteInt.h" #ifdef SQLITE_ENABLE_FTS3 # include "fts3.h" @@ -747,11 +747,11 @@ /* SQLITE_FULL */ "database or disk is full", /* SQLITE_CANTOPEN */ "unable to open database file", /* SQLITE_PROTOCOL */ 0, /* SQLITE_EMPTY */ "table contains no data", /* SQLITE_SCHEMA */ "database schema has changed", - /* SQLITE_TOOBIG */ "String or BLOB exceeded size limit", + /* SQLITE_TOOBIG */ "string or blob too big", /* SQLITE_CONSTRAINT */ "constraint failed", /* SQLITE_MISMATCH */ "datatype mismatch", /* SQLITE_MISUSE */ "library routine called out of sequence", /* SQLITE_NOLFS */ "large file support is disabled", /* SQLITE_AUTH */ "authorization denied", @@ -1560,11 +1560,10 @@ goto opendb_out; } } sqlite3_mutex_enter(db->mutex); db->errMask = 0xff; - db->priorNewRowid = 0; db->nDb = 2; db->magic = SQLITE_MAGIC_BUSY; db->aDb = db->aDbStatic; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); Index: src/malloc.c ================================================================== --- src/malloc.c +++ src/malloc.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** ** Memory allocation functions used throughout sqlite. ** -** $Id: malloc.c,v 1.62 2009/05/03 20:23:54 drh Exp $ +** $Id: malloc.c,v 1.64 2009/06/27 00:48:33 drh Exp $ */ #include "sqliteInt.h" #include /* @@ -264,19 +264,16 @@ ** Allocate memory. This routine is like sqlite3_malloc() except that it ** assumes the memory subsystem has already been initialized. */ void *sqlite3Malloc(int n){ void *p; - if( n<=0 || NEVER(n>=0x7fffff00) ){ - /* The NEVER(n>=0x7fffff00) term is added out of paranoia. We want to make - ** absolutely sure that there is nothing within SQLite that can cause a - ** memory allocation of a number of bytes which is near the maximum signed - ** integer value and thus cause an integer overflow inside of the xMalloc() - ** implementation. The n>=0x7fffff00 gives us 255 bytes of headroom. The - ** test should never be true because SQLITE_MAX_LENGTH should be much - ** less than 0x7fffff00 and it should catch large memory allocations - ** before they reach this point. */ + if( n<=0 || n>=0x7fffff00 ){ + /* A memory allocation of a number of bytes which is near the maximum + ** signed integer value might cause an integer overflow inside of the + ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving + ** 255 bytes of overhead. SQLite itself will never use anything near + ** this amount. The only way to reach the limit is with sqlite3_malloc() */ p = 0; }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); mallocWithAlarm(n, &p); sqlite3_mutex_leave(mem0.mutex); @@ -474,14 +471,17 @@ int nOld, nNew; void *pNew; if( pOld==0 ){ return sqlite3Malloc(nBytes); } - if( nBytes<=0 || NEVER(nBytes>=0x7fffff00) ){ - /* The NEVER(...) term is explained in comments on sqlite3Malloc() */ + if( nBytes<=0 ){ sqlite3_free(pOld); return 0; + } + if( nBytes>=0x7fffff00 ){ + /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ + return 0; } nOld = sqlite3MallocSize(pOld); if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -41,11 +41,11 @@ ** * sqlite3_vfs method implementations. ** * Locking primitives for the proxy uber-locking-method. (MacOSX only) ** * 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.253 2009/06/17 13:09:39 drh Exp $ +** $Id: os_unix.c,v 1.254 2009/07/03 12:57:58 drh Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_UNIX /* This file is used on unix only */ /* @@ -837,17 +837,18 @@ if( rc!=0 ) return; memset(&d, 0, sizeof(d)); d.fd = fd; d.lock = l; d.lock.l_type = F_WRLCK; - pthread_create(&t, 0, threadLockingTest, &d); - pthread_join(t, 0); + if( pthread_create(&t, 0, threadLockingTest, &d)==0 ){ + pthread_join(t, 0); + } close(fd); if( d.result!=0 ) return; threadsOverrideEachOthersLocks = (d.lock.l_type==F_UNLCK); } -#endif /* SQLITE_THERADSAFE && defined(__linux__) */ +#endif /* SQLITE_THREADSAFE && defined(__linux__) */ /* ** Release a unixLockInfo structure previously allocated by findLockInfo(). */ static void releaseLockInfo(struct unixLockInfo *pLock){ Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -16,11 +16,11 @@ ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.601 2009/06/22 05:43:24 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.605 2009/07/07 13:56:24 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" /* @@ -755,11 +755,10 @@ pPager->aSavepoint[ii].iHdrOffset = pPager->journalOff; } } pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager); - memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); /* ** Write the nRec Field - the number of page records that follow this ** journal header. Normally, zero is written to this value at this time. ** After the records are added to the journal (and the journal synced, @@ -781,12 +780,14 @@ */ assert( isOpen(pPager->fd) || pPager->noSync ); if( (pPager->noSync) || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY) || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) ){ + memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); }else{ + zHeader[0] = '\0'; put32bits(&zHeader[sizeof(aJournalMagic)], 0); } /* The random check-hash initialiser */ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); @@ -850,10 +851,11 @@ ** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes ** cannot be read from the journal file an error code is returned. */ static int readJournalHdr( Pager *pPager, /* Pager object */ + int isHot, i64 journalSize, /* Size of the open journal file in bytes */ u32 *pNRec, /* OUT: Value read from the nRec field */ u32 *pDbSize /* OUT: Value of original database size field */ ){ int rc; /* Return code */ @@ -875,16 +877,18 @@ /* Read in the first 8 bytes of the journal header. If they do not match ** the magic string found at the start of each journal header, return ** SQLITE_DONE. If an IO error occurs, return an error code. Otherwise, ** proceed. */ - rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff); - if( rc ){ - return rc; - } - if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ - return SQLITE_DONE; + if( isHot || iHdrOff!=pPager->journalHdr ){ + rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff); + if( rc ){ + return rc; + } + if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ + return SQLITE_DONE; + } } /* Read the first three 32-bit fields of the journal header: The nRec ** field, the checksum-initializer and the database size at the start ** of the transaction. Return an error code if anything goes wrong. @@ -1975,11 +1979,11 @@ /* Read the next journal header from the journal file. If there are ** not enough bytes left in the journal file for a complete header, or ** it is corrupted, then a process must of failed while writing it. ** This indicates nothing more needs to be rolled back. */ - rc = readJournalHdr(pPager, szJ, &nRec, &mxPg); + rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; } goto end_playback; @@ -2195,11 +2199,11 @@ */ while( rc==SQLITE_OK && pPager->journalOffjournalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff" ** test is related to ticket #2565. See the discussion in the @@ -2758,16 +2762,10 @@ int rc; /* Return code */ const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); assert( isOpen(pPager->jfd) ); if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - /* Variable iNRecOffset is set to the offset in the journal file - ** of the nRec field of the most recently written journal header. - ** This field will be updated following the xSync() operation - ** on the journal file. */ - i64 iNRecOffset = pPager->journalHdr + sizeof(aJournalMagic); - /* This block deals with an obscure problem. If the last connection ** that wrote to this database was operating in persistent-journal ** mode, then the journal file may at this point actually be larger ** than Pager.journalOff bytes. If the next thing in the journal ** file happens to be a journal-header (written as part of the @@ -2786,12 +2784,18 @@ ** Variable iNextHdrOffset is set to the offset at which this ** problematic header will occur, if it exists. aMagic is used ** as a temporary buffer to inspect the first couple of bytes of ** the potential journal header. */ - i64 iNextHdrOffset = journalHdrOffset(pPager); + i64 iNextHdrOffset; u8 aMagic[8]; + u8 zHeader[sizeof(aJournalMagic)+4]; + + memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); + put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec); + + iNextHdrOffset = journalHdrOffset(pPager); rc = sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ static const u8 zerobyte = 0; rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); } @@ -2814,12 +2818,14 @@ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags); if( rc!=SQLITE_OK ) return rc; } - IOTRACE(("JHDR %p %lld %d\n", pPager, iNRecOffset, 4)); - rc = write32bits(pPager->jfd, iNRecOffset, pPager->nRec); + IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); + rc = sqlite3OsWrite( + pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr + ); if( rc!=SQLITE_OK ) return rc; } if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) @@ -3245,10 +3251,11 @@ if( zPathname ){ pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); + if( pPager->zFilename[0]==0 ) pPager->zJournal[0] = 0; sqlite3_free(zPathname); } pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; @@ -4157,10 +4164,19 @@ rc = pager_open_journal(pPager); } PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); assert( !isOpen(pPager->jfd) || pPager->journalOff>0 || rc!=SQLITE_OK ); + if( rc!=SQLITE_OK ){ + assert( !pPager->dbModified ); + /* Ignore any IO error that occurs within pager_end_transaction(). The + ** purpose of this call is to reset the internal state of the pager + ** sub-system. It doesn't matter if the journal-file is not properly + ** finalized at this point (since it is not a valid journal file anyway). + */ + pager_end_transaction(pPager, 0); + } return rc; } /* ** Mark a single data page as writeable. The page is written into the Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -12,11 +12,11 @@ ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.283 2009/06/19 14:06:03 drh Exp $ +** @(#) $Id: parse.y,v 1.285 2009/07/03 15:37:28 drh Exp $ */ // All token codes are small integers with #defines that begin with "TK_" %token_prefix TK_ @@ -502,13 +502,11 @@ as(Z) on_opt(N) using_opt(U). { A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,S,N,U); } seltablist(A) ::= stl_prefix(X) LP seltablist(F) RP as(Z) on_opt(N) using_opt(U). { - if( X==0 ){ - sqlite3ExprDelete(pParse->db, N); - sqlite3IdListDelete(pParse->db, U); + if( X==0 && Z.n==0 && N==0 && U==0 ){ A = F; }else{ Select *pSubquery; sqlite3SrcListShiftJoinType(F); pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0); @@ -1178,27 +1176,59 @@ trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. { assert( X!=0 ); X->pLast = X; A = X; } + +// Disallow qualified table names on INSERT, UPDATE, and DELETE statements +// within a trigger. The table to INSERT, UPDATE, or DELETE is always in +// the same database as the table that the trigger fires on. +// +%type trnm {Token} +trnm(A) ::= nm(X). {A = X;} +trnm(A) ::= nm DOT nm(X). { + A = X; + sqlite3ErrorMsg(pParse, + "qualified table names are not allowed on INSERT, UPDATE, and DELETE " + "statements within triggers"); +} + +// Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE +// statements within triggers. We make a specific error message for this +// since it is an exception to the default grammar rules. +// +tridxby ::= . +tridxby ::= INDEXED BY nm. { + sqlite3ErrorMsg(pParse, + "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " + "within triggers"); +} +tridxby ::= NOT INDEXED. { + sqlite3ErrorMsg(pParse, + "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " + "within triggers"); +} + + %type trigger_cmd {TriggerStep*} %destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);} // UPDATE -trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z). - { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } +trigger_cmd(A) ::= + UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). + { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } // INSERT -trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) - VALUES LP itemlist(Y) RP. - {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);} +trigger_cmd(A) ::= + insert_cmd(R) INTO trnm(X) inscollist_opt(F) VALUES LP itemlist(Y) RP. + {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);} -trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S). +trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S). {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);} // DELETE -trigger_cmd(A) ::= DELETE FROM nm(X) where_opt(Y). +trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);} // SELECT trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); } Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.213 2009/06/19 14:06:03 drh Exp $ +** $Id: pragma.c,v 1.214 2009/07/02 07:47:33 danielk1977 Exp $ */ #include "sqliteInt.h" /* Ignore this whole file if pragmas are disabled */ @@ -316,16 +316,17 @@ ** synchronous setting. A negative value means synchronous is off ** and a positive value means synchronous is on. */ if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){ static const VdbeOpList getCacheSize[] = { - { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 0 */ - { OP_IfPos, 1, 6, 0}, + { OP_Transaction, 0, 0, 0}, /* 0 */ + { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ + { OP_IfPos, 1, 7, 0}, { OP_Integer, 0, 2, 0}, { OP_Subtract, 1, 2, 1}, - { OP_IfPos, 1, 6, 0}, - { OP_Integer, 0, 1, 0}, /* 5 */ + { OP_IfPos, 1, 7, 0}, + { OP_Integer, 0, 1, 0}, /* 6 */ { OP_ResultRow, 1, 1, 0}, }; int addr; if( sqlite3ReadSchema(pParse) ) goto pragma_out; sqlite3VdbeUsesBtree(v, iDb); @@ -333,11 +334,12 @@ sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC); pParse->nMem += 2; addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP1(v, addr+5, SQLITE_DEFAULT_CACHE_SIZE); + sqlite3VdbeChangeP1(v, addr+1, iDb); + sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ int size = atoi(zRight); if( size<0 ) size = -size; sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); @@ -1300,16 +1302,18 @@ sqlite3VdbeChangeP1(v, addr+2, iDb); sqlite3VdbeChangeP2(v, addr+2, iCookie); }else{ /* Read the specified cookie value */ static const VdbeOpList readCookie[] = { - { OP_ReadCookie, 0, 1, 0}, /* 0 */ + { OP_Transaction, 0, 0, 0}, /* 0 */ + { OP_ReadCookie, 0, 1, 0}, /* 1 */ { OP_ResultRow, 1, 1, 0} }; int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie); sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP3(v, addr, iCookie); + sqlite3VdbeChangeP1(v, addr+1, iDb); + sqlite3VdbeChangeP3(v, addr+1, iCookie); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT); } }else #endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */ Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -11,11 +11,11 @@ ************************************************************************* ** This file contains the implementation of the sqlite3_prepare() ** interface, and routines that contribute to loading the database schema ** from disk. ** -** $Id: prepare.c,v 1.124 2009/06/22 12:05:10 drh Exp $ +** $Id: prepare.c,v 1.128 2009/07/03 19:19:50 drh Exp $ */ #include "sqliteInt.h" /* ** Fill the InitData structure with an error message that indicates @@ -85,11 +85,11 @@ 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!=SQLITE_LOCKED ){ corruptSchema(pData, argv[0], zErr); } sqlite3DbFree(db, zErr); } }else if( argv[0]==0 ){ @@ -126,19 +126,19 @@ ** indicate success or failure. */ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ int rc; int i; - BtCursor *curMain; int size; Table *pTab; Db *pDb; char const *azArg[4]; int meta[5]; InitData initData; char const *zMasterSchema; char const *zMasterName = SCHEMA_TABLE(iDb); + int openedTransaction = 0; /* ** The master database table has a structure like this */ static const char master_schema[] = @@ -208,18 +208,23 @@ if( !OMIT_TEMPDB && ALWAYS(iDb==1) ){ DbSetProperty(db, 1, DB_SchemaLoaded); } return SQLITE_OK; } - curMain = sqlite3MallocZero(sqlite3BtreeCursorSize()); - if( !curMain ){ - rc = SQLITE_NOMEM; - goto error_out; - } + + /* If there is not already a read-only (or read-write) transaction opened + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed before this function returns. */ sqlite3BtreeEnter(pDb->pBt); - rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, curMain); - if( rc==SQLITE_EMPTY ) rc = SQLITE_OK; + if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ + rc = sqlite3BtreeBeginTrans(pDb->pBt, 0); + if( rc!=SQLITE_OK ){ + sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); + goto initone_error_out; + } + openedTransaction = 1; + } /* Get the database meta information. ** ** Meta values are as follows: ** meta[0] Schema cookie. Changes with each schema change. @@ -234,16 +239,12 @@ ** meta[9] unused ** ** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to ** the possible values of meta[4]. */ - for(i=0; rc==SQLITE_OK && ipBt, i+1, (u32 *)&meta[i]); - } - if( rc ){ - sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); - goto initone_error_out; + for(i=0; ipBt, i+1, (u32 *)&meta[i]); } pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1]; /* If opening a non-empty database, check the text encoding. For the ** main database, set sqlite3.enc to the encoding of the main database. @@ -354,12 +355,13 @@ /* Jump here for an error that occurs after successfully allocating ** curMain and calling sqlite3BtreeEnter(). For an error that occurs ** before that point, jump to error_out. */ initone_error_out: - sqlite3BtreeCloseCursor(curMain); - sqlite3_free(curMain); + if( openedTransaction ){ + sqlite3BtreeCommit(pDb->pBt); + } sqlite3BtreeLeave(pDb->pBt); error_out: if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; @@ -433,47 +435,51 @@ } /* ** Check schema cookies in all databases. If any cookie is out -** of date, return 0. If all schema cookies are current, return 1. -*/ -static int schemaIsValid(sqlite3 *db){ - int iDb; - int rc; - BtCursor *curTemp; - int cookie; - int allOk = 1; - - curTemp = (BtCursor *)sqlite3Malloc(sqlite3BtreeCursorSize()); - if( curTemp ){ - assert( sqlite3_mutex_held(db->mutex) ); - for(iDb=0; allOk && iDbnDb; iDb++){ - Btree *pBt; - pBt = db->aDb[iDb].pBt; - if( pBt==0 ) continue; - memset(curTemp, 0, sqlite3BtreeCursorSize()); - rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, curTemp); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); - if( ALWAYS(rc==SQLITE_OK) - && cookie!=db->aDb[iDb].pSchema->schema_cookie ){ - allOk = 0; - } - sqlite3BtreeCloseCursor(curTemp); - } - if( NEVER(rc==SQLITE_NOMEM) || rc==SQLITE_IOERR_NOMEM ){ - db->mallocFailed = 1; - } - } - sqlite3_free(curTemp); - }else{ - allOk = 0; - db->mallocFailed = 1; - } - - return allOk; +** of date set pParse->rc to SQLITE_SCHEMA. If all schema cookies +** make no changes to pParse->rc. +*/ +static void schemaIsValid(Parse *pParse){ + sqlite3 *db = pParse->db; + int iDb; + int rc; + int cookie; + + assert( pParse->checkSchema ); + assert( sqlite3_mutex_held(db->mutex) ); + for(iDb=0; iDbnDb; iDb++){ + int openedTransaction = 0; /* True if a transaction is opened */ + Btree *pBt = db->aDb[iDb].pBt; /* Btree database to read cookie from */ + if( pBt==0 ) continue; + + /* If there is not already a read-only (or read-write) transaction opened + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed immediately after reading the meta-value. */ + if( !sqlite3BtreeIsInReadTrans(pBt) ){ + rc = sqlite3BtreeBeginTrans(pBt, 0); + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ + db->mallocFailed = 1; + } + if( rc!=SQLITE_OK ) return; + openedTransaction = 1; + } + + /* Read the schema cookie from the database. If it does not match the + ** value stored as part of the in the in-memory schema representation, + ** set Parse.rc to SQLITE_SCHEMA. */ + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); + if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ + pParse->rc = SQLITE_SCHEMA; + } + + /* Close the transaction, if one was opened. */ + if( openedTransaction ){ + sqlite3BtreeCommit(pBt); + } + } } /* ** Convert a schema pointer into the iDb index that indicates ** which database file in db->aDb[] the schema refers to. @@ -602,12 +608,12 @@ if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; - if( pParse->checkSchema && !schemaIsValid(db) ){ - pParse->rc = SQLITE_SCHEMA; + if( pParse->checkSchema ){ + schemaIsValid(pParse); } if( pParse->rc==SQLITE_SCHEMA ){ sqlite3ResetInternalSchema(db, 0); } if( db->mallocFailed ){ Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.886 2009/06/19 14:06:03 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.892 2009/07/03 22:54:37 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ /* @@ -265,13 +265,12 @@ */ #if defined(SQLITE_COVERAGE_TEST) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) - int sqlite3Assert(void); -# define ALWAYS(X) ((X)?1:sqlite3Assert()) -# define NEVER(X) ((X)?sqlite3Assert():0) +# define ALWAYS(X) ((X)?1:(assert(0),0)) +# define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif @@ -579,10 +578,11 @@ /* ** Forward references to structures */ typedef struct AggInfo AggInfo; typedef struct AuthContext AuthContext; +typedef struct AutoincInfo AutoincInfo; typedef struct Bitvec Bitvec; typedef struct RowSet RowSet; typedef struct CollSeq CollSeq; typedef struct Column Column; typedef struct Db Db; @@ -788,11 +788,10 @@ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ int nextPagesize; /* Pagesize after VACUUM if >0 */ int nTable; /* Number of tables in the database */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ i64 lastRowid; /* ROWID of most recent insert (see above) */ - i64 priorNewRowid; /* Last randomly generated ROWID */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ int nTotalChange; /* Value returned by sqlite3_total_changes() */ sqlite3_mutex *mutex; /* Connection mutex */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ @@ -1924,10 +1923,26 @@ int iParm; /* A parameter used by the eDest disposal method */ int iMem; /* Base register where results are written */ int nMem; /* Number of registers allocated */ }; +/* +** During code generation of statements that do inserts into AUTOINCREMENT +** tables, the following information is attached to the Table.u.autoInc.p +** pointer of each autoincrement table to record some side information that +** the code generator needs. We have to keep per-table autoincrement +** information in case inserts are down within triggers. Triggers do not +** normally coordinate their activities, but we do need to coordinate the +** loading and saving of autoincrement information. +*/ +struct AutoincInfo { + AutoincInfo *pNext; /* Next info block in a list of them all */ + Table *pTab; /* Table this info block refers to */ + int iDb; /* Index in sqlite3.aDb[] of database holding pTab */ + int regCtr; /* Memory register holding the rowid counter */ +}; + /* ** Size of the column cache */ #ifndef SQLITE_N_COLCACHE # define SQLITE_N_COLCACHE 10 @@ -1990,10 +2005,11 @@ int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ #endif int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRoot; /* Register holding root page number for new objects */ + AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ int nVar; /* Number of '?' variables seen in the SQL so far */ @@ -2002,14 +2018,12 @@ Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ int nAlias; /* Number of aliased result set columns */ int nAliasAlloc; /* Number of allocated slots for aAlias[] */ int *aAlias; /* Register used to hold aliased result */ u8 explain; /* True if the EXPLAIN flag is found on the query */ - Token sErrToken; /* The token at which the error occurred */ Token sNameToken; /* Token with unqualified schema object name */ Token sLastToken; /* The last token parsed */ - const char *zSql; /* All SQL text */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ TriggerStack *trigStack; /* Trigger actions being coded */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ @@ -2123,21 +2137,18 @@ * them to. See sqlite3Update() documentation of "pChanges" * argument. * */ struct TriggerStep { - int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ - int orconf; /* OE_Rollback etc. */ + u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ + u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ - - Select *pSelect; /* Valid for SELECT and sometimes - INSERT steps (when pExprList == 0) */ - Token target; /* Target table for DELETE, UPDATE, INSERT. Quoted */ - Expr *pWhere; /* Valid for DELETE, UPDATE steps */ - ExprList *pExprList; /* Valid for UPDATE statements and sometimes - INSERT steps (when pSelect == 0) */ - IdList *pIdList; /* Valid for INSERT statements only */ + Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ + Token target; /* Target table for DELETE, UPDATE, INSERT */ + Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ + ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */ + IdList *pIdList; /* Column names for INSERT */ TriggerStep *pNext; /* Next in the link-list */ TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */ }; /* @@ -2480,10 +2491,17 @@ # define sqlite3ViewGetColumnNames(A,B) 0 #endif void sqlite3DropTable(Parse*, SrcList*, int, int); void sqlite3DeleteTable(Table*); +#ifndef SQLITE_OMIT_AUTOINCREMENT + void sqlite3AutoincrementBegin(Parse *pParse); + void sqlite3AutoincrementEnd(Parse *pParse); +#else +# define sqlite3AutoincrementBegin(X) +# define sqlite3AutoincrementEnd(X) +#endif void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); void *sqlite3ArrayAllocate(sqlite3*,void*,int,int,int*,int*,int*); IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); int sqlite3IdListIndex(IdList*,const char*); SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); Index: src/tclsqlite.c ================================================================== --- src/tclsqlite.c +++ src/tclsqlite.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** A TCL Interface to SQLite. Append this file to sqlite3.c and ** compile the whole thing to build a TCL-enabled version of SQLite. ** -** $Id: tclsqlite.c,v 1.241 2009/03/27 12:44:35 drh Exp $ +** $Id: tclsqlite.c,v 1.242 2009/07/03 22:54:37 drh Exp $ */ #include "tcl.h" #include /* @@ -1672,10 +1672,11 @@ /* Try to find a SQL statement that has already been compiled and ** which matches the next sequence of SQL. */ pStmt = 0; + while( isspace(zSql[0]) ){ zSql++; } len = strlen30(zSql); for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ int n = pPreStmt->nSql; if( len>=n && memcmp(pPreStmt->zSql, zSql, n)==0 Index: src/test3.c ================================================================== --- src/test3.c +++ src/test3.c @@ -11,11 +11,11 @@ ************************************************************************* ** Code for testing the btree.c module in SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test3.c,v 1.104 2009/05/04 11:42:30 danielk1977 Exp $ +** $Id: test3.c,v 1.111 2009/07/09 05:07:38 danielk1977 Exp $ */ #include "sqliteInt.h" #include "btreeInt.h" #include "tcl.h" #include @@ -155,329 +155,10 @@ return TCL_ERROR; } return TCL_OK; } -/* -** Usage: btree_rollback ID -** -** Rollback changes -*/ -static int btree_rollback( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeRollback(pBt); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_commit ID -** -** Commit all changes -*/ -static int btree_commit( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeCommit(pBt); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_begin_statement ID -** -** Start a new statement transaction -*/ -static int btree_begin_statement( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeBeginStmt(pBt, 1); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_rollback_statement ID -** -** Rollback changes -*/ -static int btree_rollback_statement( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0); - } - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_commit_statement ID -** -** Commit all changes -*/ -static int btree_commit_statement( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_create_table ID FLAGS -** -** Create a new table in the database -*/ -static int btree_create_table( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc, iTable, flags; - char zBuf[30]; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID FLAGS\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &flags) ) return TCL_ERROR; - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeCreateTable(pBt, &iTable, flags); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iTable); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: btree_drop_table ID TABLENUM -** -** Delete an entire table from the database -*/ -static int btree_drop_table( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int iTable; - int rc; - int notUsed1; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID TABLENUM\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeDropTable(pBt, iTable, ¬Used1); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_clear_table ID TABLENUM -** -** Remove all entries from the given table but keep the table around. -*/ -static int btree_clear_table( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int iTable; - int rc; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID TABLENUM\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeClearTable(pBt, iTable, 0); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_get_meta ID -** -** Return meta data -*/ -static int btree_get_meta( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - int i; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - for(i=0; idb->mutex); return TCL_OK; } -/* -** Usage: btree_integrity_check ID ROOT ... -** -** Look through every page of the given BTree file to verify correct -** formatting and linkage. Return a line of text for each problem found. -** Return an empty string if everything worked. -*/ -static int btree_integrity_check( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int nRoot; - int *aRoot; - int i; - int nErr; - char *zResult; - - if( argc<3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID ROOT ...\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - nRoot = argc-2; - aRoot = (int*)sqlite3_malloc( sizeof(int)*(argc-2) ); - for(i=0; ipBtree); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ - int iKey; - if( Tcl_GetInt(interp, argv[2], &iKey) ){ - sqlite3BtreeLeave(pCur->pBtree); - return TCL_ERROR; - } - rc = sqlite3BtreeMovetoUnpacked(pCur, 0, iKey, 0, &res); - }else{ - rc = sqlite3BtreeMoveto(pCur, argv[2], strlen(argv[2]), 0, &res); - } - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - if( res<0 ) res = -1; - if( res>0 ) res = 1; - sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_delete ID -** -** Delete the entry that the cursor is pointing to -*/ -static int btree_delete( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreeDelete(pCur); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return SQLITE_OK; -} - -/* -** Usage: btree_insert ID KEY DATA ?NZERO? -** -** Create a new entry with the given key and data. If an entry already -** exists with the same key the old entry is overwritten. -*/ -static int btree_insert( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - BtCursor *pCur; - int rc; - int nZero; - - if( objc!=4 && objc!=5 ){ - Tcl_WrongNumArgs(interp, 1, objv, "ID KEY DATA ?NZERO?"); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - if( objc==5 ){ - if( Tcl_GetIntFromObj(interp, objv[4], &nZero) ) return TCL_ERROR; - }else{ - nZero = 0; - } - sqlite3BtreeEnter(pCur->pBtree); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ - i64 iKey; - int len; - unsigned char *pBuf; - if( Tcl_GetWideIntFromObj(interp, objv[2], &iKey) ){ - sqlite3BtreeLeave(pCur->pBtree); - return TCL_ERROR; - } - pBuf = Tcl_GetByteArrayFromObj(objv[3], &len); - rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0, 0); - }else{ - int keylen; - int dlen; - unsigned char *pKBuf; - unsigned char *pDBuf; - pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen); - pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen); - rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0, 0); - } - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return SQLITE_OK; -} - /* ** Usage: btree_next ID ** ** Move the cursor to the next entry in the table. Return 0 on success ** or 1 if the cursor was already on the last entry in the table or if @@ -826,46 +310,10 @@ pCur = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pCur->pBtree); rc = sqlite3BtreeNext(pCur, &res); sqlite3BtreeLeave(pCur->pBtree); if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_prev ID -** -** Move the cursor to the previous entry in the table. Return 0 on -** success and 1 if the cursor was already on the first entry in -** the table or if the table was empty. -*/ -static int btree_prev( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int res = 0; - char zBuf[100]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreePrevious(pCur, &res); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); Tcl_AppendResult(interp, zBuf, 0); @@ -905,45 +353,10 @@ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); Tcl_AppendResult(interp, zBuf, 0); return SQLITE_OK; } -/* -** Usage: btree_last ID -** -** Move the cursor to the last entry in the table. Return 0 if the -** cursor was left point to something and 1 if the table is empty. -*/ -static int btree_last( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int res = 0; - char zBuf[100]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreeLast(pCur, &res); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - /* ** Usage: btree_eof ID ** ** Return TRUE if the given cursor is not pointing at a valid entry. ** Return FALSE if the cursor does point to a valid entry. @@ -970,206 +383,10 @@ sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc); Tcl_AppendResult(interp, zBuf, 0); return SQLITE_OK; } -/* -** Usage: btree_keysize ID -** -** Return the number of bytes of key. For an INTKEY table, this -** returns the key itself. -*/ -static int btree_keysize( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - u64 n; - char zBuf[50]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeKeySize(pCur, (i64*)&n); - sqlite3BtreeLeave(pCur->pBtree); - sqlite3_snprintf(sizeof(zBuf),zBuf, "%llu", n); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_key ID -** -** Return the key for the entry at which the cursor is pointing. -*/ -static int btree_key( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - u64 n; - char *zBuf; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeKeySize(pCur, (i64*)&n); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ - char zBuf2[60]; - sqlite3_snprintf(sizeof(zBuf2),zBuf2, "%llu", n); - Tcl_AppendResult(interp, zBuf2, 0); - }else{ - zBuf = sqlite3_malloc( n+1 ); - rc = sqlite3BtreeKey(pCur, 0, n, zBuf); - if( rc ){ - sqlite3BtreeLeave(pCur->pBtree); - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - zBuf[n] = 0; - Tcl_AppendResult(interp, zBuf, 0); - sqlite3_free(zBuf); - } - sqlite3BtreeLeave(pCur->pBtree); - return SQLITE_OK; -} - -/* -** Usage: btree_data ID ?N? -** -** Return the data for the entry at which the cursor is pointing. -*/ -static int btree_data( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - u32 n; - char *zBuf; - - if( argc!=2 && argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - if( argc==2 ){ - sqlite3BtreeDataSize(pCur, &n); - }else{ - n = atoi(argv[2]); - } - zBuf = sqlite3_malloc( n+1 ); - rc = sqlite3BtreeData(pCur, 0, n, zBuf); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - sqlite3_free(zBuf); - return TCL_ERROR; - } - zBuf[n] = 0; - Tcl_AppendResult(interp, zBuf, 0); - sqlite3_free(zBuf); - return SQLITE_OK; -} - -/* -** Usage: btree_fetch_key ID AMT -** -** Use the sqlite3BtreeKeyFetch() routine to get AMT bytes of the key. -** If sqlite3BtreeKeyFetch() fails, return an empty string. -*/ -static int btree_fetch_key( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int n; - int amt; - u64 nKey; - const char *zBuf; - char zStatic[1000]; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID AMT\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeKeySize(pCur, (i64*)&nKey); - zBuf = sqlite3BtreeKeyFetch(pCur, &amt); - if( zBuf && amt>=n ){ - assert( nKey0 ) nKey = n; - memcpy(zStatic, zBuf, (int)nKey); - zStatic[nKey] = 0; - Tcl_AppendResult(interp, zStatic, 0); - } - sqlite3BtreeLeave(pCur->pBtree); - return TCL_OK; -} - -/* -** Usage: btree_fetch_data ID AMT -** -** Use the sqlite3BtreeDataFetch() routine to get AMT bytes of the key. -** If sqlite3BtreeDataFetch() fails, return an empty string. -*/ -static int btree_fetch_data( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int n; - int amt; - u32 nData; - const char *zBuf; - char zStatic[1000]; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID AMT\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeDataSize(pCur, &nData); - zBuf = sqlite3BtreeDataFetch(pCur, &amt); - if( zBuf && amt>=n ){ - assert( nData0 ) nData = n; - memcpy(zStatic, zBuf, (int)nData); - zStatic[nData] = 0; - Tcl_AppendResult(interp, zStatic, 0); - } - sqlite3BtreeLeave(pCur->pBtree); - return TCL_OK; -} - /* ** Usage: btree_payload_size ID ** ** Return the number of bytes of payload */ @@ -1189,175 +406,25 @@ " ID\"", 0); return TCL_ERROR; } pCur = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pCur->pBtree); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ + + /* The cursor may be in "require-seek" state. If this is the case, the + ** call to BtreeDataSize() will fix it. */ + sqlite3BtreeDataSize(pCur, (u32*)&n2); + if( pCur->apPage[pCur->iPage]->intKey ){ n1 = 0; }else{ sqlite3BtreeKeySize(pCur, (i64*)&n1); } - sqlite3BtreeDataSize(pCur, (u32*)&n2); sqlite3BtreeLeave(pCur->pBtree); sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2)); Tcl_AppendResult(interp, zBuf, 0); return SQLITE_OK; } -/* -** Usage: btree_cursor_info ID ?UP-CNT? -** -** Return integers containing information about the entry the -** cursor is pointing to: -** -** aResult[0] = The page number -** aResult[1] = The entry number -** aResult[2] = Total number of entries on this page -** aResult[3] = Cell size (local payload + header) -** aResult[4] = Number of free bytes on this page -** aResult[5] = Number of free blocks on the page -** aResult[6] = Total payload size (local + overflow) -** aResult[7] = Header size in bytes -** aResult[8] = Local payload size -** aResult[9] = Parent page number -** aResult[10]= Page number of the first overflow page -*/ -static int btree_cursor_info( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int i, j; - int up; - int aResult[11]; - char zBuf[400]; - - if( argc!=2 && argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID ?UP-CNT?\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - if( argc==3 ){ - if( Tcl_GetInt(interp, argv[2], &up) ) return TCL_ERROR; - }else{ - up = 0; - } - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreeCursorInfo(pCur, aResult, up); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - sqlite3BtreeLeave(pCur->pBtree); - return TCL_ERROR; - } - j = 0; - for(i=0; ipBtree); - Tcl_AppendResult(interp, &zBuf[1], 0); - return SQLITE_OK; -} - -/* -** Copied from btree.c: -*/ -static u32 t4Get4byte(unsigned char *p){ - return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; -} - -/* -** btree_ovfl_info BTREE CURSOR -** -** Given a cursor, return the sequence of pages number that form the -** overflow pages for the data of the entry that the cursor is point -** to. -*/ -static int btree_ovfl_info( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - BtCursor *pCur; - Pager *pPager; - int rc; - int n; - int dataSize; - u32 pgno; - void *pPage; - int aResult[11]; - char zElem[100]; - Tcl_DString str; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " BTREE CURSOR", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - pCur = sqlite3TestTextToPtr(argv[2]); - if( (*(void**)pCur) != (void*)pBt ){ - Tcl_AppendResult(interp, "Cursor ", argv[2], " does not belong to btree ", - argv[1], 0); - return TCL_ERROR; - } - sqlite3BtreeEnter(pBt); - pPager = sqlite3BtreePager(pBt); - rc = sqlite3BtreeCursorInfo(pCur, aResult, 0); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - sqlite3BtreeLeave(pBt); - return TCL_ERROR; - } - dataSize = pBt->pBt->usableSize; - Tcl_DStringInit(&str); - n = aResult[6] - aResult[8]; - n = (n + dataSize - 1)/dataSize; - pgno = (u32)aResult[10]; - while( pgno && n-- ){ - DbPage *pDbPage; - sprintf(zElem, "%d", pgno); - Tcl_DStringAppendElement(&str, zElem); - if( sqlite3PagerGet(pPager, pgno, &pDbPage)!=SQLITE_OK ){ - Tcl_DStringFree(&str); - Tcl_AppendResult(interp, "unable to get page ", zElem, 0); - sqlite3BtreeLeave(pBt); - return TCL_ERROR; - } - pPage = sqlite3PagerGetData(pDbPage); - pgno = t4Get4byte((unsigned char*)pPage); - sqlite3PagerUnref(pDbPage); - } - sqlite3BtreeLeave(pBt); - Tcl_DStringResult(interp, &str); - return SQLITE_OK; -} - -/* -** The command is provided for the purpose of setting breakpoints. -** in regression test scripts. -** -** By setting a GDB breakpoint on this procedure and executing the -** btree_breakpoint command in a test script, we can stop GDB at -** the point in the script where the btree_breakpoint command is -** inserted. This is useful for debugging. -*/ -static int btree_breakpoint( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - return TCL_OK; -} - /* ** usage: varint_test START MULTIPLIER COUNT INCREMENT ** ** This command tests the putVarint() and getVarint() ** routines, both for accuracy and for speed. @@ -1482,42 +549,10 @@ sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt); Tcl_SetResult(interp, zBuf, TCL_VOLATILE); return TCL_OK; } - -/* -** usage: btree_set_cache_size ID NCACHE -** -** Set the size of the cache used by btree $ID. -*/ -static int btree_set_cache_size( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - int nCache; - Btree *pBt; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " BT NCACHE\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; - - sqlite3_mutex_enter(pBt->db->mutex); - sqlite3BtreeEnter(pBt); - sqlite3BtreeSetCacheSize(pBt, nCache); - sqlite3BtreeLeave(pBt); - sqlite3_mutex_leave(pBt->db->mutex); - - return TCL_OK; -} - /* ** Usage: btree_ismemdb ID ** ** Return true if the B-Tree is in-memory. */ @@ -1543,10 +578,41 @@ sqlite3_mutex_leave(pBt->db->mutex); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res)); return SQLITE_OK; } +/* +** usage: btree_set_cache_size ID NCACHE +** +** Set the size of the cache used by btree $ID. +*/ +static int btree_set_cache_size( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + int nCache; + Btree *pBt; + + if( argc!=3 ){ + Tcl_AppendResult( + interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0); + return TCL_ERROR; + } + pBt = sqlite3TestTextToPtr(argv[1]); + if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; + + sqlite3_mutex_enter(pBt->db->mutex); + sqlite3BtreeEnter(pBt); + sqlite3BtreeSetCacheSize(pBt, nCache); + sqlite3BtreeLeave(pBt); + sqlite3_mutex_leave(pBt->db->mutex); + return TCL_OK; +} + + /* ** Register commands with the TCL interpreter. */ int Sqlitetest3_Init(Tcl_Interp *interp){ @@ -1555,54 +621,25 @@ Tcl_CmdProc *xProc; } aCmd[] = { { "btree_open", (Tcl_CmdProc*)btree_open }, { "btree_close", (Tcl_CmdProc*)btree_close }, { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction }, - { "btree_commit", (Tcl_CmdProc*)btree_commit }, - { "btree_rollback", (Tcl_CmdProc*)btree_rollback }, - { "btree_create_table", (Tcl_CmdProc*)btree_create_table }, - { "btree_drop_table", (Tcl_CmdProc*)btree_drop_table }, - { "btree_clear_table", (Tcl_CmdProc*)btree_clear_table }, - { "btree_get_meta", (Tcl_CmdProc*)btree_get_meta }, - { "btree_update_meta", (Tcl_CmdProc*)btree_update_meta }, { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats }, { "btree_cursor", (Tcl_CmdProc*)btree_cursor }, { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor }, - { "btree_move_to", (Tcl_CmdProc*)btree_move_to }, - { "btree_delete", (Tcl_CmdProc*)btree_delete }, { "btree_next", (Tcl_CmdProc*)btree_next }, - { "btree_prev", (Tcl_CmdProc*)btree_prev }, { "btree_eof", (Tcl_CmdProc*)btree_eof }, - { "btree_keysize", (Tcl_CmdProc*)btree_keysize }, - { "btree_key", (Tcl_CmdProc*)btree_key }, - { "btree_data", (Tcl_CmdProc*)btree_data }, - { "btree_fetch_key", (Tcl_CmdProc*)btree_fetch_key }, - { "btree_fetch_data", (Tcl_CmdProc*)btree_fetch_data }, { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, { "btree_first", (Tcl_CmdProc*)btree_first }, - { "btree_last", (Tcl_CmdProc*)btree_last }, - { "btree_integrity_check", (Tcl_CmdProc*)btree_integrity_check }, - { "btree_breakpoint", (Tcl_CmdProc*)btree_breakpoint }, { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, - { "btree_begin_statement", (Tcl_CmdProc*)btree_begin_statement }, - { "btree_commit_statement", (Tcl_CmdProc*)btree_commit_statement }, - { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement }, { "btree_from_db", (Tcl_CmdProc*)btree_from_db }, - { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size }, - { "btree_cursor_info", (Tcl_CmdProc*)btree_cursor_info }, - { "btree_ovfl_info", (Tcl_CmdProc*)btree_ovfl_info }, - { "btree_cursor_list", (Tcl_CmdProc*)btree_cursor_list }, { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb }, + { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size } }; int i; for(i=0; i /* @@ -60,83 +60,5 @@ (pCur->eState==CURSOR_VALID) ? "" : " eof" ); } #endif } - - -/* -** Fill aResult[] with information about the entry and page that the -** cursor is pointing to. -** -** aResult[0] = The page number -** aResult[1] = The entry number -** aResult[2] = Total number of entries on this page -** aResult[3] = Cell size (local payload + header) -** aResult[4] = Number of free bytes on this page -** aResult[5] = Number of free blocks on the page -** aResult[6] = Total payload size (local + overflow) -** aResult[7] = Header size in bytes -** aResult[8] = Local payload size -** aResult[9] = Parent page number -** aResult[10]= Page number of the first overflow page -** -** This routine is used for testing and debugging only. -*/ -int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){ -#if 0 - int cnt, idx; - MemPage *pPage = pCur->apPage[pCur->iPage]; - BtCursor tmpCur; - int rc; - - if( pCur->eState==CURSOR_REQUIRESEEK ){ - rc = sqlite3BtreeRestoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - } - - assert( pPage->isInit ); - sqlite3BtreeGetTempCursor(pCur, &tmpCur); - while( upCnt-- ){ - sqlite3BtreeMoveToParent(&tmpCur); - } - pPage = tmpCur.pPage; - aResult[0] = sqlite3PagerPagenumber(pPage->pDbPage); - assert( aResult[0]==pPage->pgno ); - aResult[1] = tmpCur.idx; - aResult[2] = pPage->nCell; - if( tmpCur.idx>=0 && tmpCur.idxnCell ){ - sqlite3BtreeParseCell(tmpCur.pPage, tmpCur.idx, &tmpCur.info); - aResult[3] = tmpCur.info.nSize; - aResult[6] = tmpCur.info.nData; - aResult[7] = tmpCur.info.nHeader; - aResult[8] = tmpCur.info.nLocal; - }else{ - aResult[3] = 0; - aResult[6] = 0; - aResult[7] = 0; - aResult[8] = 0; - } - aResult[4] = pPage->nFree; - cnt = 0; - idx = get2byte(&pPage->aData[pPage->hdrOffset+1]); - while( idx>0 && idxpBt->usableSize ){ - cnt++; - idx = get2byte(&pPage->aData[idx]); - } - aResult[5] = cnt; - if( pPage->pParent==0 || sqlite3BtreeIsRootPage(pPage) ){ - aResult[9] = 0; - }else{ - aResult[9] = pPage->pParent->pgno; - } - if( tmpCur.info.iOverflow ){ - aResult[10] = get4byte(&tmpCur.info.pCell[tmpCur.info.iOverflow]); - }else{ - aResult[10] = 0; - } - sqlite3BtreeReleaseTempCursor(&tmpCur); -#endif - return SQLITE_OK; -} Index: src/test_journal.c ================================================================== --- src/test_journal.c +++ src/test_journal.c @@ -13,11 +13,11 @@ ** This file contains code for a VFS layer that acts as a wrapper around ** an existing VFS. The code in this file attempts to verify that SQLite ** correctly populates and syncs a journal file before writing to a ** corresponding database file. ** -** $Id: test_journal.c,v 1.15 2009/04/07 11:21:29 danielk1977 Exp $ +** $Id: test_journal.c,v 1.17 2009/06/26 10:39:36 danielk1977 Exp $ */ #if SQLITE_TEST /* This file is used for testing only */ #include "sqlite3.h" #include "sqliteInt.h" @@ -216,16 +216,20 @@ static void leaveJtMutex(void){ sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG)); } extern int sqlite3_io_error_pending; -static void stop_ioerr_simulation(int *piSave){ +extern int sqlite3_io_error_hit; +static void stop_ioerr_simulation(int *piSave, int *piSave2){ *piSave = sqlite3_io_error_pending; + *piSave2 = sqlite3_io_error_hit; sqlite3_io_error_pending = -1; + sqlite3_io_error_hit = 0; } -static void start_ioerr_simulation(int iSave){ +static void start_ioerr_simulation(int iSave, int iSave2){ sqlite3_io_error_pending = iSave; + sqlite3_io_error_hit = iSave2; } /* ** The jt_file pointed to by the argument may or may not be a file-handle ** open on a main database file. If it is, and a transaction is currently @@ -364,12 +368,13 @@ if( !pMain->pWritable || !pMain->aCksum || !aData ){ rc = SQLITE_IOERR_NOMEM; }else if( pMain->nPage>0 ){ u32 iTrunk; int iSave; + int iSave2; - stop_ioerr_simulation(&iSave); + stop_ioerr_simulation(&iSave, &iSave2); /* Read the database free-list. Add the page-number for each free-list ** leaf to the jt_file.pWritable bitvec. */ rc = sqlite3OsRead(p, aData, pMain->nPagesize, 0); @@ -396,92 +401,17 @@ rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff); pMain->aCksum[ii] = genCksum(aData, pMain->nPagesize); } } - start_ioerr_simulation(iSave); + start_ioerr_simulation(iSave, iSave2); } sqlite3_free(aData); return rc; } -/* -** Write data to an jt-file. -*/ -static int jtWrite( - sqlite3_file *pFile, - const void *zBuf, - int iAmt, - sqlite_int64 iOfst -){ - jt_file *p = (jt_file *)pFile; - if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){ - if( iOfst==0 ){ - jt_file *pMain = locateDatabaseHandle(p->zName); - assert( pMain ); - - if( decodeJournalHdr(zBuf, 0, &pMain->nPage, 0, &pMain->nPagesize) ){ - /* Zeroing the first journal-file header. This is the end of a - ** transaction. */ - closeTransaction(pMain); - }else{ - /* Writing the first journal header to a journal file. This happens - ** when a transaction is first started. */ - int rc; - if( SQLITE_OK!=(rc=openTransaction(pMain, p)) ){ - return rc; - } - } - } - if( p->iMaxOff<(iOfst + iAmt) ){ - p->iMaxOff = iOfst + iAmt; - } - } - - if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){ - if( iAmtnPagesize - && p->nPagesize%iAmt==0 - && iOfst>=(PENDING_BYTE+512) - && iOfst+iAmt<=PENDING_BYTE+p->nPagesize - ){ - /* No-op. This special case is hit when the backup code is copying a - ** to a database with a larger page-size than the source database and - ** it needs to fill in the non-locking-region part of the original - ** pending-byte page. - */ - }else{ - u32 pgno = iOfst/p->nPagesize + 1; - assert( (iAmt==1||iAmt==p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 ); - assert( pgno<=p->nPage || p->nSync>0 ); - assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) ); - } - } - - return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); -} - -/* -** Truncate an jt-file. -*/ -static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){ - jt_file *p = (jt_file *)pFile; - if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){ - /* Truncating a journal file. This is the end of a transaction. */ - jt_file *pMain = locateDatabaseHandle(p->zName); - closeTransaction(pMain); - } - if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){ - u32 pgno; - u32 locking_page = (u32)(PENDING_BYTE/p->nPagesize+1); - for(pgno=size/p->nPagesize+1; pgno<=p->nPage; pgno++){ - assert( pgno==locking_page || sqlite3BitvecTest(p->pWritable, pgno) ); - } - } - return sqlite3OsTruncate(p->pReal, size); -} - /* ** The first argument to this function is a handle open on a journal file. ** This function reads the journal file and adds the page number for each ** page in the journal to the Bitvec object passed as the second argument. */ @@ -491,17 +421,18 @@ sqlite3_file *pReal = p->pReal; sqlite3_int64 iOff = 0; sqlite3_int64 iSize = p->iMaxOff; unsigned char *aPage; int iSave; + int iSave2; aPage = sqlite3_malloc(pMain->nPagesize); if( !aPage ){ return SQLITE_IOERR_NOMEM; } - stop_ioerr_simulation(&iSave); + stop_ioerr_simulation(&iSave, &iSave2); while( rc==SQLITE_OK && iOffflags&SQLITE_OPEN_MAIN_JOURNAL ){ + if( iOfst==0 ){ + jt_file *pMain = locateDatabaseHandle(p->zName); + assert( pMain ); + + if( iAmt==28 ){ + /* Zeroing the first journal-file header. This is the end of a + ** transaction. */ + closeTransaction(pMain); + }else if( iAmt!=12 ){ + /* Writing the first journal header to a journal file. This happens + ** when a transaction is first started. */ + u8 *z = (u8 *)zBuf; + pMain->nPage = decodeUint32(&z[16]); + pMain->nPagesize = decodeUint32(&z[24]); + if( SQLITE_OK!=(rc=openTransaction(pMain, p)) ){ + return rc; + } + } + } + if( p->iMaxOff<(iOfst + iAmt) ){ + p->iMaxOff = iOfst + iAmt; + } + } + + if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){ + if( iAmtnPagesize + && p->nPagesize%iAmt==0 + && iOfst>=(PENDING_BYTE+512) + && iOfst+iAmt<=PENDING_BYTE+p->nPagesize + ){ + /* No-op. This special case is hit when the backup code is copying a + ** to a database with a larger page-size than the source database and + ** it needs to fill in the non-locking-region part of the original + ** pending-byte page. + */ + }else{ + u32 pgno = iOfst/p->nPagesize + 1; + assert( (iAmt==1||iAmt==p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 ); + assert( pgno<=p->nPage || p->nSync>0 ); + assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) ); + } + } + + rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); + if( (p->flags&SQLITE_OPEN_MAIN_JOURNAL) && iAmt==12 ){ + jt_file *pMain = locateDatabaseHandle(p->zName); + int rc2 = readJournalFile(p, pMain); + if( rc==SQLITE_OK ) rc = rc2; + } + return rc; +} + +/* +** Truncate an jt-file. +*/ +static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){ + jt_file *p = (jt_file *)pFile; + if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){ + /* Truncating a journal file. This is the end of a transaction. */ + jt_file *pMain = locateDatabaseHandle(p->zName); + closeTransaction(pMain); + } + if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){ + u32 pgno; + u32 locking_page = (u32)(PENDING_BYTE/p->nPagesize+1); + for(pgno=size/p->nPagesize+1; pgno<=p->nPage; pgno++){ + assert( pgno==locking_page || sqlite3BitvecTest(p->pWritable, pgno) ); + } + } + return sqlite3OsTruncate(p->pReal, size); +} + /* ** Sync an jt-file. */ static int jtSync(sqlite3_file *pFile, int flags){ jt_file *p = (jt_file *)pFile; Index: src/test_malloc.c ================================================================== --- src/test_malloc.c +++ src/test_malloc.c @@ -11,11 +11,11 @@ ************************************************************************* ** ** This file contains code used to implement test interfaces to the ** memory allocation subsystem. ** -** $Id: test_malloc.c,v 1.54 2009/04/07 11:21:29 danielk1977 Exp $ +** $Id: test_malloc.c,v 1.55 2009/07/01 18:09:02 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include @@ -1339,10 +1339,28 @@ } rc = faultsimInstall(isInstall); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); return TCL_OK; } + +/* +** sqlite3_install_memsys3 +*/ +static int test_install_memsys3( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc = SQLITE_MISUSE; +#ifdef SQLITE_ENABLE_MEMSYS3 + const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); + rc = sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetMemsys3()); +#endif + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + return TCL_OK; +} /* ** Register commands with the TCL interpreter. */ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ @@ -1376,14 +1394,15 @@ { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, + { "sqlite3_install_memsys3", test_install_memsys3 ,0 }, }; int i; for(i=0; i /* @@ -408,11 +408,11 @@ mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->activeVdbeCnt==0 ){ db->u1.isInterrupted = 0; } pParse->rc = SQLITE_OK; - pParse->zTail = pParse->zSql = zSql; + pParse->zTail = zSql; i = 0; assert( pzErrMsg!=0 ); pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc); if( pEngine==0 ){ db->mallocFailed = 1; @@ -516,10 +516,15 @@ } sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->apVarExpr); sqlite3DbFree(db, pParse->aAlias); + while( pParse->pAinc ){ + AutoincInfo *p = pParse->pAinc; + pParse->pAinc = p->pNext; + sqlite3DbFree(db, p); + } while( pParse->pZombieTab ){ Table *p = pParse->pZombieTab; pParse->pZombieTab = p->pNextZombie; sqlite3DeleteTable(p); } Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.202 2009/05/28 01:00:55 drh Exp $ +** $Id: update.c,v 1.204 2009/06/27 11:17:35 drh Exp $ */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_VIRTUALTABLE /* Forward declaration */ @@ -561,10 +561,18 @@ sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); if( pTrigger ){ sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0); sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0); } + + /* Update the sqlite_sequence table by storing the content of the + ** maximum rowid counter values recorded while inserting into + ** autoincrement tables. + */ + if( pParse->nested==0 && pParse->trigStack==0 ){ + sqlite3AutoincrementEnd(pParse); + } /* ** Return the number of rows that were changed. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. @@ -659,21 +667,20 @@ sqlite3Select(pParse, pSelect, &dest); /* Generate code to scan the ephemeral table and call VUpdate. */ iReg = ++pParse->nMem; pParse->nMem += pTab->nCol+1; - sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); - addr = sqlite3VdbeCurrentAddr(v); + addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1); for(i=0; inCol; i++){ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); } sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVtab, P4_VTAB); - sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr); - sqlite3VdbeJumpHere(v, addr-1); + sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); + sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); /* Cleanup */ sqlite3SelectDelete(db, pSelect); } Index: src/util.c ================================================================== --- src/util.c +++ src/util.c @@ -12,11 +12,11 @@ ** Utility functions used throughout sqlite. ** ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.260 2009/06/17 16:20:04 drh Exp $ +** $Id: util.c,v 1.261 2009/06/24 10:26:33 drh Exp $ */ #include "sqliteInt.h" #include #ifdef SQLITE_HAVE_ISNAN # include @@ -28,31 +28,10 @@ #ifdef SQLITE_COVERAGE_TEST void sqlite3Coverage(int x){ static int dummy = 0; dummy += x; } -#endif - -/* -** Routine needed to support the ALWAYS() and NEVER() macros. -** -** The argument to ALWAYS() should always be true and the argument -** to NEVER() should always be false. If either is not the case -** then this routine is called in order to throw an error. -** -** This routine only exists if assert() is operational. It always -** throws an assert on its first invocation. The variable has a long -** name to help the assert() message be more readable. The variable -** is used to prevent a too-clever optimizer from optimizing out the -** entire call. -*/ -#ifndef NDEBUG -int sqlite3Assert(void){ - static volatile int ALWAYS_was_false_or_NEVER_was_true = 0; - assert( ALWAYS_was_false_or_NEVER_was_true ); /* Always fails */ - return ALWAYS_was_false_or_NEVER_was_true++; /* Not Reached */ -} #endif /* ** Return true if the floating point value is Not a Number (NaN). ** Index: src/vacuum.c ================================================================== --- src/vacuum.c +++ src/vacuum.c @@ -12,11 +12,11 @@ ** This file contains code used to implement the VACUUM command. ** ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** -** $Id: vacuum.c,v 1.90 2009/06/03 11:25:07 danielk1977 Exp $ +** $Id: vacuum.c,v 1.91 2009/07/02 07:47:33 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) @@ -252,12 +252,11 @@ /* Copy Btree meta values */ for(i=0; irc = pOp->p1; p->pc = pc; - p->errorAction = pOp->p2; + p->errorAction = (u8)pOp->p2; if( pOp->p4.z ){ sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); } rc = sqlite3VdbeHalt(p); assert( rc==SQLITE_BUSY || rc==SQLITE_OK ); @@ -2023,10 +2023,11 @@ memset(&sMem, 0, sizeof(sMem)); assert( p1nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &p->aMem[pOp->p3]; MemSetTypeFlag(pDest, MEM_Null); + zRec = 0; /* This block sets the variable payloadSize to be the total number of ** bytes in the record. ** ** zRec is set to be the complete text of the record if it is available. @@ -2041,16 +2042,15 @@ pC = p->apCsr[p1]; assert( pC!=0 ); #ifndef SQLITE_OMIT_VIRTUALTABLE assert( pC->pVtabCursor==0 ); #endif - if( pC->pCursor!=0 ){ + pCrsr = pC->pCursor; + if( pCrsr!=0 ){ /* The record is stored in a B-Tree */ rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; - zRec = 0; - pCrsr = pC->pCursor; if( pC->nullRow ){ payloadSize = 0; }else if( pC->cacheStatus==p->cacheCtr ){ payloadSize = pC->payloadSize; zRec = (char*)pC->aRow; @@ -2062,19 +2062,16 @@ assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); payloadSize = (u32)payloadSize64; }else{ sqlite3BtreeDataSize(pCrsr, &payloadSize); } - nField = pC->nField; }else if( pC->pseudoTable ){ /* The record is the sole entry of a pseudo-table */ payloadSize = pC->nData; zRec = pC->pData; pC->cacheStatus = CACHE_STALE; assert( payloadSize==0 || zRec!=0 ); - nField = pC->nField; - pCrsr = 0; }else{ /* Consider the row to be NULL */ payloadSize = 0; } @@ -2086,10 +2083,11 @@ assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 ); if( payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } + nField = pC->nField; assert( p2p3=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); assert( (p->btreeMask & (1<aDb[iDb].pBt, iCookie, (u32 *)&iMeta); + sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); pOut->u.i = iMeta; MemSetTypeFlag(pOut, MEM_Int); break; } @@ -2824,16 +2822,15 @@ Btree *pBt; assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (1<p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ - rc = sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); }else{ - rc = SQLITE_OK; iMeta = 0; } - if( rc==SQLITE_OK && iMeta!=pOp->p2 ){ + if( iMeta!=pOp->p2 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie ** stored with the in-memory representation of the schema, do ** not reload the schema from the database file. @@ -2914,11 +2911,10 @@ int iDb; int wrFlag; Btree *pX; VdbeCursor *pCur; Db *pDb; - int flags; nField = 0; pKeyInfo = 0; p2 = pOp->p2; iDb = pOp->p3; @@ -2962,49 +2958,26 @@ if( pCur==0 ) goto no_mem; pCur->nullRow = 1; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); pCur->pKeyInfo = pKeyInfo; - switch( rc ){ - case SQLITE_OK: { - flags = sqlite3BtreeFlags(pCur->pCursor); - - /* Sanity checking. Only the lower four bits of the flags byte should - ** be used. Bit 3 (mask 0x08) is unpredictable. The lower 3 bits - ** (mask 0x07) should be either 5 (intkey+leafdata for tables) or - ** 2 (zerodata for indices). If these conditions are not met it can - ** only mean that we are dealing with a corrupt database file. - ** Note: All of the above is checked already in sqlite3BtreeCursor(). - */ - assert( (flags & 0xf0)==0 ); - assert( (flags & 0x07)==5 || (flags & 0x07)==2 ); - - pCur->isTable = (flags & BTREE_INTKEY)!=0 ?1:0; - pCur->isIndex = (flags & BTREE_ZERODATA)!=0 ?1:0; - /* If P4==0 it means we are expected to open a table. If P4!=0 then - ** we expect to be opening an index. If this is not what happened, - ** then the database is corrupt - */ - if( (pCur->isTable && pOp->p4type==P4_KEYINFO) - || (pCur->isIndex && pOp->p4type!=P4_KEYINFO) ){ - rc = SQLITE_CORRUPT_BKPT; - goto abort_due_to_error; - } - break; - } - case SQLITE_EMPTY: { - pCur->isTable = pOp->p4type!=P4_KEYINFO; - pCur->isIndex = !pCur->isTable; - pCur->pCursor = 0; - rc = SQLITE_OK; - break; - } - default: { - assert( rc!=SQLITE_BUSY ); /* Busy conditions detected earlier */ - goto abort_due_to_error; - } - } + /* Since it performs no memory allocation or IO, the only values that + ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. + ** SQLITE_EMPTY is only returned when attempting to open the table + ** rooted at page 1 of a zero-byte database. */ + assert( rc==SQLITE_EMPTY || rc==SQLITE_OK ); + if( rc==SQLITE_EMPTY ){ + pCur->pCursor = 0; + rc = SQLITE_OK; + } + + /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of + ** SQLite used to check if the root-page flags were sane at this point + ** and report database corruption if they were not, but this check has + ** since moved into the btree layer. */ + pCur->isTable = pOp->p4type!=P4_KEYINFO; + pCur->isIndex = !pCur->isTable; break; } /* Opcode: OpenEphemeral P1 P2 * P4 * ** @@ -3647,11 +3620,11 @@ sqlite3BtreeSetCachedRowid(pC->pCursor, vuseRandomRowid ){ assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is ** an AUTOINCREMENT table. */ - v = db->priorNewRowid; + v = db->lastRowid; cnt = 0; do{ if( cnt==0 && (v&0xffffff)==v ){ v++; }else{ @@ -3659,11 +3632,10 @@ if( cnt<5 ) v &= 0xffffff; } rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v, 0, &res); cnt++; }while( cnt<100 && rc==SQLITE_OK && res==0 ); - db->priorNewRowid = v; if( rc==SQLITE_OK && res==0 ){ rc = SQLITE_FULL; goto abort_due_to_error; } } @@ -4496,11 +4468,11 @@ ** 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 || ALWAYS(DbHasProperty(db, iDb, DB_SchemaLoaded)) ){ + if( pOp->p2 || DbHasProperty(db, iDb, DB_SchemaLoaded) ){ zMaster = SCHEMA_TABLE(iDb); initData.db = db; initData.iDb = pOp->p1; initData.pzErrMsg = &p->zErrMsg; zSql = sqlite3MPrintf(db, @@ -4922,11 +4894,11 @@ Mem *pMem; assert( pOp->p1>0 && pOp->p1<=p->nMem ); pMem = &p->aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); - if( rc==SQLITE_ERROR ){ + if( rc ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem)); } sqlite3VdbeChangeEncoding(pMem, encoding); UPDATE_MAX_BLOBSIZE(pMem); if( sqlite3VdbeMemTooBig(pMem) ){ @@ -4995,32 +4967,31 @@ /* Opcode: TableLock P1 P2 P3 P4 * ** ** Obtain a lock on a particular table. This instruction is only used when ** the shared-cache feature is enabled. ** -** If P1 is the index of the database in sqlite3.aDb[] of the database +** P1 is the index of the database in sqlite3.aDb[] of the database ** on which the lock is acquired. A readlock is obtained if P3==0 or ** a write lock if P3==1. ** ** P2 contains the root-page of the table to lock. ** ** P4 contains a pointer to the name of the table being locked. This is only ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { - int p1; - u8 isWriteLock; - - p1 = pOp->p1; - isWriteLock = (u8)pOp->p3; - assert( p1>=0 && p1nDb ); - assert( (p->btreeMask & (1<aDb[p1].pBt, pOp->p2, isWriteLock); - if( (rc&0xFF)==SQLITE_LOCKED ){ - const char *z = pOp->p4.z; - sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); + u8 isWriteLock = (u8)pOp->p3; + if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ + int p1 = pOp->p1; + assert( p1>=0 && p1nDb ); + assert( (p->btreeMask & (1<aDb[p1].pBt, pOp->p2, isWriteLock); + if( (rc&0xFF)==SQLITE_LOCKED ){ + const char *z = pOp->p4.z; + sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); + } } break; } #endif /* SQLITE_OMIT_SHARED_CACHE */ @@ -5231,10 +5202,13 @@ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; + if( sContext.isError ){ + rc = sContext.isError; + } /* Copy the result of the function to the P3 register. We ** do this regardless of whether or not an error occurred to ensure any ** dynamic allocation in sContext.s (a Mem struct) is released. */ Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -11,11 +11,11 @@ ************************************************************************* ** ** This file contains code use to implement APIs that are part of the ** VDBE. ** -** $Id: vdbeapi.c,v 1.166 2009/06/19 14:06:03 drh Exp $ +** $Id: vdbeapi.c,v 1.167 2009/06/25 01:47:12 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" #ifndef SQLITE_OMIT_DEPRECATED @@ -153,20 +153,35 @@ } /**************************** sqlite3_result_ ******************************* ** The following routines are used by user-defined functions to specify ** the function result. +** +** The setStrOrError() funtion calls sqlite3VdbeMemSetStr() to store the +** result as a string or blob but if the string or blob is too large, it +** then sets the error code to SQLITE_TOOBIG */ +static void setResultStrOrError( + sqlite3_context *pCtx, /* Function context */ + const char *z, /* String pointer */ + int n, /* Bytes in string, or negative */ + u8 enc, /* Encoding of z. 0 for BLOBs */ + void (*xDel)(void*) /* Destructor function */ +){ + if( sqlite3VdbeMemSetStr(&pCtx->s, z, n, enc, xDel)==SQLITE_TOOBIG ){ + sqlite3_result_error_toobig(pCtx); + } +} void sqlite3_result_blob( sqlite3_context *pCtx, const void *z, int n, void (*xDel)(void *) ){ assert( n>=0 ); assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetStr(&pCtx->s, z, n, 0, xDel); + setResultStrOrError(pCtx, z, n, 0, xDel); } void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); sqlite3VdbeMemSetDouble(&pCtx->s, rVal); } @@ -199,39 +214,39 @@ const char *z, int n, void (*xDel)(void *) ){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, xDel); + setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); } #ifndef SQLITE_OMIT_UTF16 void sqlite3_result_text16( sqlite3_context *pCtx, const void *z, int n, void (*xDel)(void *) ){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, xDel); + setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); } void sqlite3_result_text16be( sqlite3_context *pCtx, const void *z, int n, void (*xDel)(void *) ){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16BE, xDel); + setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); } void sqlite3_result_text16le( sqlite3_context *pCtx, const void *z, int n, void (*xDel)(void *) ){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16LE, xDel); + setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); } #endif /* SQLITE_OMIT_UTF16 */ void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); sqlite3VdbeMemCopy(&pCtx->s, pValue); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -12,11 +12,11 @@ ** This file contains code used for creating, destroying, and populating ** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior ** 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.464 2009/06/23 14:15:04 drh Exp $ +** $Id: vdbeaux.c,v 1.470 2009/07/08 08:05:35 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -143,11 +143,11 @@ i = p->nOp; assert( p->magic==VDBE_MAGIC_INIT ); assert( op>0 && op<0xff ); if( p->nOpAlloc<=i ){ if( growOpArray(p) ){ - return 0; + return 1; } } p->nOp++; pOp = &p->aOp[i]; pOp->opcode = (u8)op; @@ -348,11 +348,11 @@ assert( p->magic==VDBE_MAGIC_INIT ); if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){ return 0; } addr = p->nOp; - if( nOp>0 ){ + if( ALWAYS(nOp>0) ){ int i; VdbeOpList const *pIn = aOp; for(i=0; ip2; VdbeOp *pOut = &p->aOp[i+addr]; @@ -384,44 +384,47 @@ ** This routine is useful when a large program is loaded from a ** static array using sqlite3VdbeAddOpList but we want to make a ** few minor changes to the program. */ void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){ - assert( p==0 || p->magic==VDBE_MAGIC_INIT ); - if( p && addr>=0 && p->nOp>addr && p->aOp ){ + assert( p!=0 ); + assert( addr>=0 ); + if( p->nOp>addr ){ p->aOp[addr].p1 = val; } } /* ** Change the value of the P2 operand for a specific instruction. ** This routine is useful for setting a jump destination. */ void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){ - assert( p==0 || p->magic==VDBE_MAGIC_INIT ); - if( p && addr>=0 && p->nOp>addr && p->aOp ){ + assert( p!=0 ); + assert( addr>=0 ); + if( p->nOp>addr ){ p->aOp[addr].p2 = val; } } /* ** Change the value of the P3 operand for a specific instruction. */ void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){ - assert( p==0 || p->magic==VDBE_MAGIC_INIT ); - if( p && addr>=0 && p->nOp>addr && p->aOp ){ + assert( p!=0 ); + assert( addr>=0 ); + if( p->nOp>addr ){ p->aOp[addr].p3 = val; } } /* ** Change the value of the P5 operand for the most recently ** added operation. */ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){ - assert( p==0 || p->magic==VDBE_MAGIC_INIT ); - if( p && p->aOp ){ + assert( p!=0 ); + if( p->aOp ){ assert( p->nOp>0 ); p->aOp[p->nOp-1].p5 = val; } } @@ -437,11 +440,11 @@ /* ** If the input FuncDef structure is ephemeral, then free it. If ** the FuncDef is not ephermal, then do nothing. */ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ - if( pDef && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){ + if( ALWAYS(pDef) && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){ sqlite3DbFree(db, pDef); } } /* @@ -482,11 +485,11 @@ /* ** Change N opcodes starting at addr to No-ops. */ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){ - if( p && p->aOp ){ + if( p->aOp ){ VdbeOp *pOp = &p->aOp[addr]; sqlite3 *db = p->db; while( N-- ){ freeP4(db, pOp->p4type, pOp->p4.p); memset(pOp, 0, sizeof(pOp[0])); @@ -531,14 +534,14 @@ if (n != P4_KEYINFO) { freeP4(db, n, (void*)*(char**)&zP4); } return; } + assert( p->nOp>0 ); assert( addrnOp ); if( addr<0 ){ addr = p->nOp - 1; - if( addr<0 ) return; } pOp = &p->aOp[addr]; freeP4(db, pOp->p4type, pOp->p4.p); pOp->p4.p = 0; if( n==P4_INT32 ){ @@ -864,11 +867,11 @@ int i; int rc = SQLITE_OK; Mem *pMem = p->pResultSet = &p->aMem[1]; assert( p->explain ); - if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE; + assert( p->magic==VDBE_MAGIC_RUN ); assert( db->magic==SQLITE_MAGIC_BUSY ); assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); /* Even though this opcode does not use dynamic strings for ** the result, result columns may become dynamic if the user calls @@ -1112,11 +1115,11 @@ /* 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( nVar>=0 && !db->mallocFailed ){ + if( nVar>=0 && ALWAYS(db->mallocFailed==0) ){ 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); @@ -1142,13 +1145,13 @@ } zCsr = p->pFree; zEnd = &zCsr[nByte]; }while( nByte && !db->mallocFailed ); - p->nCursor = nCursor; + p->nCursor = (u16)nCursor; if( p->aVar ){ - p->nVar = nVar; + p->nVar = (u16)nVar; for(n=0; naVar[n].flags = MEM_Null; p->aVar[n].db = db; } } @@ -1217,19 +1220,18 @@ sqlite3DbFree(p->db, pCx->pData); } } /* -** Close all cursors except for VTab cursors that are currently -** in use. +** Close all cursors. */ -static void closeAllCursorsExceptActiveVtabs(Vdbe *p){ +static void closeAllCursors(Vdbe *p){ int i; if( p->apCsr==0 ) return; for(i=0; inCursor; i++){ VdbeCursor *pC = p->apCsr[i]; - if( pC && (!p->inVtabMethod || !pC->pVtabCursor) ){ + if( pC ){ sqlite3VdbeFreeCursor(p, pC); p->apCsr[i] = 0; } } } @@ -1243,11 +1245,11 @@ */ static void Cleanup(Vdbe *p){ int i; sqlite3 *db = p->db; Mem *pMem; - closeAllCursorsExceptActiveVtabs(p); + closeAllCursors(p); for(pMem=&p->aMem[1], i=1; i<=p->nMem; i++, pMem++){ if( pMem->flags & MEM_RowSet ){ sqlite3RowSetClear(pMem->u.pRowSet); } MemSetTypeFlag(pMem, MEM_Null); @@ -1276,11 +1278,11 @@ sqlite3 *db = p->db; releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); sqlite3DbFree(db, p->aColName); n = nResColumn*COLNAME_N; - p->nResColumn = nResColumn; + p->nResColumn = (u16)nResColumn; p->aColName = pColName = (Mem*)sqlite3DbMallocZero(db, sizeof(Mem)*n ); if( p->aColName==0 ) return; while( n-- > 0 ){ pColName->flags = MEM_Null; pColName->db = p->db; @@ -1329,10 +1331,17 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ int i; int nTrans = 0; /* Number of databases with an active write-transaction */ int rc = SQLITE_OK; int needXcommit = 0; + +#ifdef SQLITE_OMIT_VIRTUALTABLE + /* With this option, sqlite3VtabSync() is defined to be simply + ** SQLITE_OK so p is not used. + */ + UNUSED_PARAMETER(p); +#endif /* Before doing anything else, call the xSync() callback for any ** virtual module tables written in this transaction. This has to ** be done before determining whether a master journal file is ** required, as an xSync() callback may add an attached database @@ -1605,10 +1614,15 @@ ** Otherwise SQLITE_OK. */ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ sqlite3 *const db = p->db; int rc = SQLITE_OK; + + /* If p->iStatement is greater than zero, then this Vdbe opened a + ** statement transaction that should be closed here. The only exception + ** is that an IO error may have occured, causing an emergency rollback. + ** In this case (db->nStatement==0), and there is nothing to do. */ if( p->iStatement && db->nStatement ){ int i; const int iSavepoint = p->iStatement-1; assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); @@ -1697,11 +1711,11 @@ */ if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } - closeAllCursorsExceptActiveVtabs(p); + closeAllCursors(p); if( p->magic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } checkActiveVdbeCnt(db); @@ -1714,22 +1728,19 @@ /* Lock all btrees used by the statement */ sqlite3VdbeMutexArrayEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; + assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; if( isSpecialError ){ /* If the query was read-only, we need do no rollback at all. Otherwise, ** proceed with the special handling. */ if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ - if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){ - eStatementOp = SAVEPOINT_ROLLBACK; - p->rc = SQLITE_BUSY; - }else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) - && p->usesStmtJournal ){ + if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ 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. */ @@ -1947,12 +1958,10 @@ int sqlite3VdbeFinalize(Vdbe *p){ int rc = SQLITE_OK; if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ rc = sqlite3VdbeReset(p); assert( (rc & p->db->errMask)==rc ); - }else if( p->magic!=VDBE_MAGIC_INIT ){ - return SQLITE_MISUSE; } sqlite3VdbeDelete(p); return rc; } @@ -1980,11 +1989,11 @@ */ void sqlite3VdbeDelete(Vdbe *p){ int i; sqlite3 *db; - if( p==0 ) return; + if( NEVER(p==0) ) return; db = p->db; if( p->pPrev ){ p->pPrev->pNext = p->pNext; }else{ assert( db->pVdbe==p ); @@ -2436,26 +2445,25 @@ p->nField = u; return (void*)p; } /* -** This routine destroys a UnpackedRecord object +** This routine destroys a UnpackedRecord object. */ void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){ - if( p ){ - if( p->flags & UNPACKED_NEED_DESTROY ){ - int i; - Mem *pMem; - for(i=0, pMem=p->aMem; inField; i++, pMem++){ - if( pMem->zMalloc ){ - sqlite3VdbeMemRelease(pMem); - } - } - } - if( p->flags & UNPACKED_NEED_FREE ){ - sqlite3DbFree(p->pKeyInfo->db, p); - } + int i; + Mem *pMem; + + assert( p!=0 ); + assert( p->flags & UNPACKED_NEED_DESTROY ); + for(i=0, pMem=p->aMem; inField; i++, pMem++){ + if( pMem->zMalloc ){ + sqlite3VdbeMemRelease(pMem); + } + } + if( p->flags & UNPACKED_NEED_FREE ){ + sqlite3DbFree(p->pKeyInfo->db, p); } } /* ** This function compares the two table rows or index records @@ -2584,15 +2592,15 @@ u32 typeRowid; /* Serial type of the rowid */ u32 lenRowid; /* Size of the rowid */ Mem m, v; /* Get the size of the index entry. Only indices entries of less - ** than 2GiB are support - anything large must be database corruption */ + ** than 2GiB are support - anything large must be database corruption. + ** Any corruption is detected in sqlite3BtreeParseCellPtr(), though, so + ** this code can safely assume that nCellKey is 32-bits */ sqlite3BtreeKeySize(pCur, &nCellKey); - if( unlikely(nCellKey<=0 || nCellKey>0x7fffffff) ){ - return SQLITE_CORRUPT_BKPT; - } + assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); /* Read in the complete content of the index entry */ m.flags = 0; m.db = db; m.zMalloc = 0; @@ -2601,13 +2609,13 @@ return rc; } /* The index entry must begin with a header size */ (void)getVarint32((u8*)m.z, szHdr); - testcase( szHdr==2 ); + testcase( szHdr==3 ); testcase( szHdr==m.n ); - if( unlikely(szHdr<2 || (int)szHdr>m.n) ){ + if( unlikely(szHdr<3 || (int)szHdr>m.n) ){ goto idx_rowid_corruption; } /* The last field of the index should be an integer - the ROWID. ** Verify that the last entry really is an integer. */ @@ -2622,12 +2630,12 @@ testcase( typeRowid==9 ); if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){ goto idx_rowid_corruption; } lenRowid = sqlite3VdbeSerialTypeLen(typeRowid); - testcase( m.n-lenRowid==szHdr ); - if( unlikely(m.n-lenRowidpCursor; Index: src/vdbeblob.c ================================================================== --- src/vdbeblob.c +++ src/vdbeblob.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** ** This file contains code used to implement incremental BLOB I/O. ** -** $Id: vdbeblob.c,v 1.33 2009/06/01 19:53:31 drh Exp $ +** $Id: vdbeblob.c,v 1.35 2009/07/02 07:47:33 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -64,23 +64,22 @@ ** transaction. */ static const VdbeOpList openBlob[] = { {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */ {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */ - - /* One of the following two instructions is replaced by an - ** OP_Noop before exection. - */ - {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, 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 */ + {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ + + /* One of the following two instructions is replaced by an OP_Noop. */ + {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ + {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ + + {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ + {OP_NotExists, 0, 9, 1}, /* 6: Seek the cursor */ + {OP_Column, 0, 0, 1}, /* 7 */ + {OP_ResultRow, 1, 0, 0}, /* 8 */ + {OP_Close, 0, 0, 0}, /* 9 */ + {OP_Halt, 0, 0, 0}, /* 10 */ }; Vdbe *v = 0; int rc = SQLITE_OK; char *zErr = 0; @@ -168,39 +167,44 @@ v = sqlite3VdbeCreate(db); if( v ){ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); + flags = !!flags; /* flags = (flags ? 1 : 0); */ /* Configure the OP_Transaction */ sqlite3VdbeChangeP1(v, 0, iDb); - sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0)); + sqlite3VdbeChangeP2(v, 0, flags); /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); + /* Configure the OP_TableLock instruction */ + sqlite3VdbeChangeP1(v, 2, iDb); + sqlite3VdbeChangeP2(v, 2, pTab->tnum); + sqlite3VdbeChangeP3(v, 2, flags); + sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); + /* Remove either the OP_OpenWrite or OpenRead. Set the P2 - ** parameter of the other to pTab->tnum. - */ - flags = !!flags; - sqlite3VdbeChangeToNoop(v, 3 - flags, 1); - sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum); - sqlite3VdbeChangeP3(v, 2 + flags, iDb); + ** parameter of the other to pTab->tnum. */ + sqlite3VdbeChangeToNoop(v, 4 - flags, 1); + sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); + sqlite3VdbeChangeP3(v, 3 + flags, iDb); /* 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. */ - sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); - sqlite3VdbeChangeP2(v, 6, pTab->nCol); + sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); + sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ sqlite3VdbeMakeReady(v, 1, 1, 1, 0); } } Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -13,11 +13,11 @@ ** This file contains code use to manipulate "Mem" structure. A "Mem" ** stores a single value in the VDBE. Mem is an opaque structure visible ** only within the VDBE. Interface routines refer to a Mem using the ** name sqlite_value ** -** $Id: vdbemem.c,v 1.149 2009/06/22 19:05:41 drh Exp $ +** $Id: vdbemem.c,v 1.150 2009/06/25 01:47:12 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" /* @@ -94,11 +94,11 @@ sqlite3DbFree(pMem->db, pMem->zMalloc); pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n); } } - if( preserve && pMem->z && pMem->zMalloc && pMem->z!=pMem->zMalloc ){ + if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){ memcpy(pMem->zMalloc, pMem->z, pMem->n); } if( pMem->flags&MEM_Dyn && pMem->xDel ){ pMem->xDel((void *)(pMem->z)); } @@ -243,11 +243,11 @@ ** Return SQLITE_ERROR if the finalizer reports an error. SQLITE_OK ** otherwise. */ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ int rc = SQLITE_OK; - if( pFunc && pFunc->xFinalize ){ + if( ALWAYS(pFunc && pFunc->xFinalize) ){ sqlite3_context ctx; assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); memset(&ctx, 0, sizeof(ctx)); ctx.s.flags = MEM_Null; @@ -256,11 +256,11 @@ ctx.pFunc = pFunc; pFunc->xFinalize(&ctx); assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel ); sqlite3DbFree(pMem->db, pMem->zMalloc); memcpy(pMem, &ctx.s, sizeof(ctx.s)); - rc = (ctx.isError?SQLITE_ERROR:SQLITE_OK); + rc = ctx.isError; } return rc; } /* @@ -531,16 +531,13 @@ ** empty boolean index. */ void sqlite3VdbeMemSetRowSet(Mem *pMem){ sqlite3 *db = pMem->db; assert( db!=0 ); - if( pMem->flags & MEM_RowSet ){ - sqlite3RowSetClear(pMem->u.pRowSet); - }else{ - sqlite3VdbeMemRelease(pMem); - pMem->zMalloc = sqlite3DbMallocRaw(db, 64); - } + assert( (pMem->flags & MEM_RowSet)==0 ); + sqlite3VdbeMemRelease(pMem); + pMem->zMalloc = sqlite3DbMallocRaw(db, 64); if( db->mallocFailed ){ pMem->flags = MEM_Null; }else{ assert( pMem->zMalloc ); pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, @@ -878,11 +875,11 @@ }else{ zData = (char *)sqlite3BtreeDataFetch(pCur, &available); } assert( zData!=0 ); - if( offset+amt<=available && ((pMem->flags&MEM_Dyn)==0 || pMem->xDel) ){ + if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){ sqlite3VdbeMemRelease(pMem); pMem->z = &zData[offset]; pMem->flags = MEM_Blob|MEM_Ephem; }else if( SQLITE_OK==(rc = sqlite3VdbeMemGrow(pMem, amt+2, 0)) ){ pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term; Index: src/vtab.c ================================================================== --- src/vtab.c +++ src/vtab.c @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.91 2009/06/15 16:27:08 shane Exp $ +** $Id: vtab.c,v 1.92 2009/07/01 18:04:21 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" /* @@ -823,11 +823,11 @@ } /* Create a new ephemeral function definition for the overloaded ** function */ pNew = sqlite3DbMallocZero(db, sizeof(*pNew) - + sqlite3Strlen30(pDef->zName) ); + + sqlite3Strlen30(pDef->zName) + 1); if( pNew==0 ){ return pDef; } *pNew = *pDef; pNew->zName = (char *)&pNew[1]; Index: test/auth.test ================================================================== --- test/auth.test +++ test/auth.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is testing the sqlite3_set_authorizer() API # and related functionality. # -# $Id: auth.test,v 1.45 2009/05/04 01:58:31 drh Exp $ +# $Id: auth.test,v 1.46 2009/07/02 18:40:35 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -2316,12 +2316,40 @@ WHERE type='table' ORDER BY name } } {sqlite_stat1 t1 t2 t3 t4} } + +# Ticket #3944 +# +ifcapable trigger { + do_test auth-5.3.1 { + execsql { + CREATE TABLE t5 ( x ); + CREATE TRIGGER t5_tr1 AFTER INSERT ON t5 BEGIN + UPDATE t5 SET x = 1 WHERE NEW.x = 0; + END; + } + } {} + set ::authargs [list] + proc auth {args} { + eval lappend ::authargs $args + return SQLITE_OK + } + do_test auth-5.3.2 { + execsql { INSERT INTO t5 (x) values(0) } + set ::authargs + } [list SQLITE_INSERT t5 {} main {} \ + SQLITE_UPDATE t5 x main t5_tr1 \ + SQLITE_READ t5 x main t5_tr1 \ + ] + do_test auth-5.3.2 { + execsql { SELECT * FROM t5 } + } {1} +} rename proc {} rename proc_real proc finish_test Index: test/autoinc.test ================================================================== --- test/autoinc.test +++ test/autoinc.test @@ -9,11 +9,11 @@ # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the AUTOINCREMENT features. # -# $Id: autoinc.test,v 1.13 2008/08/11 18:44:58 drh Exp $ +# $Id: autoinc.test,v 1.14 2009/06/23 20:28:54 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -554,7 +554,84 @@ SELECT * FROM sqlite_sequence WHERE name='t3'; } } {t3 0} +# Ticket #3928. Make sure that triggers to not make extra slots in +# the SQLITE_SEQUENCE table. +# +do_test autoinc-3928.1 { + db eval { + CREATE TABLE t3928(a INTEGER PRIMARY KEY AUTOINCREMENT, b); + CREATE TRIGGER t3928r1 BEFORE INSERT ON t3928 BEGIN + INSERT INTO t3928(b) VALUES('before1'); + INSERT INTO t3928(b) VALUES('before2'); + END; + CREATE TRIGGER t3928r2 AFTER INSERT ON t3928 BEGIN + INSERT INTO t3928(b) VALUES('after1'); + INSERT INTO t3928(b) VALUES('after2'); + END; + INSERT INTO t3928(b) VALUES('test'); + SELECT * FROM t3928 ORDER BY a; + } +} {1 before1 2 after1 3 after2 4 before2 5 after1 6 after2 7 test 8 before1 9 before2 10 after1 11 before1 12 before2 13 after2} +do_test autoinc-3928.2 { + db eval { + SELECT * FROM sqlite_sequence WHERE name='t3928' + } +} {t3928 13} + +do_test autoinc-3928.3 { + db eval { + DROP TRIGGER t3928r1; + DROP TRIGGER t3928r2; + CREATE TRIGGER t3928r3 BEFORE UPDATE ON t3928 + WHEN typeof(new.b)=='integer' BEGIN + INSERT INTO t3928(b) VALUES('before-int-' || new.b); + END; + CREATE TRIGGER t3928r4 AFTER UPDATE ON t3928 + WHEN typeof(new.b)=='integer' BEGIN + INSERT INTO t3928(b) VALUES('after-int-' || new.b); + END; + DELETE FROM t3928 WHERE a!=1; + UPDATE t3928 SET b=456 WHERE a=1; + SELECT * FROM t3928 ORDER BY a; + } +} {1 456 14 before-int-456 15 after-int-456} +do_test autoinc-3928.4 { + db eval { + SELECT * FROM sqlite_sequence WHERE name='t3928' + } +} {t3928 15} + +do_test autoinc-3928.5 { + db eval { + CREATE TABLE t3928b(x); + INSERT INTO t3928b VALUES(100); + INSERT INTO t3928b VALUES(200); + INSERT INTO t3928b VALUES(300); + DELETE FROM t3928; + CREATE TABLE t3928c(y INTEGER PRIMARY KEY AUTOINCREMENT, z); + CREATE TRIGGER t3928br1 BEFORE DELETE ON t3928b BEGIN + INSERT INTO t3928(b) VALUES('before-del-'||old.x); + INSERT INTO t3928c(z) VALUES('before-del-'||old.x); + END; + CREATE TRIGGER t3928br2 AFTER DELETE ON t3928b BEGIN + INSERT INTO t3928(b) VALUES('after-del-'||old.x); + INSERT INTO t3928c(z) VALUES('after-del-'||old.x); + END; + DELETE FROM t3928b; + SELECT * FROM t3928 ORDER BY a; + } +} {16 before-del-100 17 after-del-100 18 before-del-200 19 after-del-200 20 before-del-300 21 after-del-300} +do_test autoinc-3928.6 { + db eval { + SELECT * FROM t3928c ORDER BY y; + } +} {1 before-del-100 2 after-del-100 3 before-del-200 4 after-del-200 5 before-del-300 6 after-del-300} +do_test autoinc-3928.7 { + db eval { + SELECT * FROM sqlite_sequence WHERE name LIKE 't3928%' ORDER BY name; + } +} {t3928 21 t3928c 6} finish_test Index: test/exclusive.test ================================================================== --- test/exclusive.test +++ test/exclusive.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The focus # of these tests is exclusive access mode (i.e. the thing activated by # "PRAGMA locking_mode = EXCLUSIVE"). # -# $Id: exclusive.test,v 1.14 2009/04/30 16:41:00 danielk1977 Exp $ +# $Id: exclusive.test,v 1.15 2009/06/26 12:30:40 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {!pager_pragmas} { @@ -257,12 +257,11 @@ set exists 0 set content 0 if {[file exists $fname]} { set exists 1 set hdr [hexio_read $fname 0 28] - set content \ - [expr {$hdr!="00000000000000000000000000000000000000000000000000000000"}] + set content [expr {0==[string match $hdr [string repeat 0 56]]}] } list $exists $content } do_test exclusive-3.0 { filestate test.db-journal Index: test/incrblob2.test ================================================================== --- test/incrblob2.test +++ test/incrblob2.test @@ -10,11 +10,11 @@ #*********************************************************************** # # Test that it is possible to have two open blob handles on a single # blob object. # -# $Id: incrblob2.test,v 1.10 2009/03/16 13:19:36 danielk1977 Exp $ +# $Id: incrblob2.test,v 1.11 2009/06/29 06:00:37 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -272,18 +272,27 @@ execsql BEGIN db2 catchsql { INSERT INTO t1 VALUES(4, 'pqrst') } db2 } {0 {}} do_test incrblob2-5.5 { - set blob [db incrblob -readonly t1 data 1] - catchsql { INSERT INTO t1 VALUES(5, 'uvwxy') } db2 - } {1 {database table is locked}} + set rc [catch { db incrblob -readonly t1 data 1 } msg] + list $rc $msg + } {1 {database table is locked: t1}} do_test incrblob2-5.6 { - close $blob + execsql { PRAGMA read_uncommitted=1 } + set blob [db incrblob -readonly t1 data 4] + read $blob + } {pqrst} + + do_test incrblob2-5.7 { catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2 } {0 {}} + + do_test incrblob2-5.8 { + close $blob + } {} db2 close db close sqlite3_enable_shared_cache $::enable_shared_cache } Index: test/join.test ================================================================== --- test/join.test +++ test/join.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for joins, including outer joins. # -# $Id: join.test,v 1.26 2008/12/05 00:00:07 drh Exp $ +# $Id: join.test,v 1.27 2009/07/01 16:12:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_test join-1.1 { @@ -307,14 +307,12 @@ catchsql { SELECT * FROM t1 JOIN t2 USING(d); } } {1 {cannot join using column d - column not present in both tables}} do_test join-3.5 { - catchsql { - SELECT * FROM t1 USING(a); - } -} {0 {1 2 3 2 3 4 3 4 5}} + catchsql { SELECT * FROM t1 USING(a) } +} {1 {a JOIN clause is required before USING}} do_test join-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}} Index: test/malloc.test ================================================================== --- test/malloc.test +++ test/malloc.test @@ -14,11 +14,11 @@ # the SQLite library accepts a special command (sqlite3_memdebug_fail N C) # which causes the N-th malloc to fail. This special feature is used # to see what happens in the library if a malloc were to really fail # due to an out-of-memory situation. # -# $Id: malloc.test,v 1.80 2009/06/22 05:43:24 danielk1977 Exp $ +# $Id: malloc.test,v 1.81 2009/06/24 13:13:45 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -841,34 +841,36 @@ SELECT test_agg_errmsg16(), group_concat(a) FROM t1 } # At one point, if an OOM occured immediately after obtaining a shared lock # on the database file, the file remained locked. This test case ensures -# that bug has been fixed. -do_malloc_test 36 -tclprep { - sqlite3 db2 test.db - execsql { - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, 2); - } db2 -} -sqlbody { - SELECT * FROM t1; -} -cleanup { - # Try to write to the database using connection [db2]. If connection [db] - # has correctly released the shared lock, this write attempt should - # succeed. If [db] has not released the lock, this should hit an - # SQLITE_BUSY error. - do_test malloc-36.$zRepeat.${::n}.unlocked { - execsql {INSERT INTO t1 VALUES(3, 4)} db2 - } {} - db2 close -} -catch { db2 close } +# that bug has been fixed.i +if {[db eval {PRAGMA locking_mode}]!="exclusive"} { + do_malloc_test 37 -tclprep { + sqlite3 db2 test.db + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } db2 + } -sqlbody { + SELECT * FROM t1; + } -cleanup { + # Try to write to the database using connection [db2]. If connection [db] + # has correctly released the shared lock, this write attempt should + # succeed. If [db] has not released the lock, this should hit an + # SQLITE_BUSY error. + do_test malloc-36.$zRepeat.${::n}.unlocked { + execsql {INSERT INTO t1 VALUES(3, 4)} db2 + } {} + db2 close + } + catch { db2 close } +} # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close} set sqlite_open_file_count } {0} puts open-file-count=$sqlite_open_file_count finish_test Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -7,11 +7,11 @@ # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # -# $Id: permutations.test,v 1.50 2009/05/13 14:46:10 danielk1977 Exp $ +# $Id: permutations.test,v 1.51 2009/07/01 18:09:02 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Argument processing. @@ -481,17 +481,25 @@ autovacuum.test delete3.test manydb.test bigrow.test incrblob2.test memdb.test bitvec.test index2.test memsubsys1.test capi3c.test ioerr.test memsubsys2.test capi3.test join3.test pagesize.test - collate5.test limit.test + collate5.test limit.test backup_ioerr.test + backup_malloc.test } -initialize { catch {db close} sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 25000000 0 sqlite3_config_lookaside 0 0 + ifcapable mem5 { + # If both memsys3 and memsys5 are enabled in the build, the call to + # [sqlite3_config_heap] will initialize the system to use memsys5. + # The following overrides this preference and installs the memsys3 + # allocator. + sqlite3_install_memsys3 + } install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} Index: test/rollback.test ================================================================== --- test/rollback.test +++ test/rollback.test @@ -11,11 +11,11 @@ # This file implements regression tests for SQLite library. The # focus of this file is verifying that a rollback in one statement # caused by an ON CONFLICT ROLLBACK clause aborts any other pending # statements. # -# $Id: rollback.test,v 1.10 2008/10/17 18:51:53 danielk1977 Exp $ +# $Id: rollback.test,v 1.11 2009/06/26 07:12:07 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl set DB [sqlite3_connection_pointer db] @@ -112,10 +112,17 @@ set iOffset [expr (([file size testA.db-journal] + 511)/512)*512] set fd [open testA.db-journal a+] fconfigure $fd -encoding binary -translation binary seek $fd $iOffset puts -nonewline $fd $zAppend + + # Also, fix the first journal-header in the journal-file. Because the + # journal file has not yet been synced, the 8-byte magic string at the + # start of the first journal-header has not been written by SQLite. + # So write it now. + seek $fd 0 + puts -nonewline $fd "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7" close $fd # Open a handle on testA.db and use it to query the database. At one # point the first query would attempt a hot rollback, attempt to open # the master-journal file and return SQLITE_CANTOPEN when it could not Index: test/rowid.test ================================================================== --- test/rowid.test +++ test/rowid.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the magic ROWID column that is # found on all tables. # -# $Id: rowid.test,v 1.20 2008/01/19 20:11:26 drh Exp $ +# $Id: rowid.test,v 1.21 2009/06/26 15:14:55 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Basic ROWID functionality tests. @@ -664,10 +664,11 @@ # rowid guesser gives up and reports SQLITE_FULL. # do_test rowid-12.1 { execsql { CREATE TABLE t7(x INTEGER PRIMARY KEY, y); + CREATE TABLE t7temp(a INTEGER PRIMARY KEY); INSERT INTO t7 VALUES(9223372036854775807,'a'); SELECT y FROM t7; } } {a} do_test rowid-12.2 { @@ -678,24 +679,25 @@ INSERT INTO t7 VALUES(NULL,'b'); SELECT x, y FROM t7; } } {1 b 9223372036854775807 a} execsql {INSERT INTO t7 VALUES(2,'y');} -for {set i 1} {$i<=101} {incr i} { +for {set i 1} {$i<100} {incr i} { do_test rowid-12.3.$i { + db eval {DELETE FROM t7temp; INSERT INTO t7temp VALUES(1);} restore_prng_state execsql { INSERT INTO t7 VALUES(NULL,'x'); - INSERT OR IGNORE INTO t7 VALUES(last_insert_rowid()+1,'y'); SELECT count(*) FROM t7 WHERE y=='x'; } } $i } do_test rowid-12.4 { + db eval {DELETE FROM t7temp; INSERT INTO t7temp VALUES(1);} restore_prng_state catchsql { INSERT INTO t7 VALUES(NULL,'x'); } } {1 {database or disk is full}} finish_test Index: test/speed3.test ================================================================== --- test/speed3.test +++ test/speed3.test @@ -10,11 +10,11 @@ #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing that the overflow-page related # enhancements added after version 3.3.17 speed things up. # -# $Id: speed3.test,v 1.5 2007/10/09 08:29:33 danielk1977 Exp $ +# $Id: speed3.test,v 1.6 2009/07/09 02:48:24 shane Exp $ # #--------------------------------------------------------------------- # Test plan: # @@ -103,22 +103,10 @@ # puts "2: [array get stats2]" puts "Incrvacuum: Read $stats1(read), wrote $stats1(write)" puts "Normal : Read $stats2(read), wrote $stats2(write)" } -proc overflow_report {db} { - set bt [btree_from_db db] - set csr [btree_cursor $bt 3 0] - - for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} { - puts "[btree_ovfl_info $bt $csr]" - } - - btree_close_cursor $csr - -} - proc reset_db {} { db close sqlite3 db test.db db eval { PRAGMA main.cache_size = 200000; @@ -164,11 +152,10 @@ } {2 0} # Delete all content in a table, one row at a time. # #io_log db -#overflow_report db reset_db speed_trial speed3-1.incrvacuum $::NROW row {DELETE FROM main.t1 WHERE 1} speed_trial speed3-1.normal $::NROW row {DELETE FROM aux.t1 WHERE 1} io_log db @@ -175,12 +162,11 @@ # Select the "C" column (located at the far end of the overflow # chain) from each table row. # #db eval {PRAGMA incremental_vacuum(500000)} populate_t1 db -#overflow_report db reset_db speed_trial speed3-2.incrvacuum $::NROW row {SELECT c FROM main.t1} speed_trial speed3-2.normal $::NROW row {SELECT c FROM aux.t1} io_log db finish_test Index: test/sqllimits1.test ================================================================== --- test/sqllimits1.test +++ test/sqllimits1.test @@ -10,11 +10,11 @@ #*********************************************************************** # # This file contains tests to verify that the limits defined in # sqlite source file limits.h are enforced. # -# $Id: sqllimits1.test,v 1.32 2009/06/12 12:04:16 drh Exp $ +# $Id: sqllimits1.test,v 1.33 2009/06/25 01:47:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Verify that the default per-connection limits are the same as @@ -402,11 +402,11 @@ longer without increasing the expression depth */\ AND 1 == 1" set N [expr {(50000 / [string length $tail])+1}] append sql [string repeat $tail $N] catchsql $sql -} {1 {String or BLOB exceeded size limit}} +} {1 {string or blob too big}} do_test sqllimits1-6.3 { sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH 50000 set sql "SELECT 1 WHERE 1==1" set tail " /* A comment to take up space in order to make the string\ longer without increasing the expression depth */\ Index: test/tkt3457.test ================================================================== --- test/tkt3457.test +++ test/tkt3457.test @@ -8,11 +8,11 @@ # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # -# $Id: tkt3457.test,v 1.2 2009/06/05 17:09:12 drh Exp $ +# $Id: tkt3457.test,v 1.3 2009/06/26 07:12:07 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl if {$tcl_platform(platform) != "unix"} { @@ -46,10 +46,20 @@ INSERT INTO t1 VALUES(4, 5, 6); } file copy -force test.db bak.db file copy -force test.db-journal bak.db-journal + + # Fix the first journal-header in the journal-file. Because the + # journal file has not yet been synced, the 8-byte magic string at the + # start of the first journal-header has not been written by SQLite. + # So write it now. + set fd [open bak.db-journal a+] + fconfigure $fd -encoding binary -translation binary + seek $fd 0 + puts -nonewline $fd "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7" + close $fd execsql COMMIT } {} do_test tkt3457-1.2 { Index: test/tkt3922.test ================================================================== --- test/tkt3922.test +++ test/tkt3922.test @@ -7,22 +7,37 @@ # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # -# $Id: tkt3922.test,v 1.1 2009/06/17 16:20:04 drh Exp $ +# $Id: tkt3922.test,v 1.2 2009/06/26 14:17:47 shane Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl -do_test tkt3922.1 { - execsql { - CREATE TABLE t1(a NUMBER); - INSERT INTO t1 VALUES('-9223372036854775808'); - SELECT a, typeof(a) FROM t1; - } -} {-9223372036854775808 integer} +if {[working_64bit_int]} { + do_test tkt3922.1 { + execsql { + CREATE TABLE t1(a NUMBER); + INSERT INTO t1 VALUES('-9223372036854775808'); + SELECT a, typeof(a) FROM t1; + } + } {-9223372036854775808 integer} +} else { + # this alternate version of tkt3922.1 doesn't + # really test the same thing as the original, + # but is needed to create the table and + # provided simply as a place holder for + # platforms without working 64bit support. + do_test tkt3922.1 { + execsql { + CREATE TABLE t1(a NUMBER); + INSERT INTO t1 VALUES('-1'); + SELECT a, typeof(a) FROM t1; + } + } {-1 integer} +} do_test tkt3922.2 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES('-9223372036854775809'); SELECT a, typeof(a) FROM t1; @@ -40,21 +55,35 @@ DELETE FROM t1; INSERT INTO t1 VALUES('-9223372036854776833'); SELECT a, typeof(a) FROM t1; } } {-9.22337203685478e+18 real} -do_test tkt3922.5 { - execsql { - DELETE FROM t1; - INSERT INTO t1 VALUES('9223372036854775807'); - SELECT a, typeof(a) FROM t1; - } -} {9223372036854775807 integer} +if {[working_64bit_int]} { + do_test tkt3922.5 { + execsql { + DELETE FROM t1; + INSERT INTO t1 VALUES('9223372036854775807'); + SELECT a, typeof(a) FROM t1; + } + } {9223372036854775807 integer} +} else { + # this alternate version of tkt3922.5 doesn't + # really test the same thing as the original, + # but provided simply as a place holder for + # platforms without working 64bit support. + do_test tkt3922.5 { + execsql { + DELETE FROM t1; + INSERT INTO t1 VALUES('1'); + SELECT a, typeof(a) FROM t1; + } + } {1 integer} +} do_test tkt3922.6 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES('9223372036854775808'); SELECT a, typeof(a) FROM t1; } } {9.22337203685478e+18 real} finish_test Index: test/trigger1.test ================================================================== --- test/trigger1.test +++ test/trigger1.test @@ -637,6 +637,66 @@ } {1 {datatype mismatch}} do_test trigger1-15.2 { catchsql { INSERT INTO tA VALUES('abc', 2, 3) } } {1 {datatype mismatch}} +# Ticket #3947: Do not allow qualified table names on INSERT, UPDATE, and +# DELETE statements within triggers. Actually, this has never been allowed +# by the grammar. But the error message is confusing: one simply gets a +# "syntax error". That has now been changed to give a full error message. +# +do_test trigger1-16.1 { + db eval { + CREATE TABLE t16(a,b,c); + CREATE INDEX t16a ON t16(a); + CREATE INDEX t16b ON t16(b); + } + catchsql { + CREATE TRIGGER main.t16err1 AFTER INSERT ON tA BEGIN + INSERT INTO main.t16 VALUES(1,2,3); + END; + } +} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}} +do_test trigger1-16.2 { + catchsql { + CREATE TRIGGER main.t16err2 AFTER INSERT ON tA BEGIN + UPDATE main.t16 SET rowid=rowid+1; + END; + } +} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}} +do_test trigger1-16.3 { + catchsql { + CREATE TRIGGER main.t16err3 AFTER INSERT ON tA BEGIN + DELETE FROM main.t16; + END; + } +} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}} +do_test trigger1-16.4 { + catchsql { + CREATE TRIGGER main.t16err4 AFTER INSERT ON tA BEGIN + UPDATE t16 NOT INDEXED SET rowid=rowid+1; + END; + } +} {1 {the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers}} +do_test trigger1-16.5 { + catchsql { + CREATE TRIGGER main.t16err5 AFTER INSERT ON tA BEGIN + UPDATE t16 INDEXED BY t16a SET rowid=rowid+1 WHERE a=1; + END; + } +} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}} +do_test trigger1-16.6 { + catchsql { + CREATE TRIGGER main.t16err6 AFTER INSERT ON tA BEGIN + DELETE FROM t16 NOT INDEXED WHERE a=123; + END; + } +} {1 {the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers}} +do_test trigger1-16.7 { + catchsql { + CREATE TRIGGER main.t16err7 AFTER INSERT ON tA BEGIN + DELETE FROM t16 INDEXED BY t16a WHERE a=123; + END; + } +} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}} + finish_test Index: test/types.test ================================================================== --- test/types.test +++ test/types.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. Specfically # it tests that the different storage classes (integer, real, text etc.) # all work correctly. # -# $Id: types.test,v 1.19 2006/06/27 12:51:13 drh Exp $ +# $Id: types.test,v 1.20 2009/06/29 06:00:37 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Tests in this file are organized roughly as follows: @@ -134,10 +134,11 @@ # Open the table with root-page $rootpage at the btree # level. Return a list that is the length of each record # in the table, in the tables default scanning order. proc record_sizes {rootpage} { set bt [btree_open test.db 10 0] + btree_begin_transaction $bt set c [btree_cursor $bt $rootpage 0] btree_first $c while 1 { lappend res [btree_payload_size $c] if {[btree_next $c]} break Index: test/vtab6.test ================================================================== --- test/vtab6.test +++ test/vtab6.test @@ -12,11 +12,11 @@ # # This file implements tests for joins, including outer joins involving # virtual tables. The test cases in this file are copied from the file # join.test, and some of the comments still reflect that. # -# $Id: vtab6.test,v 1.4 2008/07/12 14:52:21 drh Exp $ +# $Id: vtab6.test,v 1.5 2009/07/01 16:12:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab { @@ -263,14 +263,12 @@ catchsql { SELECT * FROM t1 JOIN t2 USING(a); } } {1 {cannot join using column a - column not present in both tables}} do_test vtab6-3.5 { - catchsql { - SELECT * FROM t1 USING(a); - } -} {0 {1 2 3 2 3 4 3 4 5}} + catchsql { SELECT * FROM t1 USING(a) } +} {1 {a JOIN clause is required before USING}} do_test vtab6-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}}