diff --git a/Makefile.in b/Makefile.in index 3bc5d8fda3..e954ccb7d5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -164,7 +164,7 @@ NAWK = @AWK@ # Object files for the SQLite library (non-amalgamation). # OBJS0 = alter.lo analyze.lo attach.lo auth.lo backup.lo bitvec.lo btmutex.lo \ - btree.lo build.lo callback.lo complete.lo date.lo \ + btree.lo build.lo callback.lo complete.lo ctime.lo date.lo \ delete.lo expr.lo fault.lo fkey.lo func.lo global.lo \ hash.lo journal.lo insert.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ @@ -204,6 +204,7 @@ SRC = \ $(TOP)/src/build.c \ $(TOP)/src/callback.c \ $(TOP)/src/complete.c \ + $(TOP)/src/ctime.c \ $(TOP)/src/date.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ @@ -339,6 +340,7 @@ TESTSRC2 = \ $(TOP)/src/btree.c \ $(TOP)/src/build.c \ $(TOP)/src/date.c \ + $(TOP)/src/ctime.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ $(TOP)/src/insert.c \ @@ -542,6 +544,9 @@ callback.lo: $(TOP)/src/callback.c $(HDR) complete.lo: $(TOP)/src/complete.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/complete.c +ctime.lo: $(TOP)/src/ctime.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/ctime.c + date.lo: $(TOP)/src/date.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/date.c diff --git a/Makefile.vxworks b/Makefile.vxworks index 141b7061a5..273ce131d2 100644 --- a/Makefile.vxworks +++ b/Makefile.vxworks @@ -231,6 +231,7 @@ SRC = \ $(TOP)/src/build.c \ $(TOP)/src/callback.c \ $(TOP)/src/complete.c \ + $(TOP)/src/ctime.c \ $(TOP)/src/date.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ @@ -392,7 +393,7 @@ TESTSRC = \ TESTSRC2 = \ $(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \ - $(TOP)/src/build.c $(TOP)/src/date.c \ + $(TOP)/src/build.c $(TOP)/src/ctime.c $(TOP)/src/date.c \ $(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/os.c \ $(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \ $(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \ diff --git a/VERSION b/VERSION index 4cc5902cfc..2d6744e051 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.6.22 +3.6.23 diff --git a/configure b/configure index 5ce6104f00..a95c3b3c29 100644 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for sqlite 3.6.22. +# Generated by GNU Autoconf 2.62 for sqlite 3.6.23. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. @@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.6.22' -PACKAGE_STRING='sqlite 3.6.22' +PACKAGE_VERSION='3.6.23' +PACKAGE_STRING='sqlite 3.6.23' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. @@ -1487,7 +1487,7 @@ 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.22 to adapt to many kinds of systems. +\`configure' configures sqlite 3.6.23 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1552,7 +1552,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.6.22:";; + short | recursive ) echo "Configuration of sqlite 3.6.23:";; esac cat <<\_ACEOF @@ -1670,7 +1670,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.6.22 +sqlite configure 3.6.23 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1684,7 +1684,7 @@ 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.22, which was +It was created by sqlite $as_me 3.6.23, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -13972,7 +13972,7 @@ exec 6>&1 # 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.22, which was +This file was extended by sqlite $as_me 3.6.23, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14025,7 +14025,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.6.22 +sqlite config.status 3.6.23 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c index 8b8a67380d..d78d19856a 100644 --- a/ext/fts3/fts3_snippet.c +++ b/ext/fts3/fts3_snippet.c @@ -213,6 +213,8 @@ static int fts3ExprLoadDoclistsCb1(Fts3Expr *pExpr, int iPhrase, void *ctx){ int rc = SQLITE_OK; LoadDoclistCtx *p = (LoadDoclistCtx *)ctx; + UNUSED_PARAMETER(iPhrase); + p->nPhrase++; p->nToken += pExpr->pPhrase->nToken; @@ -233,6 +235,8 @@ static int fts3ExprLoadDoclistsCb1(Fts3Expr *pExpr, int iPhrase, void *ctx){ ** fts3ExprLoadDoclists(). */ static int fts3ExprLoadDoclistsCb2(Fts3Expr *pExpr, int iPhrase, void *ctx){ + UNUSED_PARAMETER(iPhrase); + UNUSED_PARAMETER(ctx); if( pExpr->aDoclist ){ pExpr->pCurrent = pExpr->aDoclist; pExpr->iCurrent = 0; @@ -522,7 +526,7 @@ static int fts3StringAppend( int nAppend /* Size of zAppend in bytes (or -1) */ ){ if( nAppend<0 ){ - nAppend = strlen(zAppend); + nAppend = (int)strlen(zAppend); } /* If there is insufficient space allocated at StrBuffer.z, use realloc() @@ -1024,6 +1028,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ char *pList; /* Pointer to position list for phrase */ int iPos = 0; /* First position in position-list */ + UNUSED_PARAMETER(iPhrase); pList = sqlite3Fts3FindPositions(pExpr, p->iDocid, p->iCol); nTerm = pExpr->pPhrase->nToken; if( pList ){ diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index e30aef7116..23b8866049 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -2274,9 +2274,11 @@ static void fts3DecodeIntArray( int nBuf /* size of the BLOB */ ){ int i, j; + UNUSED_PARAMETER(nBuf); for(i=j=0; inColumn+1) ); + memset(a, 0, sizeof(u32)*(p->nColumn+1) ); } sqlite3_reset(pStmt); - if( nChng<0 && a[0]<-nChng ){ + if( nChng<0 && a[0]<(u32)(-nChng) ){ a[0] = 0; }else{ a[0] += nChng; diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 49c00a35bd..3111cc2e96 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -423,6 +423,7 @@ nodeAcquire( rc = sqlite3_step(pRtree->pReadNode); if( rc==SQLITE_ROW ){ const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0); + assert( sqlite3_column_bytes(pRtree->pReadNode, 0)==pRtree->iNodeSize ); memcpy(pNode->zData, zBlob, pRtree->iNodeSize); nodeReference(pParent); }else{ @@ -2619,31 +2620,69 @@ static int rtreeSqlInit( } /* -** This routine queries database handle db for the page-size used by -** database zDb. If successful, the page-size in bytes is written to -** *piPageSize and SQLITE_OK returned. Otherwise, and an SQLite error -** code is returned. +** The second argument to this function contains the text of an SQL statement +** that returns a single integer value. The statement is compiled and executed +** using database connection db. If successful, the integer value returned +** is written to *piVal and SQLITE_OK returned. Otherwise, an SQLite error +** code is returned and the value of *piVal after returning is not defined. */ -static int getPageSize(sqlite3 *db, const char *zDb, int *piPageSize){ +static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){ int rc = SQLITE_NOMEM; + if( zSql ){ + sqlite3_stmt *pStmt = 0; + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc==SQLITE_OK ){ + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + *piVal = sqlite3_column_int(pStmt, 0); + } + rc = sqlite3_finalize(pStmt); + } + } + return rc; +} + +/* +** This function is called from within the xConnect() or xCreate() method to +** determine the node-size used by the rtree table being created or connected +** to. If successful, pRtree->iNodeSize is populated and SQLITE_OK returned. +** Otherwise, an SQLite error code is returned. +** +** If this function is being called as part of an xConnect(), then the rtree +** table already exists. In this case the node-size is determined by inspecting +** the root node of the tree. +** +** Otherwise, for an xCreate(), use 64 bytes less than the database page-size. +** This ensures that each node is stored on a single database page. If the +** database page-size is so large that more than RTREE_MAXCELLS entries +** would fit in a single node, use a smaller node-size. +*/ +static int getNodeSize( + sqlite3 *db, /* Database handle */ + Rtree *pRtree, /* Rtree handle */ + int isCreate /* True for xCreate, false for xConnect */ +){ + int rc; char *zSql; - sqlite3_stmt *pStmt = 0; - - zSql = sqlite3_mprintf("PRAGMA %Q.page_size", zDb); - if( !zSql ){ - return SQLITE_NOMEM; + if( isCreate ){ + int iPageSize; + zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb); + rc = getIntFromStmt(db, zSql, &iPageSize); + if( rc==SQLITE_OK ){ + pRtree->iNodeSize = iPageSize-64; + if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)iNodeSize ){ + pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; + } + } + }else{ + zSql = sqlite3_mprintf( + "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", + pRtree->zDb, pRtree->zName + ); + rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); } - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); - if( rc!=SQLITE_OK ){ - return rc; - } - - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *piPageSize = sqlite3_column_int(pStmt, 0); - } - return sqlite3_finalize(pStmt); + return rc; } /* @@ -2664,7 +2703,6 @@ static int rtreeInit( int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; - int iPageSize = 0; Rtree *pRtree; int nDb; /* Length of string argv[1] */ int nName; /* Length of string argv[2] */ @@ -2683,11 +2721,6 @@ static int rtreeInit( return SQLITE_ERROR; } - rc = getPageSize(db, argv[1], &iPageSize); - if( rc!=SQLITE_OK ){ - return rc; - } - /* Allocate the sqlite3_vtab structure */ nDb = strlen(argv[1]); nName = strlen(argv[2]); @@ -2706,44 +2739,37 @@ static int rtreeInit( memcpy(pRtree->zDb, argv[1], nDb); memcpy(pRtree->zName, argv[2], nName); - /* Figure out the node size to use. By default, use 64 bytes less than - ** the database page-size. This ensures that each node is stored on - ** a single database page. - ** - ** If the databasd page-size is so large that more than RTREE_MAXCELLS - ** entries would fit in a single node, use a smaller node-size. - */ - pRtree->iNodeSize = iPageSize-64; - if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)iNodeSize ){ - pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; - } + /* Figure out the node size to use. */ + rc = getNodeSize(db, pRtree, isCreate); /* Create/Connect to the underlying relational database schema. If ** that is successful, call sqlite3_declare_vtab() to configure ** the r-tree table schema. */ - if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){ - *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); - }else{ - char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]); - char *zTmp; - int ii; - for(ii=4; zSql && iipBt, sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); } - aNew->zName = sqlite3DbStrDup(db, zName); aNew->safety_level = 3; + aNew->zName = sqlite3DbStrDup(db, zName); + if( rc==SQLITE_OK && aNew->zName==0 ){ + rc = SQLITE_NOMEM; + } -#if SQLITE_HAS_CODEC + +#ifdef SQLITE_HAS_CODEC if( rc==SQLITE_OK ){ extern int sqlite3CodecAttach(sqlite3*, int, const void*, int); extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); @@ -186,11 +190,9 @@ static void attachFunc( ** we found it. */ if( rc==SQLITE_OK ){ - (void)sqlite3SafetyOn(db); sqlite3BtreeEnterAll(db); rc = sqlite3Init(db, &zErrDyn); sqlite3BtreeLeaveAll(db); - (void)sqlite3SafetyOff(db); } if( rc ){ int iDb = db->nDb - 1; diff --git a/src/backup.c b/src/backup.c index 422198bfa6..2d01ddbb58 100644 --- a/src/backup.c +++ b/src/backup.c @@ -98,10 +98,10 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ }else{ pParse->db = pDb; if( sqlite3OpenTempDatabase(pParse) ){ - sqlite3ErrorClear(pParse); sqlite3Error(pErrorDb, pParse->rc, "%s", pParse->zErrMsg); rc = SQLITE_ERROR; } + sqlite3DbFree(pErrorDb, pParse->zErrMsg); sqlite3StackFree(pErrorDb, pParse); } if( rc ){ diff --git a/src/btree.c b/src/btree.c index 68f2907203..cf21b025fc 100644 --- a/src/btree.c +++ b/src/btree.c @@ -4928,8 +4928,8 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ /* If the secure_delete option is enabled, then ** always fully overwrite deleted information with zeros. */ - if( (!pPage && (rc = btreeGetPage(pBt, iPage, &pPage, 0))) - || (rc = sqlite3PagerWrite(pPage->pDbPage)) + if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) ) + || ((rc = sqlite3PagerWrite(pPage->pDbPage))!=0) ){ goto freepage_out; } @@ -5061,7 +5061,25 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){ rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); if( rc ) return rc; } - rc = freePage2(pBt, pOvfl, ovflPgno); + + if( (pOvfl || (pOvfl = btreePageLookup(pBt, ovflPgno))) + && sqlite3PagerPageRefcount(pOvfl->pDbPage)!=1 + ){ + /* There is no reason any cursor should have an outstanding reference + ** to an overflow page belonging to a cell that is being deleted/updated. + ** So if there exists more than one reference to this page, then it + ** must not really be an overflow page and the database must be corrupt. + ** It is helpful to detect this before calling freePage2(), as + ** freePage2() may zero the page contents if secure-delete mode is + ** enabled. If this 'overflow' page happens to be a page that the + ** caller is iterating through or using in some other way, this + ** can be problematic. + */ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = freePage2(pBt, pOvfl, ovflPgno); + } + if( pOvfl ){ sqlite3PagerUnref(pOvfl->pDbPage); } @@ -5305,7 +5323,7 @@ static void insertCell( Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ int *pRC /* Read and write return code from here */ ){ - int idx; /* Where to write new cell content in data[] */ + int idx = 0; /* Where to write new cell content in data[] */ int j; /* Loop counter */ int end; /* First byte past the last cell pointer in data[] */ int ins; /* Index in data[] where new cell pointer is inserted */ @@ -5797,8 +5815,15 @@ static int balance_nonroot( ** buffer. It will be copied out again as soon as the aSpace[] buffer ** is allocated. */ if( pBt->secureDelete ){ - memcpy(&aOvflSpace[apDiv[i]-pParent->aData], apDiv[i], szNew[i]); - apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; + int iOff = apDiv[i] - pParent->aData; + if( (iOff+szNew[i])>pBt->usableSize ){ + rc = SQLITE_CORRUPT_BKPT; + memset(apOld, 0, (i+1)*sizeof(MemPage*)); + goto balance_cleanup; + }else{ + memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); + apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; + } } dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); } @@ -5919,7 +5944,7 @@ static int balance_nonroot( if( leafData ){ i--; } subtotal = 0; k++; - if( k>NB+1 ){ rc = SQLITE_CORRUPT; goto balance_cleanup; } + if( k>NB+1 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } } } szNew[k] = subtotal; @@ -5973,7 +5998,7 @@ static int balance_nonroot( ** Allocate k new pages. Reuse old pages where possible. */ if( apOld[0]->pgno<=1 ){ - rc = SQLITE_CORRUPT; + rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } pageFlags = apOld[0]->aData[0]; @@ -7411,7 +7436,9 @@ static void checkList( static int checkTreePage( IntegrityCk *pCheck, /* Context for the sanity check */ int iPage, /* Page number of the page to check */ - char *zParentContext /* Parent context */ + char *zParentContext, /* Parent context */ + i64 *pnParentMinKey, + i64 *pnParentMaxKey ){ MemPage *pPage; int i, rc, depth, d2, pgno, cnt; @@ -7422,6 +7449,8 @@ static int checkTreePage( int usableSize; char zContext[100]; char *hit = 0; + i64 nMinKey = 0; + i64 nMaxKey = 0; sqlite3_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage); @@ -7464,6 +7493,16 @@ static int checkTreePage( btreeParseCellPtr(pPage, pCell, &info); sz = info.nData; if( !pPage->intKey ) sz += (int)info.nKey; + /* For intKey pages, check that the keys are in order. + */ + else if( i==0 ) nMinKey = nMaxKey = info.nKey; + else{ + if( info.nKey <= nMaxKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey); + } + nMaxKey = info.nKey; + } assert( sz==info.nPayload ); if( (sz>info.nLocal) && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize]) @@ -7487,25 +7526,62 @@ static int checkTreePage( checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); } #endif - d2 = checkTreePage(pCheck, pgno, zContext); + d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey); if( i>0 && d2!=depth ){ checkAppendMsg(pCheck, zContext, "Child page depth differs"); } depth = d2; } } + if( !pPage->leaf ){ pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); sqlite3_snprintf(sizeof(zContext), zContext, "On page %d at right child: ", iPage); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, 0); + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); } #endif - checkTreePage(pCheck, pgno, zContext); + checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey); } + /* For intKey leaf pages, check that the min/max keys are in order + ** with any left/parent/right pages. + */ + if( pPage->leaf && pPage->intKey ){ + /* if we are a left child page */ + if( pnParentMinKey ){ + /* if we are the left most child page */ + if( !pnParentMaxKey ){ + if( nMaxKey > *pnParentMinKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (max larger than parent min of %lld)", + nMaxKey, *pnParentMinKey); + } + }else{ + if( nMinKey <= *pnParentMinKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (min less than parent min of %lld)", + nMinKey, *pnParentMinKey); + } + if( nMaxKey > *pnParentMaxKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (max larger than parent max of %lld)", + nMaxKey, *pnParentMaxKey); + } + *pnParentMinKey = nMaxKey; + } + /* else if we're a right child page */ + } else if( pnParentMaxKey ){ + if( nMinKey <= *pnParentMaxKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (min less than parent max of %lld)", + nMinKey, *pnParentMaxKey); + } + } + } + /* Check for complete coverage of the page */ data = pPage->aData; @@ -7529,7 +7605,7 @@ static int checkTreePage( } if( (pc+size-1)>=usableSize ){ checkAppendMsg(pCheck, 0, - "Corruption detected in cell %d on page %d",i,iPage,0); + "Corruption detected in cell %d on page %d",i,iPage); }else{ for(j=pc+size-1; j>=pc; j--) hit[j]++; } @@ -7635,7 +7711,7 @@ char *sqlite3BtreeIntegrityCheck( checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0); } #endif - checkTreePage(&sCheck, aRoot[i], "List of tree roots: "); + checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL); } /* Make sure every page in the file is referenced diff --git a/src/build.c b/src/build.c index 6f656d840b..8e343864fa 100644 --- a/src/build.c +++ b/src/build.c @@ -202,7 +202,7 @@ void sqlite3FinishCoding(Parse *pParse){ pParse->isMultiWrite && pParse->mayAbort); pParse->rc = SQLITE_DONE; pParse->colNamesSet = 0; - }else if( pParse->rc==SQLITE_OK ){ + }else{ pParse->rc = SQLITE_ERROR; } pParse->nTab = 0; @@ -1974,13 +1974,12 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ } assert( pParse->nErr==0 ); assert( pName->nSrc==1 ); + if( noErr ) db->suppressErr++; pTab = sqlite3LocateTable(pParse, isView, pName->a[0].zName, pName->a[0].zDatabase); + if( noErr ) db->suppressErr--; if( pTab==0 ){ - if( noErr ){ - sqlite3ErrorClear(pParse); - } goto exit_drop_table; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); diff --git a/src/complete.c b/src/complete.c index 9799a6ae30..9e9140085c 100644 --- a/src/complete.c +++ b/src/complete.c @@ -1,283 +1,283 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** An tokenizer for SQL -** -** This file contains C code that implements the sqlite3_complete() API. -** This code used to be part of the tokenizer.c source file. But by -** separating it out, the code will be automatically omitted from -** static links that do not use it. -*/ -#include "sqliteInt.h" -#ifndef SQLITE_OMIT_COMPLETE - -/* -** This is defined in tokenize.c. We just have to import the definition. -*/ -#ifndef SQLITE_AMALGAMATION -#ifdef SQLITE_ASCII -#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0) -#endif -#ifdef SQLITE_EBCDIC -extern const char sqlite3IsEbcdicIdChar[]; -#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40])) -#endif -#endif /* SQLITE_AMALGAMATION */ - - -/* -** Token types used by the sqlite3_complete() routine. See the header -** comments on that procedure for additional information. -*/ -#define tkSEMI 0 -#define tkWS 1 -#define tkOTHER 2 -#ifndef SQLITE_OMIT_TRIGGER -#define tkEXPLAIN 3 -#define tkCREATE 4 -#define tkTEMP 5 -#define tkTRIGGER 6 -#define tkEND 7 -#endif - -/* -** Return TRUE if the given SQL string ends in a semicolon. -** -** Special handling is require for CREATE TRIGGER statements. -** Whenever the CREATE TRIGGER keywords are seen, the statement -** must end with ";END;". -** -** This implementation uses a state machine with 8 states: -** -** (0) INVALID We have not yet seen a non-whitespace character. -** -** (1) START At the beginning or end of an SQL statement. This routine -** returns 1 if it ends in the START state and 0 if it ends -** in any other state. -** -** (2) NORMAL We are in the middle of statement which ends with a single -** semicolon. -** -** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of -** a statement. -** -** (4) CREATE The keyword CREATE has been seen at the beginning of a -** statement, possibly preceeded by EXPLAIN and/or followed by -** TEMP or TEMPORARY -** -** (5) TRIGGER We are in the middle of a trigger definition that must be -** ended by a semicolon, the keyword END, and another semicolon. -** -** (6) SEMI We've seen the first semicolon in the ";END;" that occurs at -** the end of a trigger definition. -** -** (7) END We've seen the ";END" of the ";END;" that occurs at the end -** of a trigger difinition. -** -** Transitions between states above are determined by tokens extracted -** from the input. The following tokens are significant: -** -** (0) tkSEMI A semicolon. -** (1) tkWS Whitespace. -** (2) tkOTHER Any other SQL token. -** (3) tkEXPLAIN The "explain" keyword. -** (4) tkCREATE The "create" keyword. -** (5) tkTEMP The "temp" or "temporary" keyword. -** (6) tkTRIGGER The "trigger" keyword. -** (7) tkEND The "end" keyword. -** -** Whitespace never causes a state transition and is always ignored. -** This means that a SQL string of all whitespace is invalid. -** -** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed -** to recognize the end of a trigger can be omitted. All we have to do -** is look for a semicolon that is not part of an string or comment. -*/ -int sqlite3_complete(const char *zSql){ - u8 state = 0; /* Current state, using numbers defined in header comment */ - u8 token; /* Value of the next token */ - -#ifndef SQLITE_OMIT_TRIGGER - /* A complex statement machine used to detect the end of a CREATE TRIGGER - ** statement. This is the normal case. - */ - static const u8 trans[8][8] = { - /* Token: */ - /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ - /* 0 INVALID: */ { 1, 0, 2, 3, 4, 2, 2, 2, }, - /* 1 START: */ { 1, 1, 2, 3, 4, 2, 2, 2, }, - /* 2 NORMAL: */ { 1, 2, 2, 2, 2, 2, 2, 2, }, - /* 3 EXPLAIN: */ { 1, 3, 3, 2, 4, 2, 2, 2, }, - /* 4 CREATE: */ { 1, 4, 2, 2, 2, 4, 5, 2, }, - /* 5 TRIGGER: */ { 6, 5, 5, 5, 5, 5, 5, 5, }, - /* 6 SEMI: */ { 6, 6, 5, 5, 5, 5, 5, 7, }, - /* 7 END: */ { 1, 7, 5, 5, 5, 5, 5, 5, }, - }; -#else - /* If triggers are not supported by this compile then the statement machine - ** used to detect the end of a statement is much simplier - */ - static const u8 trans[3][3] = { - /* Token: */ - /* State: ** SEMI WS OTHER */ - /* 0 INVALID: */ { 1, 0, 2, }, - /* 1 START: */ { 1, 1, 2, }, - /* 2 NORMAL: */ { 1, 2, 2, }, - }; -#endif /* SQLITE_OMIT_TRIGGER */ - - while( *zSql ){ - switch( *zSql ){ - case ';': { /* A semicolon */ - token = tkSEMI; - break; - } - case ' ': - case '\r': - case '\t': - case '\n': - case '\f': { /* White space is ignored */ - token = tkWS; - break; - } - case '/': { /* C-style comments */ - if( zSql[1]!='*' ){ - token = tkOTHER; - break; - } - zSql += 2; - while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; } - if( zSql[0]==0 ) return 0; - zSql++; - token = tkWS; - break; - } - case '-': { /* SQL-style comments from "--" to end of line */ - if( zSql[1]!='-' ){ - token = tkOTHER; - break; - } - while( *zSql && *zSql!='\n' ){ zSql++; } - if( *zSql==0 ) return state==1; - token = tkWS; - break; - } - case '[': { /* Microsoft-style identifiers in [...] */ - zSql++; - while( *zSql && *zSql!=']' ){ zSql++; } - if( *zSql==0 ) return 0; - token = tkOTHER; - break; - } - case '`': /* Grave-accent quoted symbols used by MySQL */ - case '"': /* single- and double-quoted strings */ - case '\'': { - int c = *zSql; - zSql++; - while( *zSql && *zSql!=c ){ zSql++; } - if( *zSql==0 ) return 0; - token = tkOTHER; - break; - } - default: { -#ifdef SQLITE_EBCDIC - unsigned char c; -#endif - if( IdChar((u8)*zSql) ){ - /* Keywords and unquoted identifiers */ - int nId; - for(nId=1; IdChar(zSql[nId]); nId++){} -#ifdef SQLITE_OMIT_TRIGGER - token = tkOTHER; -#else - switch( *zSql ){ - case 'c': case 'C': { - if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){ - token = tkCREATE; - }else{ - token = tkOTHER; - } - break; - } - case 't': case 'T': { - if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){ - token = tkTRIGGER; - }else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){ - token = tkTEMP; - }else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){ - token = tkTEMP; - }else{ - token = tkOTHER; - } - break; - } - case 'e': case 'E': { - if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){ - token = tkEND; - }else -#ifndef SQLITE_OMIT_EXPLAIN - if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){ - token = tkEXPLAIN; - }else -#endif - { - token = tkOTHER; - } - break; - } - default: { - token = tkOTHER; - break; - } - } -#endif /* SQLITE_OMIT_TRIGGER */ - zSql += nId-1; - }else{ - /* Operators and special symbols */ - token = tkOTHER; - } - break; - } - } - state = trans[state][token]; - zSql++; - } - return state==1; -} - -#ifndef SQLITE_OMIT_UTF16 -/* -** This routine is the same as the sqlite3_complete() routine described -** above, except that the parameter is required to be UTF-16 encoded, not -** UTF-8. -*/ -int sqlite3_complete16(const void *zSql){ - sqlite3_value *pVal; - char const *zSql8; - int rc = SQLITE_NOMEM; - -#ifndef SQLITE_OMIT_AUTOINIT - rc = sqlite3_initialize(); - if( rc ) return rc; -#endif - pVal = sqlite3ValueNew(0); - sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC); - zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8); - if( zSql8 ){ - rc = sqlite3_complete(zSql8); - }else{ - rc = SQLITE_NOMEM; - } - sqlite3ValueFree(pVal); - return sqlite3ApiExit(0, rc); -} -#endif /* SQLITE_OMIT_UTF16 */ -#endif /* SQLITE_OMIT_COMPLETE */ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** An tokenizer for SQL +** +** This file contains C code that implements the sqlite3_complete() API. +** This code used to be part of the tokenizer.c source file. But by +** separating it out, the code will be automatically omitted from +** static links that do not use it. +*/ +#include "sqliteInt.h" +#ifndef SQLITE_OMIT_COMPLETE + +/* +** This is defined in tokenize.c. We just have to import the definition. +*/ +#ifndef SQLITE_AMALGAMATION +#ifdef SQLITE_ASCII +#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0) +#endif +#ifdef SQLITE_EBCDIC +extern const char sqlite3IsEbcdicIdChar[]; +#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40])) +#endif +#endif /* SQLITE_AMALGAMATION */ + + +/* +** Token types used by the sqlite3_complete() routine. See the header +** comments on that procedure for additional information. +*/ +#define tkSEMI 0 +#define tkWS 1 +#define tkOTHER 2 +#ifndef SQLITE_OMIT_TRIGGER +#define tkEXPLAIN 3 +#define tkCREATE 4 +#define tkTEMP 5 +#define tkTRIGGER 6 +#define tkEND 7 +#endif + +/* +** Return TRUE if the given SQL string ends in a semicolon. +** +** Special handling is require for CREATE TRIGGER statements. +** Whenever the CREATE TRIGGER keywords are seen, the statement +** must end with ";END;". +** +** This implementation uses a state machine with 8 states: +** +** (0) INVALID We have not yet seen a non-whitespace character. +** +** (1) START At the beginning or end of an SQL statement. This routine +** returns 1 if it ends in the START state and 0 if it ends +** in any other state. +** +** (2) NORMAL We are in the middle of statement which ends with a single +** semicolon. +** +** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of +** a statement. +** +** (4) CREATE The keyword CREATE has been seen at the beginning of a +** statement, possibly preceeded by EXPLAIN and/or followed by +** TEMP or TEMPORARY +** +** (5) TRIGGER We are in the middle of a trigger definition that must be +** ended by a semicolon, the keyword END, and another semicolon. +** +** (6) SEMI We've seen the first semicolon in the ";END;" that occurs at +** the end of a trigger definition. +** +** (7) END We've seen the ";END" of the ";END;" that occurs at the end +** of a trigger difinition. +** +** Transitions between states above are determined by tokens extracted +** from the input. The following tokens are significant: +** +** (0) tkSEMI A semicolon. +** (1) tkWS Whitespace. +** (2) tkOTHER Any other SQL token. +** (3) tkEXPLAIN The "explain" keyword. +** (4) tkCREATE The "create" keyword. +** (5) tkTEMP The "temp" or "temporary" keyword. +** (6) tkTRIGGER The "trigger" keyword. +** (7) tkEND The "end" keyword. +** +** Whitespace never causes a state transition and is always ignored. +** This means that a SQL string of all whitespace is invalid. +** +** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed +** to recognize the end of a trigger can be omitted. All we have to do +** is look for a semicolon that is not part of an string or comment. +*/ +int sqlite3_complete(const char *zSql){ + u8 state = 0; /* Current state, using numbers defined in header comment */ + u8 token; /* Value of the next token */ + +#ifndef SQLITE_OMIT_TRIGGER + /* A complex statement machine used to detect the end of a CREATE TRIGGER + ** statement. This is the normal case. + */ + static const u8 trans[8][8] = { + /* Token: */ + /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ + /* 0 INVALID: */ { 1, 0, 2, 3, 4, 2, 2, 2, }, + /* 1 START: */ { 1, 1, 2, 3, 4, 2, 2, 2, }, + /* 2 NORMAL: */ { 1, 2, 2, 2, 2, 2, 2, 2, }, + /* 3 EXPLAIN: */ { 1, 3, 3, 2, 4, 2, 2, 2, }, + /* 4 CREATE: */ { 1, 4, 2, 2, 2, 4, 5, 2, }, + /* 5 TRIGGER: */ { 6, 5, 5, 5, 5, 5, 5, 5, }, + /* 6 SEMI: */ { 6, 6, 5, 5, 5, 5, 5, 7, }, + /* 7 END: */ { 1, 7, 5, 5, 5, 5, 5, 5, }, + }; +#else + /* If triggers are not supported by this compile then the statement machine + ** used to detect the end of a statement is much simplier + */ + static const u8 trans[3][3] = { + /* Token: */ + /* State: ** SEMI WS OTHER */ + /* 0 INVALID: */ { 1, 0, 2, }, + /* 1 START: */ { 1, 1, 2, }, + /* 2 NORMAL: */ { 1, 2, 2, }, + }; +#endif /* SQLITE_OMIT_TRIGGER */ + + while( *zSql ){ + switch( *zSql ){ + case ';': { /* A semicolon */ + token = tkSEMI; + break; + } + case ' ': + case '\r': + case '\t': + case '\n': + case '\f': { /* White space is ignored */ + token = tkWS; + break; + } + case '/': { /* C-style comments */ + if( zSql[1]!='*' ){ + token = tkOTHER; + break; + } + zSql += 2; + while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; } + if( zSql[0]==0 ) return 0; + zSql++; + token = tkWS; + break; + } + case '-': { /* SQL-style comments from "--" to end of line */ + if( zSql[1]!='-' ){ + token = tkOTHER; + break; + } + while( *zSql && *zSql!='\n' ){ zSql++; } + if( *zSql==0 ) return state==1; + token = tkWS; + break; + } + case '[': { /* Microsoft-style identifiers in [...] */ + zSql++; + while( *zSql && *zSql!=']' ){ zSql++; } + if( *zSql==0 ) return 0; + token = tkOTHER; + break; + } + case '`': /* Grave-accent quoted symbols used by MySQL */ + case '"': /* single- and double-quoted strings */ + case '\'': { + int c = *zSql; + zSql++; + while( *zSql && *zSql!=c ){ zSql++; } + if( *zSql==0 ) return 0; + token = tkOTHER; + break; + } + default: { +#ifdef SQLITE_EBCDIC + unsigned char c; +#endif + if( IdChar((u8)*zSql) ){ + /* Keywords and unquoted identifiers */ + int nId; + for(nId=1; IdChar(zSql[nId]); nId++){} +#ifdef SQLITE_OMIT_TRIGGER + token = tkOTHER; +#else + switch( *zSql ){ + case 'c': case 'C': { + if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){ + token = tkCREATE; + }else{ + token = tkOTHER; + } + break; + } + case 't': case 'T': { + if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){ + token = tkTRIGGER; + }else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){ + token = tkTEMP; + }else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){ + token = tkTEMP; + }else{ + token = tkOTHER; + } + break; + } + case 'e': case 'E': { + if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){ + token = tkEND; + }else +#ifndef SQLITE_OMIT_EXPLAIN + if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){ + token = tkEXPLAIN; + }else +#endif + { + token = tkOTHER; + } + break; + } + default: { + token = tkOTHER; + break; + } + } +#endif /* SQLITE_OMIT_TRIGGER */ + zSql += nId-1; + }else{ + /* Operators and special symbols */ + token = tkOTHER; + } + break; + } + } + state = trans[state][token]; + zSql++; + } + return state==1; +} + +#ifndef SQLITE_OMIT_UTF16 +/* +** This routine is the same as the sqlite3_complete() routine described +** above, except that the parameter is required to be UTF-16 encoded, not +** UTF-8. +*/ +int sqlite3_complete16(const void *zSql){ + sqlite3_value *pVal; + char const *zSql8; + int rc = SQLITE_NOMEM; + +#ifndef SQLITE_OMIT_AUTOINIT + rc = sqlite3_initialize(); + if( rc ) return rc; +#endif + pVal = sqlite3ValueNew(0); + sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC); + zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8); + if( zSql8 ){ + rc = sqlite3_complete(zSql8); + }else{ + rc = SQLITE_NOMEM; + } + sqlite3ValueFree(pVal); + return sqlite3ApiExit(0, rc); +} +#endif /* SQLITE_OMIT_UTF16 */ +#endif /* SQLITE_OMIT_COMPLETE */ diff --git a/src/ctime.c b/src/ctime.c new file mode 100644 index 0000000000..dacdc5883c --- /dev/null +++ b/src/ctime.c @@ -0,0 +1,385 @@ +/* +** 2010 February 23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements routines used to report what compile-time options +** SQLite was built with. +*/ + +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS + +#include "sqliteInt.h" + +/* +** An array of names of all compile-time options. This array should +** be sorted A-Z. +** +** This array looks large, but in a typical installation actually uses +** only a handful of compile-time options, so most times this array is usually +** rather short and uses little memory space. +*/ +static const char * const azCompileOpt[] = { + +/* These macros are provided to "stringify" the value of the define +** for those options in which the value is meaningful. */ +#define CTIMEOPT_VAL_(opt) #opt +#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) + +#ifdef SQLITE_32BIT_ROWID + "32BIT_ROWID", +#endif +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC + "4_BYTE_ALIGNED_MALLOC", +#endif +#ifdef SQLITE_CASE_SENSITIVE_LIKE + "CASE_SENSITIVE_LIKE", +#endif +#ifdef SQLITE_CHECK_PAGES + "CHECK_PAGES", +#endif +#ifdef SQLITE_COVERAGE_TEST + "COVERAGE_TEST", +#endif +#ifdef SQLITE_DEBUG + "DEBUG", +#endif +#ifdef SQLITE_DEFAULT_LOCKING_MODE + "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), +#endif +#ifdef SQLITE_DISABLE_DIRSYNC + "DISABLE_DIRSYNC", +#endif +#ifdef SQLITE_DISABLE_LFS + "DISABLE_LFS", +#endif +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + "ENABLE_ATOMIC_WRITE", +#endif +#ifdef SQLITE_ENABLE_CEROD + "ENABLE_CEROD", +#endif +#ifdef SQLITE_ENABLE_COLUMN_METADATA + "ENABLE_COLUMN_METADATA", +#endif +#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT + "ENABLE_EXPENSIVE_ASSERT", +#endif +#ifdef SQLITE_ENABLE_FTS1 + "ENABLE_FTS1", +#endif +#ifdef SQLITE_ENABLE_FTS2 + "ENABLE_FTS2", +#endif +#ifdef SQLITE_ENABLE_FTS3 + "ENABLE_FTS3", +#endif +#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS + "ENABLE_FTS3_PARENTHESIS", +#endif +#ifdef SQLITE_ENABLE_FTS4 + "ENABLE_FTS4", +#endif +#ifdef SQLITE_ENABLE_ICU + "ENABLE_ICU", +#endif +#ifdef SQLITE_ENABLE_IOTRACE + "ENABLE_IOTRACE", +#endif +#ifdef SQLITE_ENABLE_LOAD_EXTENSION + "ENABLE_LOAD_EXTENSION", +#endif +#ifdef SQLITE_ENABLE_LOCKING_STYLE + "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), +#endif +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + "ENABLE_MEMORY_MANAGEMENT", +#endif +#ifdef SQLITE_ENABLE_MEMSYS3 + "ENABLE_MEMSYS3", +#endif +#ifdef SQLITE_ENABLE_MEMSYS5 + "ENABLE_MEMSYS5", +#endif +#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK + "ENABLE_OVERSIZE_CELL_CHECK", +#endif +#ifdef SQLITE_ENABLE_RTREE + "ENABLE_RTREE", +#endif +#ifdef SQLITE_ENABLE_STAT2 + "ENABLE_STAT2", +#endif +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + "ENABLE_UNLOCK_NOTIFY", +#endif +#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + "ENABLE_UPDATE_DELETE_LIMIT", +#endif +#ifdef SQLITE_HAS_CODEC + "HAS_CODEC", +#endif +#ifdef SQLITE_HAVE_ISNAN + "HAVE_ISNAN", +#endif +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX + "HOMEGROWN_RECURSIVE_MUTEX", +#endif +#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS + "IGNORE_AFP_LOCK_ERRORS", +#endif +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + "IGNORE_FLOCK_LOCK_ERRORS", +#endif +#ifdef SQLITE_INT64_TYPE + "INT64_TYPE", +#endif +#ifdef SQLITE_LOCK_TRACE + "LOCK_TRACE", +#endif +#ifdef SQLITE_MEMDEBUG + "MEMDEBUG", +#endif +#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT + "MIXED_ENDIAN_64BIT_FLOAT", +#endif +#ifdef SQLITE_NO_SYNC + "NO_SYNC", +#endif +#ifdef SQLITE_OMIT_ALTERTABLE + "OMIT_ALTERTABLE", +#endif +#ifdef SQLITE_OMIT_ANALYZE + "OMIT_ANALYZE", +#endif +#ifdef SQLITE_OMIT_ATTACH + "OMIT_ATTACH", +#endif +#ifdef SQLITE_OMIT_AUTHORIZATION + "OMIT_AUTHORIZATION", +#endif +#ifdef SQLITE_OMIT_AUTOINCREMENT + "OMIT_AUTOINCREMENT", +#endif +#ifdef SQLITE_OMIT_AUTOINIT + "OMIT_AUTOINIT", +#endif +#ifdef SQLITE_OMIT_AUTOVACUUM + "OMIT_AUTOVACUUM", +#endif +#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION + "OMIT_BETWEEN_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_BLOB_LITERAL + "OMIT_BLOB_LITERAL", +#endif +#ifdef SQLITE_OMIT_BTREECOUNT + "OMIT_BTREECOUNT", +#endif +#ifdef SQLITE_OMIT_BUILTIN_TEST + "OMIT_BUILTIN_TEST", +#endif +#ifdef SQLITE_OMIT_CAST + "OMIT_CAST", +#endif +#ifdef SQLITE_OMIT_CHECK + "OMIT_CHECK", +#endif +#ifdef SQLITE_OMIT_COMPILEOPTION_DIAGS + "OMIT_COMPILEOPTION_DIAGS", +#endif +#ifdef SQLITE_OMIT_COMPLETE + "OMIT_COMPLETE", +#endif +#ifdef SQLITE_OMIT_COMPOUND_SELECT + "OMIT_COMPOUND_SELECT", +#endif +#ifdef SQLITE_OMIT_DATETIME_FUNCS + "OMIT_DATETIME_FUNCS", +#endif +#ifdef SQLITE_OMIT_DECLTYPE + "OMIT_DECLTYPE", +#endif +#ifdef SQLITE_OMIT_DEPRECATED + "OMIT_DEPRECATED", +#endif +#ifdef SQLITE_OMIT_DISKIO + "OMIT_DISKIO", +#endif +#ifdef SQLITE_OMIT_EXPLAIN + "OMIT_EXPLAIN", +#endif +#ifdef SQLITE_OMIT_FLAG_PRAGMAS + "OMIT_FLAG_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_FLOATING_POINT + "OMIT_FLOATING_POINT", +#endif +#ifdef SQLITE_OMIT_FOREIGN_KEY + "OMIT_FOREIGN_KEY", +#endif +#ifdef SQLITE_OMIT_GET_TABLE + "OMIT_GET_TABLE", +#endif +#ifdef SQLITE_OMIT_GLOBALRECOVER + "OMIT_GLOBALRECOVER", +#endif +#ifdef SQLITE_OMIT_INCRBLOB + "OMIT_INCRBLOB", +#endif +#ifdef SQLITE_OMIT_INTEGRITY_CHECK + "OMIT_INTEGRITY_CHECK", +#endif +#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION + "OMIT_LIKE_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_LOAD_EXTENSION + "OMIT_LOAD_EXTENSION", +#endif +#ifdef SQLITE_OMIT_LOCALTIME + "OMIT_LOCALTIME", +#endif +#ifdef SQLITE_OMIT_LOOKASIDE + "OMIT_LOOKASIDE", +#endif +#ifdef SQLITE_OMIT_MEMORYDB + "OMIT_MEMORYDB", +#endif +#ifdef SQLITE_OMIT_OR_OPTIMIZATION + "OMIT_OR_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_PAGER_PRAGMAS + "OMIT_PAGER_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_PRAGMA + "OMIT_PRAGMA", +#endif +#ifdef SQLITE_OMIT_PROGRESS_CALLBACK + "OMIT_PROGRESS_CALLBACK", +#endif +#ifdef SQLITE_OMIT_QUICKBALANCE + "OMIT_QUICKBALANCE", +#endif +#ifdef SQLITE_OMIT_REINDEX + "OMIT_REINDEX", +#endif +#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS + "OMIT_SCHEMA_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS + "OMIT_SCHEMA_VERSION_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_SHARED_CACHE + "OMIT_SHARED_CACHE", +#endif +#ifdef SQLITE_OMIT_SUBQUERY + "OMIT_SUBQUERY", +#endif +#ifdef SQLITE_OMIT_TCL_VARIABLE + "OMIT_TCL_VARIABLE", +#endif +#ifdef SQLITE_OMIT_TEMPDB + "OMIT_TEMPDB", +#endif +#ifdef SQLITE_OMIT_TRACE + "OMIT_TRACE", +#endif +#ifdef SQLITE_OMIT_TRIGGER + "OMIT_TRIGGER", +#endif +#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION + "OMIT_TRUNCATE_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_UTF16 + "OMIT_UTF16", +#endif +#ifdef SQLITE_OMIT_VACUUM + "OMIT_VACUUM", +#endif +#ifdef SQLITE_OMIT_VIEW + "OMIT_VIEW", +#endif +#ifdef SQLITE_OMIT_VIRTUALTABLE + "OMIT_VIRTUALTABLE", +#endif +#ifdef SQLITE_OMIT_WSD + "OMIT_WSD", +#endif +#ifdef SQLITE_OMIT_XFER_OPT + "OMIT_XFER_OPT", +#endif +#ifdef SQLITE_PERFORMANCE_TRACE + "PERFORMANCE_TRACE", +#endif +#ifdef SQLITE_PROXY_DEBUG + "PROXY_DEBUG", +#endif +#ifdef SQLITE_SECURE_DELETE + "SECURE_DELETE", +#endif +#ifdef SQLITE_SMALL_STACK + "SMALL_STACK", +#endif +#ifdef SQLITE_SOUNDEX + "SOUNDEX", +#endif +#ifdef SQLITE_TCL + "TCL", +#endif +#ifdef SQLITE_TEMP_STORE + "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), +#endif +#ifdef SQLITE_TEST + "TEST", +#endif +#ifdef SQLITE_THREADSAFE + "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), +#endif +#ifdef SQLITE_USE_ALLOCA + "USE_ALLOCA", +#endif +#ifdef SQLITE_ZERO_MALLOC + "ZERO_MALLOC" +#endif +}; + +/* +** Given the name of a compile-time option, return true if that option +** was used and false if not. +** +** The name can optionally begin with "SQLITE_" but the "SQLITE_" prefix +** is not required for a match. +*/ +int sqlite3_compileoption_used(const char *zOptName){ + int i, n; + if( sqlite3StrNICmp(zOptName, "SQLITE_", 7)==0 ) zOptName += 7; + n = sqlite3Strlen30(zOptName); + + /* Since ArraySize(azCompileOpt) is normally in single digits, a + ** linear search is adequate. No need for a binary search. */ + for(i=0; i=0 && N=0 && rdb->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } - sqlite3MultiWrite(pParse); if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ + sqlite3MultiWrite(pParse); sqlite3GenerateRowDelete( pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace ); - }else{ + }else if( pTab->pIndex ){ + sqlite3MultiWrite(pParse); sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); } seenReplace = 1; diff --git a/src/legacy.c b/src/legacy.c index 871e15863c..ebab2de37d 100644 --- a/src/legacy.c +++ b/src/legacy.c @@ -41,7 +41,7 @@ int sqlite3_exec( int nRetry = 0; /* Number of retry attempts */ int callbackIsInit; /* True if callback data is initialized */ - if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE; + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; if( zSql==0 ) zSql = ""; sqlite3_mutex_enter(db->mutex); diff --git a/src/main.c b/src/main.c index 07fe15ce72..879907fadb 100644 --- a/src/main.c +++ b/src/main.c @@ -257,7 +257,7 @@ int sqlite3_config(int op, ...){ /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while ** the SQLite library is in use. */ - if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE; + if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT; va_start(ap, op); switch( op ){ @@ -378,6 +378,21 @@ int sqlite3_config(int op, ...){ sqlite3GlobalConfig.nLookaside = va_arg(ap, int); break; } + + /* Record a pointer to the logger funcction and its first argument. + ** The default is NULL. Logging is disabled if the function pointer is + ** NULL. + */ + case SQLITE_CONFIG_LOG: { + /* MSVC is picky about pulling func ptrs from va lists. + ** http://support.microsoft.com/kb/47961 + ** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*)); + */ + typedef void(*LOGFUNC_t)(void*,int,const char*); + sqlite3GlobalConfig.xLog = va_arg(ap, LOGFUNC_t); + sqlite3GlobalConfig.pLogArg = va_arg(ap, void*); + break; + } default: { rc = SQLITE_ERROR; @@ -591,7 +606,7 @@ int sqlite3_close(sqlite3 *db){ return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); @@ -938,7 +953,7 @@ int sqlite3CreateFunc( (!xFunc && (!xFinal && xStep)) || (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) || (255<(nName = sqlite3Strlen30( zFunctionName))) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } #ifndef SQLITE_OMIT_UTF16 @@ -1269,7 +1284,7 @@ const char *sqlite3_errmsg(sqlite3 *db){ return sqlite3ErrStr(SQLITE_NOMEM); } if( !sqlite3SafetyCheckSickOrOk(db) ){ - return sqlite3ErrStr(SQLITE_MISUSE); + return sqlite3ErrStr(SQLITE_MISUSE_BKPT); } sqlite3_mutex_enter(db->mutex); if( db->mallocFailed ){ @@ -1338,7 +1353,7 @@ const void *sqlite3_errmsg16(sqlite3 *db){ */ int sqlite3_errcode(sqlite3 *db){ if( db && !sqlite3SafetyCheckSickOrOk(db) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } if( !db || db->mallocFailed ){ return SQLITE_NOMEM; @@ -1347,7 +1362,7 @@ int sqlite3_errcode(sqlite3 *db){ } int sqlite3_extended_errcode(sqlite3 *db){ if( db && !sqlite3SafetyCheckSickOrOk(db) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } if( !db || db->mallocFailed ){ return SQLITE_NOMEM; @@ -1385,7 +1400,7 @@ static int createCollation( enc2 = SQLITE_UTF16NATIVE; } if( enc2SQLITE_UTF16BE ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } /* Check if this call is removing or replacing an existing collation @@ -1929,16 +1944,34 @@ int sqlite3_get_autocommit(sqlite3 *db){ return db->autoCommit; } -#ifdef SQLITE_DEBUG /* -** The following routine is subtituted for constant SQLITE_CORRUPT in -** debugging builds. This provides a way to set a breakpoint for when -** corruption is first detected. +** The following routines are subtitutes for constants SQLITE_CORRUPT, +** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_IOERR and possibly other error +** constants. They server two purposes: +** +** 1. Serve as a convenient place to set a breakpoint in a debugger +** to detect when version error conditions occurs. +** +** 2. Invoke sqlite3_log() to provide the source code location where +** a low-level error is first detected. */ -int sqlite3Corrupt(void){ +int sqlite3CorruptError(int lineno){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(SQLITE_CORRUPT, + "database corruption found by source line %d", lineno); return SQLITE_CORRUPT; } -#endif +int sqlite3MisuseError(int lineno){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(SQLITE_MISUSE, "misuse detected by source line %d", lineno); + return SQLITE_MISUSE; +} +int sqlite3CantopenError(int lineno){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(SQLITE_CANTOPEN, "cannot open file at source line %d", lineno); + return SQLITE_CANTOPEN; +} + #ifndef SQLITE_OMIT_DEPRECATED /* @@ -1982,7 +2015,6 @@ int sqlite3_table_column_metadata( /* Ensure the database schema has been loaded */ sqlite3_mutex_enter(db->mutex); - (void)sqlite3SafetyOn(db); sqlite3BtreeEnterAll(db); rc = sqlite3Init(db, &zErrMsg); if( SQLITE_OK!=rc ){ @@ -2041,7 +2073,6 @@ int sqlite3_table_column_metadata( error_out: sqlite3BtreeLeaveAll(db); - (void)sqlite3SafetyOff(db); /* Whether the function call succeeded or failed, set the output parameters ** to whatever their local counterparts contain. If an error did occur, diff --git a/src/mem1.c b/src/mem1.c index fbfa35eb91..1a01839992 100644 --- a/src/mem1.c +++ b/src/mem1.c @@ -42,6 +42,9 @@ static void *sqlite3MemMalloc(int nByte){ if( p ){ p[0] = nByte; p++; + }else{ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); } return (void *)p; } @@ -61,6 +64,18 @@ static void sqlite3MemFree(void *pPrior){ free(p); } +/* +** Report the allocated size of a prior return from xMalloc() +** or xRealloc(). +*/ +static int sqlite3MemSize(void *pPrior){ + sqlite3_int64 *p; + if( pPrior==0 ) return 0; + p = (sqlite3_int64*)pPrior; + p--; + return (int)p[0]; +} + /* ** Like realloc(). Resize an allocation previously obtained from ** sqlite3MemMalloc(). @@ -75,28 +90,20 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){ sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 && nByte>0 ); nByte = ROUND8(nByte); - p = (sqlite3_int64*)pPrior; p--; p = realloc(p, nByte+8 ); if( p ){ p[0] = nByte; p++; + }else{ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(SQLITE_NOMEM, + "failed memory resize %u to %u bytes", + sqlite3MemSize(pPrior), nByte); } return (void*)p; } -/* -** Report the allocated size of a prior return from xMalloc() -** or xRealloc(). -*/ -static int sqlite3MemSize(void *pPrior){ - sqlite3_int64 *p; - if( pPrior==0 ) return 0; - p = (sqlite3_int64*)pPrior; - p--; - return (int)p[0]; -} - /* ** Round up a request size to the next valid allocation size. */ diff --git a/src/mem5.c b/src/mem5.c index 3fe04e2455..a828cf812e 100644 --- a/src/mem5.c +++ b/src/mem5.c @@ -268,7 +268,11 @@ static void *memsys5MallocUnsafe(int nByte){ ** two in order to create a new free block of size iLogsize. */ for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){} - if( iBin>LOGMAX ) return 0; + if( iBin>LOGMAX ){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte); + return 0; + } i = memsys5UnlinkFirst(iBin); while( iBin>iLogsize ){ int newSize; diff --git a/src/os_unix.c b/src/os_unix.c index 9dc2075579..88263df664 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -1111,7 +1111,7 @@ static int transferOwnership(unixFile *pFile){ } if( pFile->locktype!=NO_LOCK ){ /* We cannot change ownership while we are holding a lock! */ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } OSTRACE4("Transfer ownership of %d from %d to %d\n", pFile->h, pFile->tid, hSelf); @@ -1248,7 +1248,7 @@ static int unixLock(sqlite3_file *id, int locktype){ struct unixLockInfo *pLock = pFile->pLock; struct flock lock; int s = 0; - int tErrno; + int tErrno = 0; assert( pFile ); OSTRACE7("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, @@ -1512,7 +1512,7 @@ static int _posixUnlock(sqlite3_file *id, int locktype, int handleNFSUnlock){ return SQLITE_OK; } if( CHECK_THREADID(pFile) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } unixEnterMutex(); h = pFile->h; @@ -2735,7 +2735,7 @@ static int afpUnlock(sqlite3_file *id, int locktype) { return SQLITE_OK; } if( CHECK_THREADID(pFile) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } unixEnterMutex(); pLock = pFile->pLock; @@ -3867,7 +3867,7 @@ static int openDirectory(const char *zFilename, int *pFd){ } } *pFd = fd; - return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN); + return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT); } /* @@ -4130,7 +4130,7 @@ static int unixOpen( fd = open(zName, openFlags, openMode); } if( fd<0 ){ - rc = SQLITE_CANTOPEN; + rc = SQLITE_CANTOPEN_BKPT; goto open_finished; } } @@ -4354,7 +4354,7 @@ static int unixFullPathname( }else{ int nCwd; if( getcwd(zOut, nOut-1)==0 ){ - return SQLITE_CANTOPEN; + return SQLITE_CANTOPEN_BKPT; } nCwd = (int)strlen(zOut); sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); @@ -4865,7 +4865,7 @@ static int proxyCreateUnixFile( case EIO: return SQLITE_IOERR_LOCK; /* even though it is the conch */ default: - return SQLITE_CANTOPEN; + return SQLITE_CANTOPEN_BKPT; } } @@ -5249,7 +5249,7 @@ static int proxyTakeConch(unixFile *pFile){ if( fd>=0 ){ pFile->h = fd; }else{ - rc=SQLITE_CANTOPEN; /* SQLITE_BUSY? proxyTakeConch called + rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called during locking */ } } @@ -5262,7 +5262,7 @@ static int proxyTakeConch(unixFile *pFile){ */ forceNewLockPath = 1; tryOldLockPath = 0; - continue; // go back to the do {} while start point, try again + continue; /* go back to the do {} while start point, try again */ } } if( rc==SQLITE_OK ){ diff --git a/src/os_win.c b/src/os_win.c index bd23a848d3..4721eadff8 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -1417,7 +1417,7 @@ static int winOpen( return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); }else{ - return SQLITE_CANTOPEN; + return SQLITE_CANTOPEN_BKPT; } } if( pOutFlags ){ @@ -1439,7 +1439,7 @@ static int winOpen( ){ CloseHandle(h); free(zConverted); - return SQLITE_CANTOPEN; + return SQLITE_CANTOPEN_BKPT; } if( isTemp ){ pFile->zDeleteOnClose = zConverted; diff --git a/src/pager.c b/src/pager.c index 83f02940ac..97a6a8e6eb 100644 --- a/src/pager.c +++ b/src/pager.c @@ -3162,7 +3162,7 @@ int sqlite3PagerOpen( ** as it will not be possible to open the journal file or even ** check for a hot-journal before reading. */ - rc = SQLITE_CANTOPEN; + rc = SQLITE_CANTOPEN_BKPT; } if( rc!=SQLITE_OK ){ sqlite3_free(zPathname); @@ -3621,7 +3621,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){ - rc = SQLITE_CANTOPEN; + rc = SQLITE_CANTOPEN_BKPT; sqlite3OsClose(pPager->jfd); } }else{ @@ -3840,7 +3840,7 @@ int sqlite3PagerAcquire( goto pager_acquire_err; } - if( MEMDB || nMax<(int)pgno || noContent ){ + if( MEMDB || nMax<(int)pgno || noContent || !isOpen(pPager->fd) ){ if( pgno>pPager->mxPgno ){ rc = SQLITE_FULL; goto pager_acquire_err; diff --git a/src/pcache.c b/src/pcache.c index 257134ab7b..41536e8bef 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -189,6 +189,7 @@ void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ if( pCache->pCache ){ sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache); pCache->pCache = 0; + pCache->pPage1 = 0; } pCache->szPage = szPage; } diff --git a/src/pragma.c b/src/pragma.c index bcc85cbb77..bd96e47c18 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -285,6 +285,7 @@ void sqlite3Pragma( Db *pDb; Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db); if( v==0 ) return; + sqlite3VdbeRunOnlyOnce(v); pParse->nMem = 2; /* Interpret the [database.] part of the pragma statement. iDb is the @@ -1359,6 +1360,26 @@ void sqlite3Pragma( }else #endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */ +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS + /* + ** PRAGMA compile_options + ** + ** Return the names of all compile-time options used in this build, + ** one option per row. + */ + if( sqlite3StrICmp(zLeft, "compile_options")==0 ){ + int i = 0; + const char *zOpt; + sqlite3VdbeSetNumCols(v, 1); + pParse->nMem = 1; + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "compile_option", SQLITE_STATIC); + while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){ + sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0); + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + } + }else +#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ + #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) /* ** Report the current state of file logs for all databases @@ -1393,7 +1414,7 @@ void sqlite3Pragma( }else #endif -#if SQLITE_HAS_CODEC +#ifdef SQLITE_HAS_CODEC if( sqlite3StrICmp(zLeft, "key")==0 && zRight ){ sqlite3_key(db, zRight, sqlite3Strlen30(zRight)); }else @@ -1416,9 +1437,9 @@ void sqlite3Pragma( } }else #endif -#if SQLITE_HAS_CODEC || defined(SQLITE_ENABLE_CEROD) +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ -#if SQLITE_HAS_CODEC +#ifdef SQLITE_HAS_CODEC if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ sqlite3_activate_see(&zRight[4]); } @@ -1434,12 +1455,6 @@ void sqlite3Pragma( {/* Empty ELSE clause */} - /* Code an OP_Expire at the end of each PRAGMA program to cause - ** the VDBE implementing the pragma to expire. Most (all?) pragmas - ** are only valid for a single execution. - */ - sqlite3VdbeAddOp2(v, OP_Expire, 1, 0); - /* ** Reset the safety level, in case the fullfsync flag or synchronous ** setting changed. diff --git a/src/prepare.c b/src/prepare.c index 15aafbeadb..c15cd13020 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -192,9 +192,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ initData.iDb = iDb; initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; - (void)sqlite3SafetyOff(db); sqlite3InitCallback(&initData, 3, (char **)azArg, 0); - (void)sqlite3SafetyOn(db); if( initData.rc ){ rc = initData.rc; goto error_out; @@ -317,7 +315,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ zSql = sqlite3MPrintf(db, "SELECT name, rootpage, sql FROM '%q'.%s ORDER BY rowid", db->aDb[iDb].zName, zMasterName); - (void)sqlite3SafetyOff(db); #ifndef SQLITE_OMIT_AUTHORIZATION { int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); @@ -330,7 +327,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ } #endif if( rc==SQLITE_OK ) rc = initData.rc; - (void)sqlite3SafetyOn(db); sqlite3DbFree(db, zSql); #ifndef SQLITE_OMIT_ANALYZE if( rc==SQLITE_OK ){ @@ -539,11 +535,6 @@ static int sqlite3Prepare( goto end_prepare; } pParse->pReprepare = pReprepare; - - if( sqlite3SafetyOn(db) ){ - rc = SQLITE_MISUSE; - goto end_prepare; - } assert( ppStmt && *ppStmt==0 ); assert( !db->mallocFailed ); assert( sqlite3_mutex_held(db->mutex) ); @@ -579,7 +570,6 @@ static int sqlite3Prepare( if( rc ){ const char *zDb = db->aDb[i].zName; sqlite3Error(db, rc, "database schema is locked: %s", zDb); - (void)sqlite3SafetyOff(db); testcase( db->flags & SQLITE_ReadUncommitted ); goto end_prepare; } @@ -596,7 +586,6 @@ static int sqlite3Prepare( testcase( nBytes==mxLen+1 ); if( nBytes>mxLen ){ sqlite3Error(db, SQLITE_TOOBIG, "statement too long"); - (void)sqlite3SafetyOff(db); rc = sqlite3ApiExit(db, SQLITE_TOOBIG); goto end_prepare; } @@ -653,10 +642,6 @@ static int sqlite3Prepare( } #endif - if( sqlite3SafetyOff(db) ){ - rc = SQLITE_MISUSE; - } - assert( db->init.busy==0 || saveSqlFlag==0 ); if( db->init.busy==0 ){ Vdbe *pVdbe = pParse->pVdbe; @@ -704,7 +689,7 @@ static int sqlite3LockAndPrepare( assert( ppStmt!=0 ); *ppStmt = 0; if( !sqlite3SafetyCheckOk(db) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); @@ -743,7 +728,7 @@ int sqlite3Reprepare(Vdbe *p){ db->mallocFailed = 1; } assert( pNew==0 ); - return (rc==SQLITE_LOCKED) ? SQLITE_LOCKED : SQLITE_SCHEMA; + return rc; }else{ assert( pNew!=0 ); } @@ -812,7 +797,7 @@ static int sqlite3Prepare16( assert( ppStmt ); *ppStmt = 0; if( !sqlite3SafetyCheckOk(db) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes); diff --git a/src/printf.c b/src/printf.c index fdd4793c36..e67d48ee17 100644 --- a/src/printf.c +++ b/src/printf.c @@ -939,6 +939,28 @@ char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ return z; } +/* +** Format and write a message to the log if logging is enabled. +*/ +void sqlite3_log(int iErrCode, const char *zFormat, ...){ + void (*xLog)(void*, int, const char*); /* The global logger function */ + void *pLogArg; /* First argument to the logger */ + va_list ap; /* Vararg list */ + char *zMsg; /* Complete log message */ + + xLog = sqlite3GlobalConfig.xLog; + if( xLog ){ + va_start(ap, zFormat); + sqlite3BeginBenignMalloc(); + zMsg = sqlite3_vmprintf(zFormat, ap); + sqlite3EndBenignMalloc(); + va_end(ap); + pLogArg = sqlite3GlobalConfig.pLogArg; + xLog(pLogArg, iErrCode, zMsg ? zMsg : zFormat); + sqlite3_free(zMsg); + } +} + #if defined(SQLITE_DEBUG) /* ** A version of printf() that understands %lld. Used for debugging. diff --git a/src/resolve.c b/src/resolve.c index 3b48baa6f3..3a44aef623 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -664,6 +664,9 @@ static int resolveOrderByTermToExprList( int i; /* Loop counter */ ExprList *pEList; /* The columns of the result set */ NameContext nc; /* Name context for resolving pE */ + sqlite3 *db; /* Database connection */ + int rc; /* Return code from subprocedures */ + u8 savedSuppErr; /* Saved value of db->suppressErr */ assert( sqlite3ExprIsInteger(pE, &i)==0 ); pEList = pSelect->pEList; @@ -676,10 +679,12 @@ static int resolveOrderByTermToExprList( nc.pEList = pEList; nc.allowAgg = 1; nc.nErr = 0; - if( sqlite3ResolveExprNames(&nc, pE) ){ - sqlite3ErrorClear(pParse); - return 0; - } + db = pParse->db; + savedSuppErr = db->suppressErr; + db->suppressErr = 1; + rc = sqlite3ResolveExprNames(&nc, pE); + db->suppressErr = savedSuppErr; + if( rc ) return 0; /* Try to match the ORDER BY expression against an expression ** in the result set. Return an 1-based index of the matching diff --git a/src/select.c b/src/select.c index 021c156a50..be4b95c055 100644 --- a/src/select.c +++ b/src/select.c @@ -3327,18 +3327,19 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ struct SrcList_item *pFrom; assert( p->selFlags & SF_Resolved ); - assert( (p->selFlags & SF_HasTypeInfo)==0 ); - p->selFlags |= SF_HasTypeInfo; - pParse = pWalker->pParse; - pTabList = p->pSrc; - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; - if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){ - /* A sub-query in the FROM clause of a SELECT */ - Select *pSel = pFrom->pSelect; - assert( pSel ); - while( pSel->pPrior ) pSel = pSel->pPrior; - selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSel); + if( (p->selFlags & SF_HasTypeInfo)==0 ){ + p->selFlags |= SF_HasTypeInfo; + pParse = pWalker->pParse; + pTabList = p->pSrc; + for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ + Table *pTab = pFrom->pTab; + if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){ + /* A sub-query in the FROM clause of a SELECT */ + Select *pSel = pFrom->pSelect; + assert( pSel ); + while( pSel->pPrior ) pSel = pSel->pPrior; + selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSel); + } } } return WRC_Continue; diff --git a/src/shell.c b/src/shell.c index af56792ecd..a1c0b5e292 100644 --- a/src/shell.c +++ b/src/shell.c @@ -195,868 +195,6 @@ static void endTimer(void){ */ #define UNUSED_PARAMETER(x) (void)(x) - -/************************************************************************** -*************************************************************************** -** Begin genfkey logic. -*/ -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined SQLITE_OMIT_SUBQUERY - -#define GENFKEY_ERROR 1 -#define GENFKEY_DROPTRIGGER 2 -#define GENFKEY_CREATETRIGGER 3 -static int genfkey_create_triggers(sqlite3 *, const char *, void *, - int (*)(void *, int, const char *) -); - -struct GenfkeyCb { - void *pCtx; - int eType; - int (*xData)(void *, int, const char *); -}; -typedef struct GenfkeyCb GenfkeyCb; - -/* The code in this file defines a sqlite3 virtual-table module that -** provides a read-only view of the current database schema. There is one -** row in the schema table for each column in the database schema. -*/ -#define SCHEMA \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "tablename," /* Name of table */ \ - "cid," /* Column number (from left-to-right, 0 upward) */ \ - "name," /* Column name */ \ - "type," /* Specified type (i.e. VARCHAR(32)) */ \ - "not_null," /* Boolean. True if NOT NULL was specified */ \ - "dflt_value," /* Default value for this column */ \ - "pk" /* True if this column is part of the primary key */ \ -")" - -#define SCHEMA2 \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "from_tbl," /* Name of table */ \ - "fkid," \ - "seq," \ - "to_tbl," \ - "from_col," \ - "to_col," \ - "on_update," \ - "on_delete," \ - "match" \ -")" - -#define SCHEMA3 \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "tablename," /* Name of table */ \ - "seq," \ - "name," \ - "isunique" \ -")" - -#define SCHEMA4 \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "indexname," /* Name of table */ \ - "seqno," \ - "cid," \ - "name" \ -")" - -#define SCHEMA5 \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "triggername," /* Name of trigger */ \ - "dummy" /* Unused */ \ -")" - -typedef struct SchemaTable SchemaTable; -static struct SchemaTable { - const char *zName; - const char *zObject; - const char *zPragma; - const char *zSchema; -} aSchemaTable[] = { - { "table_info", "table", "PRAGMA %Q.table_info(%Q)", SCHEMA }, - { "foreign_key_list", "table", "PRAGMA %Q.foreign_key_list(%Q)", SCHEMA2 }, - { "index_list", "table", "PRAGMA %Q.index_list(%Q)", SCHEMA3 }, - { "index_info", "index", "PRAGMA %Q.index_info(%Q)", SCHEMA4 }, - { "trigger_list", "trigger", "SELECT 1", SCHEMA5 }, - { 0, 0, 0, 0 } -}; - -typedef struct schema_vtab schema_vtab; -typedef struct schema_cursor schema_cursor; - -/* A schema table object */ -struct schema_vtab { - sqlite3_vtab base; - sqlite3 *db; - SchemaTable *pType; -}; - -/* A schema table cursor object */ -struct schema_cursor { - sqlite3_vtab_cursor base; - sqlite3_stmt *pDbList; - sqlite3_stmt *pTableList; - sqlite3_stmt *pColumnList; - int rowid; -}; - -/* -** Table destructor for the schema module. -*/ -static int schemaDestroy(sqlite3_vtab *pVtab){ - sqlite3_free(pVtab); - return 0; -} - -/* -** Table constructor for the schema module. -*/ -static int schemaCreate( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - int rc = SQLITE_NOMEM; - schema_vtab *pVtab; - SchemaTable *pType = &aSchemaTable[0]; - - UNUSED_PARAMETER(pzErr); - if( argc>3 ){ - int i; - pType = 0; - for(i=0; aSchemaTable[i].zName; i++){ - if( 0==strcmp(argv[3], aSchemaTable[i].zName) ){ - pType = &aSchemaTable[i]; - } - } - if( !pType ){ - return SQLITE_ERROR; - } - } - - pVtab = sqlite3_malloc(sizeof(schema_vtab)); - if( pVtab ){ - memset(pVtab, 0, sizeof(schema_vtab)); - pVtab->db = (sqlite3 *)pAux; - pVtab->pType = pType; - rc = sqlite3_declare_vtab(db, pType->zSchema); - } - *ppVtab = (sqlite3_vtab *)pVtab; - return rc; -} - -/* -** Open a new cursor on the schema table. -*/ -static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - int rc = SQLITE_NOMEM; - schema_cursor *pCur; - UNUSED_PARAMETER(pVTab); - pCur = sqlite3_malloc(sizeof(schema_cursor)); - if( pCur ){ - memset(pCur, 0, sizeof(schema_cursor)); - *ppCursor = (sqlite3_vtab_cursor *)pCur; - rc = SQLITE_OK; - } - return rc; -} - -/* -** Close a schema table cursor. -*/ -static int schemaClose(sqlite3_vtab_cursor *cur){ - schema_cursor *pCur = (schema_cursor *)cur; - sqlite3_finalize(pCur->pDbList); - sqlite3_finalize(pCur->pTableList); - sqlite3_finalize(pCur->pColumnList); - sqlite3_free(pCur); - return SQLITE_OK; -} - -static void columnToResult(sqlite3_context *ctx, sqlite3_stmt *pStmt, int iCol){ - switch( sqlite3_column_type(pStmt, iCol) ){ - case SQLITE_NULL: - sqlite3_result_null(ctx); - break; - case SQLITE_INTEGER: - sqlite3_result_int64(ctx, sqlite3_column_int64(pStmt, iCol)); - break; - case SQLITE_FLOAT: - sqlite3_result_double(ctx, sqlite3_column_double(pStmt, iCol)); - break; - case SQLITE_TEXT: { - const char *z = (const char *)sqlite3_column_text(pStmt, iCol); - sqlite3_result_text(ctx, z, -1, SQLITE_TRANSIENT); - break; - } - } -} - -/* -** Retrieve a column of data. -*/ -static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ - schema_cursor *pCur = (schema_cursor *)cur; - switch( i ){ - case 0: - columnToResult(ctx, pCur->pDbList, 1); - break; - case 1: - columnToResult(ctx, pCur->pTableList, 0); - break; - default: - columnToResult(ctx, pCur->pColumnList, i-2); - break; - } - return SQLITE_OK; -} - -/* -** Retrieve the current rowid. -*/ -static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - schema_cursor *pCur = (schema_cursor *)cur; - *pRowid = pCur->rowid; - return SQLITE_OK; -} - -static int finalize(sqlite3_stmt **ppStmt){ - int rc = sqlite3_finalize(*ppStmt); - *ppStmt = 0; - return rc; -} - -static int schemaEof(sqlite3_vtab_cursor *cur){ - schema_cursor *pCur = (schema_cursor *)cur; - return (pCur->pDbList ? 0 : 1); -} - -/* -** Advance the cursor to the next row. -*/ -static int schemaNext(sqlite3_vtab_cursor *cur){ - int rc = SQLITE_OK; - schema_cursor *pCur = (schema_cursor *)cur; - schema_vtab *pVtab = (schema_vtab *)(cur->pVtab); - char *zSql = 0; - - while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){ - if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit; - - while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){ - if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit; - - assert(pCur->pDbList); - while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){ - rc = finalize(&pCur->pDbList); - goto next_exit; - } - - /* Set zSql to the SQL to pull the list of tables from the - ** sqlite_master (or sqlite_temp_master) table of the database - ** identfied by the row pointed to by the SQL statement pCur->pDbList - ** (iterating through a "PRAGMA database_list;" statement). - */ - if( sqlite3_column_int(pCur->pDbList, 0)==1 ){ - zSql = sqlite3_mprintf( - "SELECT name FROM sqlite_temp_master WHERE type=%Q", - pVtab->pType->zObject - ); - }else{ - sqlite3_stmt *pDbList = pCur->pDbList; - zSql = sqlite3_mprintf( - "SELECT name FROM %Q.sqlite_master WHERE type=%Q", - sqlite3_column_text(pDbList, 1), pVtab->pType->zObject - ); - } - if( !zSql ){ - rc = SQLITE_NOMEM; - goto next_exit; - } - - rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0); - sqlite3_free(zSql); - if( rc!=SQLITE_OK ) goto next_exit; - } - - /* Set zSql to the SQL to the table_info pragma for the table currently - ** identified by the rows pointed to by statements pCur->pDbList and - ** pCur->pTableList. - */ - zSql = sqlite3_mprintf(pVtab->pType->zPragma, - sqlite3_column_text(pCur->pDbList, 1), - sqlite3_column_text(pCur->pTableList, 0) - ); - - if( !zSql ){ - rc = SQLITE_NOMEM; - goto next_exit; - } - rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0); - sqlite3_free(zSql); - if( rc!=SQLITE_OK ) goto next_exit; - } - pCur->rowid++; - -next_exit: - /* TODO: Handle rc */ - return rc; -} - -/* -** Reset a schema table cursor. -*/ -static int schemaFilter( - sqlite3_vtab_cursor *pVtabCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - int rc; - schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab); - schema_cursor *pCur = (schema_cursor *)pVtabCursor; - UNUSED_PARAMETER(idxNum); - UNUSED_PARAMETER(idxStr); - UNUSED_PARAMETER(argc); - UNUSED_PARAMETER(argv); - pCur->rowid = 0; - finalize(&pCur->pTableList); - finalize(&pCur->pColumnList); - finalize(&pCur->pDbList); - rc = sqlite3_prepare(pVtab->db,"SELECT 0, 'main'", -1, &pCur->pDbList, 0); - return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc); -} - -/* -** Analyse the WHERE condition. -*/ -static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - UNUSED_PARAMETER(tab); - UNUSED_PARAMETER(pIdxInfo); - return SQLITE_OK; -} - -/* -** A virtual table module that merely echos method calls into TCL -** variables. -*/ -static sqlite3_module schemaModule = { - 0, /* iVersion */ - schemaCreate, - schemaCreate, - schemaBestIndex, - schemaDestroy, - schemaDestroy, - schemaOpen, /* xOpen - open a cursor */ - schemaClose, /* xClose - close a cursor */ - schemaFilter, /* xFilter - configure scan constraints */ - schemaNext, /* xNext - advance a cursor */ - schemaEof, /* xEof */ - schemaColumn, /* xColumn - read data */ - schemaRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ -}; - -/* -** Extension load function. -*/ -static int installSchemaModule(sqlite3 *db, sqlite3 *sdb){ - sqlite3_create_module(db, "schema", &schemaModule, (void *)sdb); - return 0; -} - -/* -** sj(zValue, zJoin) -** -** The following block contains the implementation of an aggregate -** function that returns a string. Each time the function is stepped, -** it appends data to an internal buffer. When the aggregate is finalized, -** the contents of the buffer are returned. -** -** The first time the aggregate is stepped the buffer is set to a copy -** of the first argument. The second time and subsequent times it is -** stepped a copy of the second argument is appended to the buffer, then -** a copy of the first. -** -** Example: -** -** INSERT INTO t1(a) VALUES('1'); -** INSERT INTO t1(a) VALUES('2'); -** INSERT INTO t1(a) VALUES('3'); -** SELECT sj(a, ', ') FROM t1; -** -** => "1, 2, 3" -** -*/ -struct StrBuffer { - char *zBuf; -}; -typedef struct StrBuffer StrBuffer; -static void joinFinalize(sqlite3_context *context){ - StrBuffer *p; - p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer)); - sqlite3_result_text(context, p->zBuf, -1, SQLITE_TRANSIENT); - sqlite3_free(p->zBuf); -} -static void joinStep( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - StrBuffer *p; - UNUSED_PARAMETER(argc); - p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer)); - if( p->zBuf==0 ){ - p->zBuf = sqlite3_mprintf("%s", sqlite3_value_text(argv[0])); - }else{ - char *zTmp = p->zBuf; - p->zBuf = sqlite3_mprintf("%s%s%s", - zTmp, sqlite3_value_text(argv[1]), sqlite3_value_text(argv[0]) - ); - sqlite3_free(zTmp); - } -} - -/* -** dq(zString) -** -** This scalar function accepts a single argument and interprets it as -** a text value. The return value is the argument enclosed in double -** quotes. If any double quote characters are present in the argument, -** these are escaped. -** -** dq('the raven "Nevermore."') == '"the raven ""Nevermore."""' -*/ -static void doublequote( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int ii; - char *zOut; - char *zCsr; - const char *zIn = (const char *)sqlite3_value_text(argv[0]); - int nIn = sqlite3_value_bytes(argv[0]); - - UNUSED_PARAMETER(argc); - zOut = sqlite3_malloc(nIn*2+3); - zCsr = zOut; - *zCsr++ = '"'; - for(ii=0; iinMalloc ){ - char *zNew; - nMalloc = 16 + (nOut+nCopy)*2; - zNew = (char*)sqlite3_realloc(zOut, nMalloc); - if( zNew==0 ){ - sqlite3_result_error_nomem(context); - return; - }else{ - zOut = zNew; - } - } - assert( nMalloc>=(nOut+nCopy) ); - memcpy(&zOut[nOut], zCopy, nCopy); - i += nReplace; - nOut += nCopy; - } - - sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT); - sqlite3_free(zOut); -} - -/* -** A callback for sqlite3_exec() invokes the callback specified by the -** GenfkeyCb structure pointed to by the void* passed as the first argument. -*/ -static int invokeCallback(void *p, int nArg, char **azArg, char **azCol){ - GenfkeyCb *pCb = (GenfkeyCb *)p; - UNUSED_PARAMETER(nArg); - UNUSED_PARAMETER(azCol); - return pCb->xData(pCb->pCtx, pCb->eType, azArg[0]); -} - -static int detectSchemaProblem( - sqlite3 *db, /* Database connection */ - const char *zMessage, /* English language error message */ - const char *zSql, /* SQL statement to run */ - GenfkeyCb *pCb -){ - sqlite3_stmt *pStmt; - int rc; - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - while( SQLITE_ROW==sqlite3_step(pStmt) ){ - char *zDel; - int iFk = sqlite3_column_int(pStmt, 0); - const char *zTab = (const char *)sqlite3_column_text(pStmt, 1); - zDel = sqlite3_mprintf("Error in table %s: %s", zTab, zMessage); - rc = pCb->xData(pCb->pCtx, pCb->eType, zDel); - sqlite3_free(zDel); - if( rc!=SQLITE_OK ) return rc; - zDel = sqlite3_mprintf( - "DELETE FROM temp.fkey WHERE from_tbl = %Q AND fkid = %d" - , zTab, iFk - ); - sqlite3_exec(db, zDel, 0, 0, 0); - sqlite3_free(zDel); - } - sqlite3_finalize(pStmt); - return SQLITE_OK; -} - -/* -** Create and populate temporary table "fkey". -*/ -static int populateTempTable(sqlite3 *db, GenfkeyCb *pCallback){ - int rc; - - rc = sqlite3_exec(db, - "CREATE VIRTUAL TABLE temp.v_fkey USING schema(foreign_key_list);" - "CREATE VIRTUAL TABLE temp.v_col USING schema(table_info);" - "CREATE VIRTUAL TABLE temp.v_idxlist USING schema(index_list);" - "CREATE VIRTUAL TABLE temp.v_idxinfo USING schema(index_info);" - "CREATE VIRTUAL TABLE temp.v_triggers USING schema(trigger_list);" - "CREATE TABLE temp.fkey AS " - "SELECT from_tbl, to_tbl, fkid, from_col, to_col, on_update, on_delete " - "FROM temp.v_fkey WHERE database = 'main';" - , 0, 0, 0 - ); - if( rc!=SQLITE_OK ) return rc; - - rc = detectSchemaProblem(db, "foreign key columns do not exist", - "SELECT fkid, from_tbl " - "FROM temp.fkey " - "WHERE to_col IS NOT NULL AND NOT EXISTS (SELECT 1 " - "FROM temp.v_col WHERE tablename=to_tbl AND name==to_col" - ")", pCallback - ); - if( rc!=SQLITE_OK ) return rc; - - /* At this point the temp.fkey table is mostly populated. If any foreign - ** keys were specified so that they implicitly refer to they primary - ** key of the parent table, the "to_col" values of the temp.fkey rows - ** are still set to NULL. - ** - ** This is easily fixed for single column primary keys, but not for - ** composites. With a composite primary key, there is no way to reliably - ** query sqlite for the order in which the columns that make up the - ** composite key were declared i.e. there is no way to tell if the - ** schema actually contains "PRIMARY KEY(a, b)" or "PRIMARY KEY(b, a)". - ** Therefore, this case is not handled. The following function call - ** detects instances of this case. - */ - rc = detectSchemaProblem(db, "implicit mapping to composite primary key", - "SELECT fkid, from_tbl " - "FROM temp.fkey " - "WHERE to_col IS NULL " - "GROUP BY fkid, from_tbl HAVING count(*) > 1", pCallback - ); - if( rc!=SQLITE_OK ) return rc; - - /* Detect attempts to implicitly map to the primary key of a table - ** that has no primary key column. - */ - rc = detectSchemaProblem(db, "implicit mapping to non-existant primary key", - "SELECT fkid, from_tbl " - "FROM temp.fkey " - "WHERE to_col IS NULL AND NOT EXISTS " - "(SELECT 1 FROM temp.v_col WHERE pk AND tablename = temp.fkey.to_tbl)" - , pCallback - ); - if( rc!=SQLITE_OK ) return rc; - - /* Fix all the implicit primary key mappings in the temp.fkey table. */ - rc = sqlite3_exec(db, - "UPDATE temp.fkey SET to_col = " - "(SELECT name FROM temp.v_col WHERE pk AND tablename=temp.fkey.to_tbl)" - " WHERE to_col IS NULL;" - , 0, 0, 0 - ); - if( rc!=SQLITE_OK ) return rc; - - /* Now check that all all parent keys are either primary keys or - ** subject to a unique constraint. - */ - rc = sqlite3_exec(db, - "CREATE TABLE temp.idx2 AS SELECT " - "il.tablename AS tablename," - "ii.indexname AS indexname," - "ii.name AS col " - "FROM temp.v_idxlist AS il, temp.v_idxinfo AS ii " - "WHERE il.isunique AND il.database='main' AND ii.indexname = il.name;" - "INSERT INTO temp.idx2 " - "SELECT tablename, 'pk', name FROM temp.v_col WHERE pk;" - - "CREATE TABLE temp.idx AS SELECT " - "tablename, indexname, sj(dq(col),',') AS cols " - "FROM (SELECT * FROM temp.idx2 ORDER BY col) " - "GROUP BY tablename, indexname;" - - "CREATE TABLE temp.fkey2 AS SELECT " - "fkid, from_tbl, to_tbl, sj(dq(to_col),',') AS cols " - "FROM (SELECT * FROM temp.fkey ORDER BY to_col) " - "GROUP BY fkid, from_tbl;" - - "CREATE TABLE temp.triggers AS SELECT " - "triggername FROM temp.v_triggers WHERE database='main' AND " - "triggername LIKE 'genfkey%';" - , 0, 0, 0 - ); - if( rc!=SQLITE_OK ) return rc; - rc = detectSchemaProblem(db, "foreign key is not unique", - "SELECT fkid, from_tbl " - "FROM temp.fkey2 " - "WHERE NOT EXISTS (SELECT 1 " - "FROM temp.idx WHERE tablename=to_tbl AND fkey2.cols==idx.cols" - ")", pCallback - ); - if( rc!=SQLITE_OK ) return rc; - - return rc; -} - -#define GENFKEY_ERROR 1 -#define GENFKEY_DROPTRIGGER 2 -#define GENFKEY_CREATETRIGGER 3 -static int genfkey_create_triggers( - sqlite3 *sdb, /* Connection to read schema from */ - const char *zDb, /* Name of db to read ("main", "temp") */ - void *pCtx, /* Context pointer to pass to xData */ - int (*xData)(void *, int, const char *) -){ - const char *zSql = - "SELECT multireplace('" - - "-- Triggers for foreign key mapping:\n" - "--\n" - "-- /from_readable/ REFERENCES /to_readable/\n" - "-- on delete /on_delete/\n" - "-- on update /on_update/\n" - "--\n" - - /* The "BEFORE INSERT ON " trigger. This trigger's job is to - ** throw an exception if the user tries to insert a row into the - ** referencing table for which there is no corresponding row in - ** the referenced table. - */ - "CREATE TRIGGER /name/_insert_referencing BEFORE INSERT ON /tbl/ WHEN \n" - " /key_notnull/ AND NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n" - "BEGIN\n" - " SELECT RAISE(ABORT, ''constraint failed'');\n" - "END;\n" - - /* The "BEFORE UPDATE ON " trigger. This trigger's job - ** is to throw an exception if the user tries to update a row in the - ** referencing table causing it to correspond to no row in the - ** referenced table. - */ - "CREATE TRIGGER /name/_update_referencing BEFORE\n" - " UPDATE OF /rkey_list/ ON /tbl/ WHEN \n" - " /key_notnull/ AND \n" - " NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n" - "BEGIN\n" - " SELECT RAISE(ABORT, ''constraint failed'');\n" - "END;\n" - - - /* The "BEFORE DELETE ON " trigger. This trigger's job - ** is to detect when a row is deleted from the referenced table to - ** which rows in the referencing table correspond. The action taken - ** depends on the value of the 'ON DELETE' clause. - */ - "CREATE TRIGGER /name/_delete_referenced BEFORE DELETE ON /ref/ WHEN\n" - " EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n" - "BEGIN\n" - " /delete_action/\n" - "END;\n" - - /* The "AFTER UPDATE ON " trigger. This trigger's job - ** is to detect when the key columns of a row in the referenced table - ** to which one or more rows in the referencing table correspond are - ** updated. The action taken depends on the value of the 'ON UPDATE' - ** clause. - */ - "CREATE TRIGGER /name/_update_referenced AFTER\n" - " UPDATE OF /fkey_list/ ON /ref/ WHEN \n" - " EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n" - "BEGIN\n" - " /update_action/\n" - "END;\n" - "'" - - /* These are used in the SQL comment written above each set of triggers */ - ", '/from_readable/', from_tbl || '(' || sj(from_col, ', ') || ')'" - ", '/to_readable/', to_tbl || '(' || sj(to_col, ', ') || ')'" - ", '/on_delete/', on_delete" - ", '/on_update/', on_update" - - ", '/name/', 'genfkey' || min(rowid)" - ", '/tbl/', dq(from_tbl)" - ", '/ref/', dq(to_tbl)" - ", '/key_notnull/', sj('new.' || dq(from_col) || ' IS NOT NULL', ' AND ')" - - ", '/fkey_list/', sj(dq(to_col), ', ')" - ", '/rkey_list/', sj(dq(from_col), ', ')" - - ", '/cond1/', sj(multireplace('new./from/ == /to/'" - ", '/from/', dq(from_col)" - ", '/to/', dq(to_col)" - "), ' AND ')" - ", '/cond2/', sj(multireplace('old./to/ == /from/'" - ", '/from/', dq(from_col)" - ", '/to/', dq(to_col)" - "), ' AND ')" - - ", '/update_action/', CASE on_update " - "WHEN 'SET NULL' THEN " - "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(dq(from_col)||' = NULL',', ')" - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" - ")" - "WHEN 'CASCADE' THEN " - "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(dq(from_col)||' = new.'||dq(to_col),', ')" - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" - ")" - "ELSE " - " 'SELECT RAISE(ABORT, ''constraint failed'');'" - "END " - - ", '/delete_action/', CASE on_delete " - "WHEN 'SET NULL' THEN " - "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(dq(from_col)||' = NULL',', ')" - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" - ")" - "WHEN 'CASCADE' THEN " - "multireplace('DELETE FROM /tbl/ WHERE /where/;' " - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" - ")" - "ELSE " - " 'SELECT RAISE(ABORT, ''constraint failed'');'" - "END " - - ") FROM temp.fkey " - "GROUP BY from_tbl, fkid" - ; - - int rc; - const int enc = SQLITE_UTF8; - sqlite3 *db = 0; - - GenfkeyCb cb; - cb.xData = xData; - cb.pCtx = pCtx; - - UNUSED_PARAMETER(zDb); - - /* Open the working database handle. */ - rc = sqlite3_open(":memory:", &db); - if( rc!=SQLITE_OK ) goto genfkey_exit; - - /* Create the special scalar and aggregate functions used by this program. */ - sqlite3_create_function(db, "dq", 1, enc, 0, doublequote, 0, 0); - sqlite3_create_function(db, "multireplace", -1, enc, db, multireplace, 0, 0); - sqlite3_create_function(db, "sj", 2, enc, 0, 0, joinStep, joinFinalize); - - /* Install the "schema" virtual table module */ - installSchemaModule(db, sdb); - - /* Create and populate a temp table with the information required to - ** build the foreign key triggers. See function populateTempTable() - ** for details. - */ - cb.eType = GENFKEY_ERROR; - rc = populateTempTable(db, &cb); - if( rc!=SQLITE_OK ) goto genfkey_exit; - - /* Unless the --no-drop option was specified, generate DROP TRIGGER - ** statements to drop any triggers in the database generated by a - ** previous run of this program. - */ - cb.eType = GENFKEY_DROPTRIGGER; - rc = sqlite3_exec(db, - "SELECT 'DROP TRIGGER main.' || dq(triggername) || ';' FROM triggers" - ,invokeCallback, (void *)&cb, 0 - ); - if( rc!=SQLITE_OK ) goto genfkey_exit; - - /* Run the main query to create the trigger definitions. */ - cb.eType = GENFKEY_CREATETRIGGER; - rc = sqlite3_exec(db, zSql, invokeCallback, (void *)&cb, 0); - if( rc!=SQLITE_OK ) goto genfkey_exit; - -genfkey_exit: - sqlite3_close(db); - return rc; -} - - -#endif -/* End genfkey logic. */ -/*************************************************************************/ -/*************************************************************************/ - /* ** If the following flag is set, then command execution stops ** at an error if we are not interactive. @@ -1277,6 +415,7 @@ struct callback_data { char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ sqlite3_stmt *pStmt; /* Current statement if any. */ + FILE *pLog; /* Write log output here */ }; /* @@ -1319,6 +458,16 @@ static int strlen30(const char *z){ return 0x3fffffff & (int)(z2 - z); } +/* +** A callback for the sqlite3_log() interface. +*/ +static void shellLog(void *pArg, int iErrCode, const char *zMsg){ + struct callback_data *p = (struct callback_data*)pArg; + if( p->pLog==0 ) return; + fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); + fflush(p->pLog); +} + /* ** Output the given string as a hex-encoded blob (eg. X'1234' ) */ @@ -1853,9 +1002,9 @@ static int shell_exec( /* echo the sql statement if echo on */ if( pArg->echoOn ){ - char *zStmtSql = sqlite3_sql(pStmt); + const char *zStmtSql = sqlite3_sql(pStmt); fprintf(pArg->out,"%s\n", zStmtSql ? zStmtSql : zSql); - } + } /* perform the first step. this will tell us if we ** have a result set or not and how wide it is. @@ -2059,62 +1208,6 @@ static int run_schema_dump_query( return rc; } -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY) -struct GenfkeyCmd { - sqlite3 *db; /* Database handle */ - struct callback_data *pCb; /* Callback data */ - int isIgnoreErrors; /* True for --ignore-errors */ - int isExec; /* True for --exec */ - int isNoDrop; /* True for --no-drop */ - int nErr; /* Number of errors seen so far */ -}; -typedef struct GenfkeyCmd GenfkeyCmd; - -static int genfkeyParseArgs(GenfkeyCmd *p, char **azArg, int nArg){ - int ii; - memset(p, 0, sizeof(GenfkeyCmd)); - - for(ii=0; ii2 && n<10 && 0==strncmp(azArg[ii], "--no-drop", n) ){ - p->isNoDrop = 1; - }else if( n>2 && n<16 && 0==strncmp(azArg[ii], "--ignore-errors", n) ){ - p->isIgnoreErrors = 1; - }else if( n>2 && n<7 && 0==strncmp(azArg[ii], "--exec", n) ){ - p->isExec = 1; - }else{ - fprintf(stderr, "unknown option: %s\n", azArg[ii]); - return -1; - } - } - - return SQLITE_OK; -} - -static int genfkeyCmdCb(void *pCtx, int eType, const char *z){ - GenfkeyCmd *p = (GenfkeyCmd *)pCtx; - if( eType==GENFKEY_ERROR && !p->isIgnoreErrors ){ - p->nErr++; - fprintf(stderr, "%s\n", z); - } - - if( p->nErr==0 && ( - (eType==GENFKEY_CREATETRIGGER) - || (eType==GENFKEY_DROPTRIGGER && !p->isNoDrop) - )){ - if( p->isExec ){ - sqlite3_exec(p->db, z, 0, 0, 0); - }else{ - char *zCol = "sql"; - callback((void *)p->pCb, 1, (char **)&z, (char **)&zCol); - } - } - - return SQLITE_OK; -} -#endif - /* ** Text of a help message */ @@ -2129,14 +1222,6 @@ static char zHelp[] = ".exit Exit this program\n" ".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n" " With no args, it turns EXPLAIN on.\n" -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY) - ".genfkey ?OPTIONS? Options are:\n" - " --no-drop: Do not drop old fkey triggers.\n" - " --ignore-errors: Ignore tables with fkey errors\n" - " --exec: Execute generated SQL immediately\n" - " See file tool/genfkey.README in the source \n" - " distribution for further information.\n" -#endif ".header(s) ON|OFF Turn display of headers on or off\n" ".help Show this message\n" ".import FILE TABLE Import data from FILE into TABLE\n" @@ -2149,6 +1234,7 @@ static char zHelp[] = #ifndef SQLITE_OMIT_LOAD_EXTENSION ".load FILE ?ENTRY? Load an extension library\n" #endif + ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" @@ -2456,17 +1542,6 @@ static int do_meta_command(char *zLine, struct callback_data *p){ } }else -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY) - if( c=='g' && strncmp(azArg[0], "genfkey", n)==0 ){ - GenfkeyCmd cmd; - if( 0==genfkeyParseArgs(&cmd, &azArg[1], nArg-1) ){ - cmd.db = p->db; - cmd.pCb = p; - genfkey_create_triggers(p->db, "main", (void *)&cmd, genfkeyCmdCb); - } - }else -#endif - if( c=='h' && (strncmp(azArg[0], "header", n)==0 || strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){ p->showHeader = booleanValue(azArg[1]); @@ -2675,6 +1750,26 @@ static int do_meta_command(char *zLine, struct callback_data *p){ }else #endif + if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=1 ){ + const char *zFile = azArg[1]; + if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){ + fclose(p->pLog); + p->pLog = 0; + } + if( strcmp(zFile,"stdout")==0 ){ + p->pLog = stdout; + }else if( strcmp(zFile, "stderr")==0 ){ + p->pLog = stderr; + }else if( strcmp(zFile, "off")==0 ){ + p->pLog = 0; + }else{ + p->pLog = fopen(zFile, "w"); + if( p->pLog==0 ){ + fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); + } + } + }else + if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){ int n2 = strlen30(azArg[1]); if( (n2==4 && strncmp(azArg[1],"line",n2)==0) @@ -3320,8 +2415,10 @@ static void main_init(struct callback_data *data) { data->mode = MODE_List; memcpy(data->separator,"|", 2); data->showHeader = 0; + sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); + sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); } int main(int argc, char **argv){ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index e1b8dc61fb..b23cf97782 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -108,7 +108,7 @@ extern "C" { /* ** CAPI3REF: Run-Time Library Version Numbers -** KEYWORDS: sqlite3_version +** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros @@ -130,9 +130,9 @@ extern "C" { ** function is provided for use in DLLs since DLL users usually do not have ** direct access to string constants within the DLL. ^The ** sqlite3_libversion_number() function returns an integer equal to -** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function a pointer -** to a string constant whose value is the same as the [SQLITE_SOURCE_ID] -** C preprocessor macro. +** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns +** a pointer to a string constant whose value is the same as the +** [SQLITE_SOURCE_ID] C preprocessor macro. ** ** See also: [sqlite_version()] and [sqlite_source_id()]. */ @@ -141,6 +141,33 @@ const char *sqlite3_libversion(void); const char *sqlite3_sourceid(void); int sqlite3_libversion_number(void); +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS +/* +** CAPI3REF: Run-Time Library Compilation Options Diagnostics +** +** ^The sqlite3_compileoption_used() function returns 0 or 1 +** indicating whether the specified option was defined at +** compile time. ^The SQLITE_ prefix may be omitted from the +** option name passed to sqlite3_compileoption_used(). +** +** ^The sqlite3_compileoption_get() function allows interating +** over the list of options that were defined at compile time by +** returning the N-th compile time option string. ^If N is out of range, +** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ +** prefix is omitted from any strings returned by +** sqlite3_compileoption_get(). +** +** ^Support for the diagnostic functions sqlite3_compileoption_used() +** and sqlite3_compileoption_get() may be omitted by specifing the +** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. +** +** See also: SQL functions [sqlite_compileoption_used()] and +** [sqlite_compileoption_get()] and the [compile_options pragma]. +*/ +int sqlite3_compileoption_used(const char *zOptName); +const char *sqlite3_compileoption_get(int N); +#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ + /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** @@ -914,7 +941,6 @@ int sqlite3_os_end(void); /* ** CAPI3REF: Configuring The SQLite Library -** EXPERIMENTAL ** ** The sqlite3_config() interface is used to make global configuration ** changes to SQLite in order to tune SQLite to the specific needs of @@ -1255,6 +1281,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ #define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ /* ** CAPI3REF: Configuration Options @@ -3657,7 +3684,7 @@ int sqlite3_collation_needed16( void(*)(void*,sqlite3*,int eTextRep,const void*) ); -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC /* ** Specify the key for an encrypted database. This routine should be ** called right after sqlite3_open(). @@ -3687,19 +3714,19 @@ int sqlite3_rekey( ** Specify the activation key for a SEE database. Unless ** activated, none of the SEE routines will work. */ -void sqlite3_activate_see( - const char *zPassPhrase /* Activation phrase */ -); -#endif - -#ifdef SQLITE_ENABLE_CEROD +void sqlite3_activate_see( + const char *zPassPhrase /* Activation phrase */ +); +#endif + +#ifdef SQLITE_ENABLE_CEROD /* ** Specify the activation key for a CEROD database. Unless ** activated, none of the CEROD routines will work. */ -void sqlite3_activate_cerod( - const char *zPassPhrase /* Activation phrase */ -); +void sqlite3_activate_cerod( + const char *zPassPhrase /* Activation phrase */ +); #endif /* @@ -5663,6 +5690,24 @@ int sqlite3_unlock_notify( */ int sqlite3_strnicmp(const char *, const char *, int); +/* +** CAPI3REF: Error Logging Interface +** EXPERIMENTAL +** +** ^The [sqlite3_log()] interface writes a message into the error log +** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. +** ^If logging is enabled, the zFormat string and subsequent arguments are +** passed through to [sqlite3_vmprintf()] to generate the final output string. +** +** The sqlite3_log() interface is intended for use by extensions such as +** virtual tables, collating functions, and SQL functions. While there is +** nothing to prevent an application from calling sqlite3_log(), doing so +** is considered bad form. +** +** The zFormat string must not be NULL. +*/ +void sqlite3_log(int iErrCode, const char *zFormat, ...); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ff90e03079..c14c3c4cb0 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -794,6 +794,7 @@ struct sqlite3 { u8 dfltLockMode; /* Default locking-mode for attached dbs */ u8 dfltJournalMode; /* Default journal mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ + u8 suppressErr; /* Do not issue error messages if true */ int nextPagesize; /* Pagesize after VACUUM if >0 */ int nTable; /* Number of tables in the database */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ @@ -2370,6 +2371,8 @@ struct Sqlite3Config { int isPCacheInit; /* True after malloc is initialized */ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */ int nRefInitMutex; /* Number of users of pInitMutex */ + void (*xLog)(void*,int,const char*); /* Function for logging */ + void *pLogArg; /* First argument to xLog() */ }; /* @@ -2411,17 +2414,19 @@ int sqlite3WalkSelectFrom(Walker*, Select*); } /* -** The SQLITE_CORRUPT_BKPT macro can be either a constant (for production -** builds) or a function call (for debugging). If it is a function call, -** it allows the operator to set a breakpoint at the spot where database -** corruption is first detected. +** The SQLITE_*_BKPT macros are substitutes for the error codes with +** the same name but without the _BKPT suffix. These macros invoke +** routines that report the line-number on which the error originated +** using sqlite3_log(). The routines also provide a convenient place +** to set a debugger breakpoint. */ -#ifdef SQLITE_DEBUG - int sqlite3Corrupt(void); -# define SQLITE_CORRUPT_BKPT sqlite3Corrupt() -#else -# define SQLITE_CORRUPT_BKPT SQLITE_CORRUPT -#endif +int sqlite3CorruptError(int); +int sqlite3MisuseError(int); +int sqlite3CantopenError(int); +#define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__) +#define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__) +#define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__) + /* ** FTS4 is really an extension for FTS3. It is enabled using the @@ -2552,7 +2557,6 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...); #endif void sqlite3SetString(char **, sqlite3*, const char*, ...); void sqlite3ErrorMsg(Parse*, const char*, ...); -void sqlite3ErrorClear(Parse*); int sqlite3Dequote(char*); int sqlite3KeywordCode(const unsigned char*, int); int sqlite3RunParser(Parse*, const char*, char **); @@ -2722,13 +2726,6 @@ FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int); void sqlite3RegisterBuiltinFunctions(sqlite3*); void sqlite3RegisterDateTimeFunctions(void); void sqlite3RegisterGlobalFunctions(void); -#ifdef SQLITE_DEBUG - int sqlite3SafetyOn(sqlite3*); - int sqlite3SafetyOff(sqlite3*); -#else -# define sqlite3SafetyOn(A) 0 -# define sqlite3SafetyOff(A) 0 -#endif int sqlite3SafetyCheckOk(sqlite3*); int sqlite3SafetyCheckSickOrOk(sqlite3*); void sqlite3ChangeCookie(Parse*, int); diff --git a/src/status.c b/src/status.c index 58a7e68c19..f4c77a9109 100644 --- a/src/status.c +++ b/src/status.c @@ -83,7 +83,7 @@ void sqlite3StatusSet(int op, int X){ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ wsdStatInit; if( op<0 || op>=ArraySize(wsdStat.nowValue) ){ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } *pCurrent = wsdStat.nowValue[op]; *pHighwater = wsdStat.mxValue[op]; diff --git a/src/test_config.c b/src/test_config.c index 1cb33e8af1..7350392646 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -183,7 +183,11 @@ static void set_options(Tcl_Interp *interp){ TCL_GLOBAL_ONLY); #endif - +#ifdef SQLITE_OMIT_COMPILEOPTION_DIAGS + Tcl_SetVar2(interp, "sqlite_options", "compileoption_diags", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "compileoption_diags", "1", TCL_GLOBAL_ONLY); +#endif #ifdef SQLITE_OMIT_COMPLETE Tcl_SetVar2(interp, "sqlite_options", "complete", "0", TCL_GLOBAL_ONLY); diff --git a/src/tokenize.c b/src/tokenize.c index a93eeaa781..4b40770d53 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -480,6 +480,7 @@ abort_parse: assert( pzErrMsg!=0 ); if( pParse->zErrMsg ){ *pzErrMsg = pParse->zErrMsg; + sqlite3_log(pParse->rc, "%s", *pzErrMsg); pParse->zErrMsg = 0; nErr++; } diff --git a/src/trigger.c b/src/trigger.c index 51969ce230..f57d5600f2 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -126,7 +126,8 @@ void sqlite3BeginTrigger( goto trigger_cleanup; } pTab = sqlite3SrcListLookup(pParse, pTableName); - if( pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){ + if( db->init.busy==0 && pName2->n==0 && pTab + && pTab->pSchema==db->aDb[1].pSchema ){ iDb = 1; } @@ -254,12 +255,12 @@ void sqlite3FinishTrigger( TriggerStep *pStepList, /* The triggered program */ Token *pAll /* Token that describes the complete CREATE TRIGGER */ ){ - Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */ - char *zName; /* Name of trigger */ - sqlite3 *db = pParse->db; /* The database */ - DbFixer sFix; - int iDb; /* Database containing the trigger */ - Token nameToken; /* Trigger name for error reporting */ + Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */ + char *zName; /* Name of trigger */ + sqlite3 *db = pParse->db; /* The database */ + DbFixer sFix; /* Fixer object */ + int iDb; /* Database containing the trigger */ + Token nameToken; /* Trigger name for error reporting */ pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; @@ -278,7 +279,7 @@ void sqlite3FinishTrigger( goto triggerfinish_cleanup; } - /* if we are not initializing, and this trigger is not on a TEMP table, + /* if we are not initializing, ** build the sqlite_master entry */ if( !db->init.busy ){ diff --git a/src/util.c b/src/util.c index f5c996425c..4ea518159c 100644 --- a/src/util.c +++ b/src/util.c @@ -148,23 +148,20 @@ void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){ ** (sqlite3_step() etc.). */ void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ + char *zMsg; va_list ap; sqlite3 *db = pParse->db; - pParse->nErr++; - sqlite3DbFree(db, pParse->zErrMsg); va_start(ap, zFormat); - pParse->zErrMsg = sqlite3VMPrintf(db, zFormat, ap); + zMsg = sqlite3VMPrintf(db, zFormat, ap); va_end(ap); - pParse->rc = SQLITE_ERROR; -} - -/* -** Clear the error message in pParse, if any -*/ -void sqlite3ErrorClear(Parse *pParse){ - sqlite3DbFree(pParse->db, pParse->zErrMsg); - pParse->zErrMsg = 0; - pParse->nErr = 0; + if( db->suppressErr ){ + sqlite3DbFree(db, zMsg); + }else{ + pParse->nErr++; + sqlite3DbFree(db, pParse->zErrMsg); + pParse->zErrMsg = zMsg; + pParse->rc = SQLITE_ERROR; + } } /* @@ -653,6 +650,19 @@ int sqlite3PutVarint32(unsigned char *p, u32 v){ return sqlite3PutVarint(p, v); } +/* +** Bitmasks used by sqlite3GetVarint(). These precomputed constants +** are defined here rather than simply putting the constant expressions +** inline in order to work around bugs in the RVT compiler. +** +** SLOT_2_0 A mask for (0x7f<<14) | 0x7f +** +** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0 +*/ +#define SLOT_2_0 0x001fc07f +#define SLOT_4_2_0 0xf01fc07f + + /* ** Read a 64-bit variable-length integer from memory starting at p[0]. ** Return the number of bytes read. The value is stored in *v. @@ -680,13 +690,17 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ return 2; } + /* Verify that constants are precomputed correctly */ + assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) ); + assert( SLOT_4_2_0 == ((0xf<<28) | (0x7f<<14) | (0x7f)) ); + p++; a = a<<14; a |= *p; /* a: p0<<14 | p2 (unmasked) */ if (!(a&0x80)) { - a &= (0x7f<<14)|(0x7f); + a &= SLOT_2_0; b &= 0x7f; b = b<<7; a |= b; @@ -695,14 +709,14 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ } /* CSE1 from below */ - a &= (0x7f<<14)|(0x7f); + a &= SLOT_2_0; p++; b = b<<14; b |= *p; /* b: p1<<14 | p3 (unmasked) */ if (!(b&0x80)) { - b &= (0x7f<<14)|(0x7f); + b &= SLOT_2_0; /* moved CSE1 up */ /* a &= (0x7f<<14)|(0x7f); */ a = a<<7; @@ -716,7 +730,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ /* moved CSE1 up */ /* a &= (0x7f<<14)|(0x7f); */ - b &= (0x7f<<14)|(0x7f); + b &= SLOT_2_0; s = a; /* s: p0<<14 | p2 (masked) */ @@ -749,7 +763,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ { /* we can skip this cause it was (effectively) done above in calc'ing s */ /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ - a &= (0x7f<<14)|(0x7f); + a &= SLOT_2_0; a = a<<7; a |= b; s = s>>18; @@ -763,8 +777,8 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ /* a: p2<<28 | p4<<14 | p6 (unmasked) */ if (!(a&0x80)) { - a &= (0x1f<<28)|(0x7f<<14)|(0x7f); - b &= (0x7f<<14)|(0x7f); + a &= SLOT_4_2_0; + b &= SLOT_2_0; b = b<<7; a |= b; s = s>>11; @@ -773,14 +787,14 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ } /* CSE2 from below */ - a &= (0x7f<<14)|(0x7f); + a &= SLOT_2_0; p++; b = b<<14; b |= *p; /* b: p3<<28 | p5<<14 | p7 (unmasked) */ if (!(b&0x80)) { - b &= (0x1f<<28)|(0x7f<<14)|(0x7f); + b &= SLOT_4_2_0; /* moved CSE2 up */ /* a &= (0x7f<<14)|(0x7f); */ a = a<<7; @@ -797,7 +811,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ /* moved CSE2 up */ /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */ - b &= (0x7f<<14)|(0x7f); + b &= SLOT_2_0; b = b<<8; a |= b; @@ -917,9 +931,9 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ /* a: p0<<28 | p2<<14 | p4 (unmasked) */ if (!(a&0x80)) { - /* Walues between 268435456 and 34359738367 */ - a &= (0x1f<<28)|(0x7f<<14)|(0x7f); - b &= (0x1f<<28)|(0x7f<<14)|(0x7f); + /* Values between 268435456 and 34359738367 */ + a &= SLOT_4_2_0; + b &= SLOT_4_2_0; b = b<<7; *v = a | b; return 5; @@ -1012,64 +1026,17 @@ void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ } #endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */ - /* -** Change the sqlite.magic from SQLITE_MAGIC_OPEN to SQLITE_MAGIC_BUSY. -** Return an error (non-zero) if the magic was not SQLITE_MAGIC_OPEN -** when this routine is called. -** -** This routine is called when entering an SQLite API. The SQLITE_MAGIC_OPEN -** value indicates that the database connection passed into the API is -** open and is not being used by another thread. By changing the value -** to SQLITE_MAGIC_BUSY we indicate that the connection is in use. -** sqlite3SafetyOff() below will change the value back to SQLITE_MAGIC_OPEN -** when the API exits. -** -** This routine is a attempt to detect if two threads use the -** same sqlite* pointer at the same time. There is a race -** condition so it is possible that the error is not detected. -** But usually the problem will be seen. The result will be an -** error which can be used to debug the application that is -** using SQLite incorrectly. -** -** Ticket #202: If db->magic is not a valid open value, take care not -** to modify the db structure at all. It could be that db is a stale -** pointer. In other words, it could be that there has been a prior -** call to sqlite3_close(db) and db has been deallocated. And we do -** not want to write into deallocated memory. +** Log an error that is an API call on a connection pointer that should +** not have been used. The "type" of connection pointer is given as the +** argument. The zType is a word like "NULL" or "closed" or "invalid". */ -#ifdef SQLITE_DEBUG -int sqlite3SafetyOn(sqlite3 *db){ - if( db->magic==SQLITE_MAGIC_OPEN ){ - db->magic = SQLITE_MAGIC_BUSY; - assert( sqlite3_mutex_held(db->mutex) ); - return 0; - }else if( db->magic==SQLITE_MAGIC_BUSY ){ - db->magic = SQLITE_MAGIC_ERROR; - db->u1.isInterrupted = 1; - } - return 1; +static void logBadConnection(const char *zType){ + sqlite3_log(SQLITE_MISUSE, + "API call with %s database connection pointer", + zType + ); } -#endif - -/* -** Change the magic from SQLITE_MAGIC_BUSY to SQLITE_MAGIC_OPEN. -** Return an error (non-zero) if the magic was not SQLITE_MAGIC_BUSY -** when this routine is called. -*/ -#ifdef SQLITE_DEBUG -int sqlite3SafetyOff(sqlite3 *db){ - if( db->magic==SQLITE_MAGIC_BUSY ){ - db->magic = SQLITE_MAGIC_OPEN; - assert( sqlite3_mutex_held(db->mutex) ); - return 0; - }else{ - db->magic = SQLITE_MAGIC_ERROR; - db->u1.isInterrupted = 1; - return 1; - } -} -#endif /* ** Check to make sure we have a valid db pointer. This test is not @@ -1087,13 +1054,16 @@ int sqlite3SafetyOff(sqlite3 *db){ */ int sqlite3SafetyCheckOk(sqlite3 *db){ u32 magic; - if( db==0 ) return 0; + if( db==0 ){ + logBadConnection("NULL"); + return 0; + } magic = db->magic; - if( magic!=SQLITE_MAGIC_OPEN -#ifdef SQLITE_DEBUG - && magic!=SQLITE_MAGIC_BUSY -#endif - ){ + if( magic!=SQLITE_MAGIC_OPEN ){ + if( sqlite3SafetyCheckSickOrOk(db) ){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + logBadConnection("unopened"); + } return 0; }else{ return 1; @@ -1104,6 +1074,11 @@ int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ magic = db->magic; if( magic!=SQLITE_MAGIC_SICK && magic!=SQLITE_MAGIC_OPEN && - magic!=SQLITE_MAGIC_BUSY ) return 0; - return 1; + magic!=SQLITE_MAGIC_BUSY ){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + logBadConnection("invalid"); + return 0; + }else{ + return 1; + } } diff --git a/src/vacuum.c b/src/vacuum.c index 9d26da3dae..1d3b998c66 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -18,28 +18,42 @@ #include "vdbeInt.h" #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) +/* +** Finalize a prepared statement. If there was an error, store the +** text of the error message in *pzErrMsg. Return the result code. +*/ +static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){ + int rc; + rc = sqlite3VdbeFinalize((Vdbe*)pStmt); + if( rc ){ + sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db)); + } + return rc; +} + /* ** Execute zSql on database db. Return an error code. */ -static int execSql(sqlite3 *db, const char *zSql){ +static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ sqlite3_stmt *pStmt; VVA_ONLY( int rc; ) if( !zSql ){ return SQLITE_NOMEM; } if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ + sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db)); return sqlite3_errcode(db); } VVA_ONLY( rc = ) sqlite3_step(pStmt); assert( rc!=SQLITE_ROW ); - return sqlite3_finalize(pStmt); + return vacuumFinalize(db, pStmt, pzErrMsg); } /* ** Execute zSql on database db. The statement returns exactly ** one column. Execute this as SQL on the same database. */ -static int execExecSql(sqlite3 *db, const char *zSql){ +static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ sqlite3_stmt *pStmt; int rc; @@ -47,14 +61,14 @@ static int execExecSql(sqlite3 *db, const char *zSql){ if( rc!=SQLITE_OK ) return rc; while( SQLITE_ROW==sqlite3_step(pStmt) ){ - rc = execSql(db, (char*)sqlite3_column_text(pStmt, 0)); + rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0)); if( rc!=SQLITE_OK ){ - sqlite3_finalize(pStmt); + vacuumFinalize(db, pStmt, pzErrMsg); return rc; } } - return sqlite3_finalize(pStmt); + return vacuumFinalize(db, pStmt, pzErrMsg); } /* @@ -124,8 +138,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ ** time to parse and run the PRAGMA to turn journalling off than it does ** to write the journal header file. */ - zSql = "ATTACH '' AS vacuum_db;"; - rc = execSql(db, zSql); + if( sqlite3TempInMemory(db) ){ + zSql = "ATTACH ':memory:' AS vacuum_db;"; + }else{ + zSql = "ATTACH '' AS vacuum_db;"; + } + rc = execSql(db, pzErrMsg, zSql); if( rc!=SQLITE_OK ) goto end_of_vacuum; pDb = &db->aDb[db->nDb-1]; assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); @@ -157,7 +175,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ rc = SQLITE_NOMEM; goto end_of_vacuum; } - rc = execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); + rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF"); if( rc!=SQLITE_OK ){ goto end_of_vacuum; } @@ -168,23 +186,23 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ #endif /* Begin a transaction */ - rc = execSql(db, "BEGIN EXCLUSIVE;"); + rc = execSql(db, pzErrMsg, "BEGIN EXCLUSIVE;"); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Query the schema of the main database. Create a mirror schema ** in the temporary database. */ - rc = execExecSql(db, + rc = execExecSql(db, pzErrMsg, "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) " " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'" " AND rootpage>0" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = execExecSql(db, + rc = execExecSql(db, pzErrMsg, "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)" " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' "); if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = execExecSql(db, + rc = execExecSql(db, pzErrMsg, "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) " " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"); if( rc!=SQLITE_OK ) goto end_of_vacuum; @@ -193,24 +211,23 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ - rc = execExecSql(db, + rc = execExecSql(db, pzErrMsg, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM main.' || quote(name) || ';'" "FROM main.sqlite_master " "WHERE type = 'table' AND name!='sqlite_sequence' " " AND rootpage>0" - ); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy over the sequence table */ - rc = execExecSql(db, + rc = execExecSql(db, pzErrMsg, "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' " "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' " ); if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = execExecSql(db, + rc = execExecSql(db, pzErrMsg, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM main.' || quote(name) || ';' " "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';" @@ -223,7 +240,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ ** associated storage, so all we have to do is copy their entries ** from the SQLITE_MASTER table. */ - rc = execSql(db, + rc = execSql(db, pzErrMsg, "INSERT INTO vacuum_db.sqlite_master " " SELECT type, name, tbl_name, rootpage, sql" " FROM main.sqlite_master" diff --git a/src/vdbe.c b/src/vdbe.c index 35a35ddb8a..7eae498985 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -538,7 +538,7 @@ static int checkSavepointCount(sqlite3 *db){ int sqlite3VdbeExec( Vdbe *p /* The VDBE */ ){ - int pc; /* The program counter */ + int pc=0; /* The program counter */ Op *aOp = p->aOp; /* Copy of p->aOp */ Op *pOp; /* Current operation */ int rc = SQLITE_OK; /* Value to return */ @@ -563,7 +563,6 @@ int sqlite3VdbeExec( /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ - assert( db->magic==SQLITE_MAGIC_BUSY ); sqlite3VdbeMutexArrayEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or @@ -648,9 +647,7 @@ int sqlite3VdbeExec( if( checkProgress ){ if( db->nProgressOps==nProgressOps ){ int prc; - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - prc =db->xProgress(db->pProgressArg); - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; + prc = db->xProgress(db->pProgressArg); if( prc!=0 ){ rc = SQLITE_INTERRUPT; goto vdbe_error_halt; @@ -850,7 +847,13 @@ case OP_Halt: { p->errorAction = (u8)pOp->p2; p->pc = pc; if( pOp->p4.z ){ + assert( p->rc!=SQLITE_OK ); sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z); + }else if( p->rc ){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql); } rc = sqlite3VdbeHalt(p); assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); @@ -1400,21 +1403,12 @@ case OP_Function: { assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = pOp[-1].p4.pColl; } - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; (*ctx.pFunc->xFunc)(&ctx, n, apVal); - if( sqlite3SafetyOn(db) ){ - sqlite3VdbeMemRelease(&ctx.s); - goto abort_due_to_misuse; - } if( db->mallocFailed ){ /* Even though a malloc() has failed, the implementation of the ** user function may have called an sqlite3_result_XXX() function ** to return a value. The following call releases any resources ** associated with such a value. - ** - ** Note: Maybe MemRelease() should be called if sqlite3SafetyOn() - ** fails also (the if(...) statement above). But if people are - ** misusing sqlite, they have bigger problems than a leaked value. */ sqlite3VdbeMemRelease(&ctx.s); goto no_mem; @@ -3119,7 +3113,7 @@ case OP_OpenEphemeral: { ** register P2. In other words, cursor P1 becomes an alias for the ** MEM_Blob content contained in register P2. ** -** A pseudo-table created by this opcode is used to hold the a single +** A pseudo-table created by this opcode is used to hold a single ** row output from the sorter so that the row can be decomposed into ** individual columns using the OP_Column opcode. The OP_Column opcode ** is the only cursor opcode that works with a pseudo-table. @@ -4049,12 +4043,10 @@ case OP_Rowid: { /* out2-prerelease */ pVtab = pC->pVtabCursor->pVtab; pModule = pVtab->pModule; assert( pModule->xRowid ); - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xRowid(pC->pVtabCursor, &v); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ assert( pC->pCursor!=0 ); @@ -4590,7 +4582,6 @@ case OP_ParseSchema: { if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ - (void)sqlite3SafetyOff(db); assert( db->init.busy==0 ); db->init.busy = 1; initData.rc = SQLITE_OK; @@ -4599,7 +4590,6 @@ case OP_ParseSchema: { if( rc==SQLITE_OK ) rc = initData.rc; sqlite3DbFree(db, zSql); db->init.busy = 0; - (void)sqlite3SafetyOn(db); } } sqlite3BtreeLeaveAll(db); @@ -5169,9 +5159,7 @@ case OP_AggFinal: { ** a transaction. */ case OP_Vacuum: { - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = sqlite3RunVacuum(&p->zErrMsg, db); - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; break; } #endif @@ -5315,12 +5303,10 @@ case OP_VOpen: { pVtab = pOp->p4.pVtab->pVtab; pModule = (sqlite3_module *)pVtab->pModule; assert(pVtab && pModule); - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xOpen(pVtab, &pVtabCursor); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( SQLITE_OK==rc ){ /* Initialize sqlite3_vtab_cursor base class */ pVtabCursor->pVtab = pVtab; @@ -5394,7 +5380,6 @@ case OP_VFilter: { /* jump */ sqlite3VdbeMemStoreType(apArg[i]); } - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; p->inVtabMethod = 1; rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); p->inVtabMethod = 0; @@ -5404,7 +5389,6 @@ case OP_VFilter: { /* jump */ if( rc==SQLITE_OK ){ res = pModule->xEof(pVtabCursor); } - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( res ){ pc = pOp->p2 - 1; @@ -5450,7 +5434,6 @@ case OP_VColumn: { sqlite3VdbeMemMove(&sContext.s, pDest); MemSetTypeFlag(&sContext.s, MEM_Null); - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; @@ -5468,9 +5451,6 @@ case OP_VColumn: { REGISTER_TRACE(pOp->p3, pDest); UPDATE_MAX_BLOBSIZE(pDest); - if( sqlite3SafetyOn(db) ){ - goto abort_due_to_misuse; - } if( sqlite3VdbeMemTooBig(pDest) ){ goto too_big; } @@ -5507,7 +5487,6 @@ case OP_VNext: { /* jump */ ** data is available) and the error code returned when xColumn or ** some other method is next invoked on the save virtual table cursor. */ - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; p->inVtabMethod = 1; rc = pModule->xNext(pCur->pVtabCursor); p->inVtabMethod = 0; @@ -5517,7 +5496,6 @@ case OP_VNext: { /* jump */ if( rc==SQLITE_OK ){ res = pModule->xEof(pCur->pVtabCursor); } - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( !res ){ /* If there is data, jump to P2 */ @@ -5543,12 +5521,10 @@ case OP_VRename: { assert( pVtab->pModule->xRename ); REGISTER_TRACE(pOp->p1, pName); assert( pName->flags & MEM_Str ); - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pVtab->pModule->xRename(pVtab, pName->z); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; break; } @@ -5599,12 +5575,10 @@ case OP_VUpdate: { apArg[i] = pX; pX++; } - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( rc==SQLITE_OK && pOp->p1 ){ assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); db->lastRowid = rowid; @@ -5729,6 +5703,7 @@ default: { /* This is really OP_Noop and OP_Explain */ vdbe_error_halt: assert( rc ); p->rc = rc; + sqlite3_log(rc, "prepared statement aborts at %d: [%s]", pc, p->zSql); sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; @@ -5757,12 +5732,6 @@ no_mem: rc = SQLITE_NOMEM; goto vdbe_error_halt; - /* Jump to here for an SQLITE_MISUSE error. - */ -abort_due_to_misuse: - rc = SQLITE_MISUSE; - /* Fall thru into abort_due_to_error */ - /* Jump to here for any other kind of fatal error. The "rc" variable ** should hold the error number. */ diff --git a/src/vdbe.h b/src/vdbe.h index f67e903b85..c234d51a5b 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -182,6 +182,7 @@ void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); +void sqlite3VdbeRunOnlyOnce(Vdbe*); void sqlite3VdbeDelete(Vdbe*); void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int); int sqlite3VdbeFinalize(Vdbe*); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index dd958cdd0a..489e8da6a7 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -301,6 +301,7 @@ struct Vdbe { u8 explain; /* True if EXPLAIN present on SQL command */ u8 changeCntOn; /* True to update the change-counter */ u8 expired; /* True if the VM needs to be recompiled */ + u8 runOnlyOnce; /* Automatically expire on reset */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 inVtabMethod; /* See comments above */ u8 usesStmtJournal; /* True if uses a statement journal */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 964c620e3c..125f325d18 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -31,6 +31,28 @@ int sqlite3_expired(sqlite3_stmt *pStmt){ } #endif +/* +** Check on a Vdbe to make sure it has not been finalized. Log +** an error and return true if it has been finalized (or is otherwise +** invalid). Return false if it is ok. +*/ +static int vdbeSafety(Vdbe *p){ + if( p->db==0 ){ + sqlite3_log(SQLITE_MISUSE, "API called with finalized prepared statement"); + return 1; + }else{ + return 0; + } +} +static int vdbeSafetyNotNull(Vdbe *p){ + if( p==0 ){ + sqlite3_log(SQLITE_MISUSE, "API called with NULL prepared statement"); + return 1; + }else{ + return vdbeSafety(p); + } +} + /* ** The following routine destroys a virtual machine that is created by ** the sqlite3_compile() routine. The integer returned is an SQLITE_ @@ -50,7 +72,7 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){ #if SQLITE_THREADSAFE sqlite3_mutex *mutex; #endif - if( db==0 ) return SQLITE_MISUSE; + if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; #if SQLITE_THREADSAFE mutex = v->db->mutex; #endif @@ -299,7 +321,9 @@ static int sqlite3Step(Vdbe *p){ assert(p); if( p->magic!=VDBE_MAGIC_RUN ){ - return SQLITE_MISUSE; + sqlite3_log(SQLITE_MISUSE, + "attempt to step a halted statement: [%s]", p->zSql); + return SQLITE_MISUSE_BKPT; } /* Check that malloc() has not failed. If it has, return early. */ @@ -310,16 +334,10 @@ static int sqlite3Step(Vdbe *p){ } if( p->pc<=0 && p->expired ){ - if( ALWAYS(p->rc==SQLITE_OK || p->rc==SQLITE_SCHEMA) ){ - p->rc = SQLITE_SCHEMA; - } + p->rc = SQLITE_SCHEMA; rc = SQLITE_ERROR; goto end_of_step; } - if( sqlite3SafetyOn(db) ){ - p->rc = SQLITE_MISUSE; - return SQLITE_MISUSE; - } if( p->pc<0 ){ /* If there are no other statements currently running, then ** reset the interrupt flag. This prevents a call to sqlite3_interrupt @@ -352,10 +370,6 @@ static int sqlite3Step(Vdbe *p){ rc = sqlite3VdbeExec(p); } - if( sqlite3SafetyOff(db) ){ - rc = SQLITE_MISUSE; - } - #ifndef SQLITE_OMIT_TRACE /* Invoke the profile callback if there is one */ @@ -402,39 +416,44 @@ end_of_step: ** call sqlite3Reprepare() and try again. */ int sqlite3_step(sqlite3_stmt *pStmt){ - int rc = SQLITE_MISUSE; - Vdbe *v = (Vdbe*)pStmt; - if( v && (v->db)!=0 ){ - int cnt = 0; - sqlite3 *db = v->db; - sqlite3_mutex_enter(db->mutex); - while( (rc = sqlite3Step(v))==SQLITE_SCHEMA - && cnt++ < 5 - && (rc = sqlite3Reprepare(v))==SQLITE_OK ){ - sqlite3_reset(pStmt); - v->expired = 0; - } - if( rc==SQLITE_SCHEMA && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){ - /* This case occurs after failing to recompile an sql statement. - ** The error message from the SQL compiler has already been loaded - ** into the database handle. This block copies the error message - ** from the database handle into the statement and sets the statement - ** program counter to 0 to ensure that when the statement is - ** finalized or reset the parser error message is available via - ** sqlite3_errmsg() and sqlite3_errcode(). - */ - const char *zErr = (const char *)sqlite3_value_text(db->pErr); - sqlite3DbFree(db, v->zErrMsg); - if( !db->mallocFailed ){ - v->zErrMsg = sqlite3DbStrDup(db, zErr); - } else { - v->zErrMsg = 0; - v->rc = SQLITE_NOMEM; - } - } - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); + int rc = SQLITE_OK; /* Result from sqlite3Step() */ + int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */ + Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */ + int cnt = 0; /* Counter to prevent infinite loop of reprepares */ + sqlite3 *db; /* The database connection */ + + if( vdbeSafetyNotNull(v) ){ + return SQLITE_MISUSE_BKPT; } + db = v->db; + sqlite3_mutex_enter(db->mutex); + while( (rc = sqlite3Step(v))==SQLITE_SCHEMA + && cnt++ < 5 + && (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){ + sqlite3_reset(pStmt); + v->expired = 0; + } + if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){ + /* This case occurs after failing to recompile an sql statement. + ** The error message from the SQL compiler has already been loaded + ** into the database handle. This block copies the error message + ** from the database handle into the statement and sets the statement + ** program counter to 0 to ensure that when the statement is + ** finalized or reset the parser error message is available via + ** sqlite3_errmsg() and sqlite3_errcode(). + */ + const char *zErr = (const char *)sqlite3_value_text(db->pErr); + sqlite3DbFree(db, v->zErrMsg); + if( !db->mallocFailed ){ + v->zErrMsg = sqlite3DbStrDup(db, zErr); + v->rc = rc2; + } else { + v->zErrMsg = 0; + v->rc = rc = SQLITE_NOMEM; + } + } + rc = sqlite3ApiExit(db, rc); + sqlite3_mutex_leave(db->mutex); return rc; } @@ -904,12 +923,16 @@ const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ */ static int vdbeUnbind(Vdbe *p, int i){ Mem *pVar; - if( p==0 ) return SQLITE_MISUSE; + if( vdbeSafetyNotNull(p) ){ + return SQLITE_MISUSE_BKPT; + } sqlite3_mutex_enter(p->db->mutex); if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ sqlite3Error(p->db, SQLITE_MISUSE, 0); sqlite3_mutex_leave(p->db->mutex); - return SQLITE_MISUSE; + sqlite3_log(SQLITE_MISUSE, + "bind on a busy prepared statement: [%s]", p->zSql); + return SQLITE_MISUSE_BKPT; } if( i<1 || i>p->nVar ){ sqlite3Error(p->db, SQLITE_RANGE, 0); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index c8cf4af854..3612a1aa86 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -255,6 +255,13 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){ } } +/* +** Mark the VDBE as one that can only be run one time. +*/ +void sqlite3VdbeRunOnlyOnce(Vdbe *p){ + p->runOnlyOnce = 1; +} + #ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */ /* @@ -1059,7 +1066,6 @@ int sqlite3VdbeList( assert( p->explain ); 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 @@ -1474,9 +1480,7 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; const sqlite3_module *pModule = pCx->pModule; p->inVtabMethod = 1; - (void)sqlite3SafetyOff(p->db); pModule->xClose(pVtabCursor); - (void)sqlite3SafetyOn(p->db); p->inVtabMethod = 0; } #endif @@ -1657,9 +1661,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ - (void)sqlite3SafetyOff(db); rc = db->xCommitCallback(db->pCommitArg); - (void)sqlite3SafetyOn(db); if( rc ){ return SQLITE_CONSTRAINT; } @@ -2213,9 +2215,7 @@ int sqlite3VdbeReset(Vdbe *p){ ** error, then it might not have been halted properly. So halt ** it now. */ - (void)sqlite3SafetyOn(db); sqlite3VdbeHalt(p); - (void)sqlite3SafetyOff(db); /* If the VDBE has be run even partially, then transfer the error code ** and error message from the VDBE into the main database structure. But @@ -2235,6 +2235,7 @@ int sqlite3VdbeReset(Vdbe *p){ }else{ sqlite3Error(db, SQLITE_OK, 0); } + if( p->runOnlyOnce ) p->expired = 1; }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call ** to sqlite3_step(). For consistency (since sqlite3_step() was @@ -3017,7 +3018,7 @@ int sqlite3VdbeIdxKeyCompare( ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ *res = 0; - return SQLITE_CORRUPT; + return SQLITE_CORRUPT_BKPT; } memset(&m, 0, sizeof(m)); rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 6a6c6a71fc..829b6de6dd 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -95,13 +95,6 @@ int sqlite3_blob_open( memset(pParse, 0, sizeof(Parse)); pParse->db = db; - if( sqlite3SafetyOn(db) ){ - sqlite3DbFree(db, zErr); - sqlite3StackFree(db, pParse); - sqlite3_mutex_leave(db->mutex); - return SQLITE_MISUSE; - } - sqlite3BtreeEnterAll(db); pTab = sqlite3LocateTable(pParse, 0, zTable, zDb); if( pTab && IsVirtual(pTab) ){ @@ -121,7 +114,6 @@ int sqlite3_blob_open( pParse->zErrMsg = 0; } rc = SQLITE_ERROR; - (void)sqlite3SafetyOff(db); sqlite3BtreeLeaveAll(db); goto blob_open_out; } @@ -136,7 +128,6 @@ int sqlite3_blob_open( sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); rc = SQLITE_ERROR; - (void)sqlite3SafetyOff(db); sqlite3BtreeLeaveAll(db); goto blob_open_out; } @@ -177,7 +168,6 @@ int sqlite3_blob_open( sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); rc = SQLITE_ERROR; - (void)sqlite3SafetyOff(db); sqlite3BtreeLeaveAll(db); goto blob_open_out; } @@ -227,8 +217,7 @@ int sqlite3_blob_open( } sqlite3BtreeLeaveAll(db); - rc = sqlite3SafetyOff(db); - if( NEVER(rc!=SQLITE_OK) || db->mallocFailed ){ + if( db->mallocFailed ){ goto blob_open_out; } @@ -329,7 +318,7 @@ static int blobReadWrite( Vdbe *v; sqlite3 *db; - if( p==0 ) return SQLITE_MISUSE; + if( p==0 ) return SQLITE_MISUSE_BKPT; db = p->db; sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; diff --git a/src/vtab.c b/src/vtab.c index d0bc22dcee..cbb7523549 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -123,16 +123,7 @@ void sqlite3VtabUnlock(VTable *pVTab){ if( pVTab->nRef==0 ){ sqlite3_vtab *p = pVTab->pVtab; if( p ){ -#ifdef SQLITE_DEBUG - if( pVTab->db->magic==SQLITE_MAGIC_BUSY ){ - (void)sqlite3SafetyOff(db); - p->pModule->xDisconnect(p); - (void)sqlite3SafetyOn(db); - } else -#endif - { - p->pModule->xDisconnect(p); - } + p->pModule->xDisconnect(p); } sqlite3DbFree(db, pVTab); } @@ -468,9 +459,7 @@ static int vtabCallConstructor( db->pVTab = pTab; /* Invoke the virtual table constructor */ - (void)sqlite3SafetyOff(db); rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); - (void)sqlite3SafetyOn(db); if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; if( SQLITE_OK!=rc ){ @@ -658,7 +647,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ if( !pTab ){ sqlite3Error(db, SQLITE_MISUSE, 0); sqlite3_mutex_leave(db->mutex); - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } assert( (pTab->tabFlags & TF_Virtual)!=0 ); @@ -717,10 +706,8 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ VTable *p = vtabDisconnectAll(db, pTab); - rc = sqlite3SafetyOff(db); assert( rc==SQLITE_OK ); rc = p->pMod->pModule->xDestroy(p->pVtab); - (void)sqlite3SafetyOn(db); /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ @@ -772,10 +759,8 @@ static void callFinaliser(sqlite3 *db, int offset){ int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ int i; int rc = SQLITE_OK; - int rcsafety; VTable **aVTrans = db->aVTrans; - rc = sqlite3SafetyOff(db); db->aVTrans = 0; for(i=0; rc==SQLITE_OK && inVTrans; i++){ int (*x)(sqlite3_vtab *); @@ -788,11 +773,6 @@ int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ } } db->aVTrans = aVTrans; - rcsafety = sqlite3SafetyOn(db); - - if( rc==SQLITE_OK ){ - rc = rcsafety; - } return rc; } diff --git a/src/where.c b/src/where.c index 1e6fea87af..5f81ce0768 100644 --- a/src/where.c +++ b/src/where.c @@ -1096,7 +1096,7 @@ static void exprAnalyze( Expr *pExpr; /* The expression to be analyzed */ Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ Bitmask prereqAll; /* Prerequesites of pExpr */ - Bitmask extraRight = 0; /* */ + Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ int noCase = 0; /* LIKE/GLOB distinguishes case */ @@ -1168,7 +1168,8 @@ static void exprAnalyze( pLeft = pDup->pLeft; pNew->leftCursor = pLeft->iTable; pNew->u.leftColumn = pLeft->iColumn; - pNew->prereqRight = prereqLeft; + testcase( (prereqLeft | extraRight) != prereqLeft ); + pNew->prereqRight = prereqLeft | extraRight; pNew->prereqAll = prereqAll; pNew->eOperator = operatorMask(pDup->op); } @@ -1758,12 +1759,10 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ int i; int rc; - (void)sqlite3SafetyOff(pParse->db); WHERETRACE(("xBestIndex for %s\n", pTab->zName)); TRACE_IDX_INPUTS(p); rc = pVtab->pModule->xBestIndex(pVtab, p); TRACE_IDX_OUTPUTS(p); - (void)sqlite3SafetyOn(pParse->db); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ){ diff --git a/test/analyze3.test b/test/analyze3.test index f623f987a4..438ecd7b2b 100644 --- a/test/analyze3.test +++ b/test/analyze3.test @@ -481,10 +481,10 @@ do_test analyze3-4.1.2 { sqlite3_bind_text $S 2 "abc" 3 execsql { DROP TABLE t1 } sqlite3_step $S -} {SQLITE_SCHEMA} +} {SQLITE_ERROR} do_test analyze3-4.1.3 { sqlite3_finalize $S -} {SQLITE_SCHEMA} +} {SQLITE_ERROR} # Check an authorization error. # @@ -511,10 +511,10 @@ do_test analyze3-4.2.2 { sqlite3_reset $S sqlite3_bind_text $S 2 "abc" 3 sqlite3_step $S -} {SQLITE_SCHEMA} +} {SQLITE_AUTH} do_test analyze3-4.2.4 { sqlite3_finalize $S -} {SQLITE_SCHEMA} +} {SQLITE_AUTH} # Check the effect of an authorization error that occurs in a re-prepare # performed by sqlite3_step() is the same as one that occurs within @@ -526,10 +526,10 @@ do_test analyze3-4.3.1 { execsql { CREATE TABLE t2(d, e, f) } db auth auth sqlite3_step $S -} {SQLITE_SCHEMA} +} {SQLITE_AUTH} do_test analyze3-4.3.2 { sqlite3_finalize $S -} {SQLITE_SCHEMA} +} {SQLITE_AUTH} db auth {} #------------------------------------------------------------------------- diff --git a/test/attachmalloc.test b/test/attachmalloc.test index 30841cea76..cc506bee6a 100644 --- a/test/attachmalloc.test +++ b/test/attachmalloc.test @@ -60,4 +60,17 @@ do_malloc_test attachmalloc-2 -tclprep { ATTACH 'test2.db' AS db1; } +set enable_shared_cache [sqlite3_enable_shared_cache 1] +sqlite3 dbaux test2.db +dbaux eval {SELECT * FROM sqlite_master} +do_malloc_test attachmalloc-3 -sqlbody { + SELECT * FROM sqlite_master; + ATTACH 'test2.db' AS two; +} -cleanup { + db eval { DETACH two } +} +dbaux close +sqlite3_enable_shared_cache $enable_shared_cache + + finish_test diff --git a/test/capi3c.test b/test/capi3c.test index 1bcc4373c2..55f4667e15 100644 --- a/test/capi3c.test +++ b/test/capi3c.test @@ -1176,7 +1176,7 @@ do_test capi3c-19.4 { sqlite3_reset $STMT db eval {DROP TABLE t3} sqlite3_step $STMT -} SQLITE_SCHEMA +} SQLITE_ERROR do_test capi3c-19.4.1 { sqlite3_errmsg $DB } {no such table: t3} diff --git a/test/corrupt.test b/test/corrupt.test index be6236ea37..fc84033fc4 100644 --- a/test/corrupt.test +++ b/test/corrupt.test @@ -1,4 +1,4 @@ -# 2004 August 30 +# 2004 August 30 {} # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: @@ -311,4 +311,41 @@ ifcapable oversize_cell_check { } {1 {database disk image is malformed}} } +db close +file delete -force test.db test.db-journal +do_test corrupt-8.1 { + sqlite3 db test.db + execsql { + PRAGMA page_size = 1024; + PRAGMA secure_delete = on; + PRAGMA auto_vacuum = 0; + CREATE TABLE t1(x INTEGER PRIMARY KEY, y); + INSERT INTO t1 VALUES(5, randomblob(1900)); + } + + hexio_write test.db 2044 [hexio_render_int32 2] + hexio_write test.db 24 [hexio_render_int32 45] + + catchsql { INSERT OR REPLACE INTO t1 VALUES(5, randomblob(1900)) } +} {1 {database disk image is malformed}} + +db close +file delete -force test.db test.db-journal +do_test corrupt-8.2 { + sqlite3 db test.db + execsql { + PRAGMA page_size = 1024; + PRAGMA secure_delete = on; + PRAGMA auto_vacuum = 0; + CREATE TABLE t1(x INTEGER PRIMARY KEY, y); + INSERT INTO t1 VALUES(5, randomblob(900)); + INSERT INTO t1 VALUES(6, randomblob(900)); + } + + hexio_write test.db 2047 FF + hexio_write test.db 24 [hexio_render_int32 45] + + catchsql { INSERT INTO t1 VALUES(4, randomblob(1900)) } +} {1 {database disk image is malformed}} + finish_test diff --git a/test/corrupt7.test b/test/corrupt7.test index 8f8d437a62..39aa62018e 100644 --- a/test/corrupt7.test +++ b/test/corrupt7.test @@ -89,7 +89,7 @@ Corruption detected in cell 15 on page 2}} sqlite3 db test.db db eval {PRAGMA integrity_check(1)} } {{*** in database main *** -Corruption detected in cell 15 on page 2}} +On tree page 2 cell 15: Rowid 0 out of order (previous was 15)}} } # The code path that was causing the buffer overrun that this test diff --git a/test/corruptE.test b/test/corruptE.test new file mode 100644 index 0000000000..35fa545b4d --- /dev/null +++ b/test/corruptE.test @@ -0,0 +1,171 @@ +# 2010 February 18 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to make sure SQLite does not crash or +# segfault if it sees a corrupt database file. It specifcally +# focuses on rowid order corruption. +# +# $Id: corruptE.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $ + +catch {file delete -force test.db test.db-journal test.bu} + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Construct a compact, dense database for testing. +# +do_test corruptE-1.1 { + execsql { + PRAGMA auto_vacuum = 0; + PRAGMA legacy_file_format=1; + BEGIN; + CREATE TABLE t1(x,y); + INSERT INTO t1 VALUES(1,1); + INSERT OR IGNORE INTO t1 SELECT x*2,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*3,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*5,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*7,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*11,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*13,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*17,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*19,y FROM t1; + CREATE INDEX t1i1 ON t1(x); + CREATE TABLE t2 AS SELECT x,2 as y FROM t1 WHERE rowid%5!=0; + COMMIT; + } +} {} + +ifcapable {integrityck} { + integrity_check corruptE-1.2 +} + +# Copy file $from into $to +# +proc copy_file {from to} { + file copy -force $from $to +} + +# Setup for the tests. Make a backup copy of the good database in test.bu. +# +db close +copy_file test.db test.bu +sqlite3 db test.db +set fsize [file size test.db] + + +do_test corruptE-2.1 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 2041 [format %02x 0x2e] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*previous was} $ans] \ + [regexp {out of order.*max larger than parent max} $ans] +} {1 1} + +do_test corruptE-2.2 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 2047 [format %02x 0x84] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*previous was} $ans] \ + [regexp {out of order.*min less than parent min} $ans] +} {1 1} + +do_test corruptE-2.3 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 7420 [format %02x 0xa8] + hexio_write test.db 10459 [format %02x 0x8d] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*max larger than parent min} $ans] +} {1} + +do_test corruptE-2.4 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 10233 [format %02x 0xd0] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*min less than parent max} $ans] +} {1} + + +set tests [list {10233 0xd0} \ + {941 0x42} \ + {1028 0x53} \ + {2041 0xd0} \ + {2042 0x1f} \ + {2047 0xaa} \ + {2263 0x29} \ + {2274 0x75} \ + {3267 0xf2} \ + {4104 0x2c} \ + {5113 0x36} \ + {10233 0x84} \ + {10234 0x74} \ + {10239 0x41} \ + {10453 0x11} \ + {11273 0x28} \ + {11455 0x11} \ + {11461 0xe6} \ + {12281 0x99} \ + {12296 0x9e} \ + {12297 0xd7} \ + {13303 0x53} ] + +set tc 1 +foreach test $tests { + do_test corruptE-3.$tc { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db [lindex $test 0] [format %02x [lindex $test 1]] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order} $ans] + } {1} + incr tc 1 +} + +finish_test diff --git a/test/ctime.test b/test/ctime.test new file mode 100644 index 0000000000..bb184b8b9d --- /dev/null +++ b/test/ctime.test @@ -0,0 +1,224 @@ +# 2009 February 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests for the compile time diagnostic +# functions. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Test organization: +# +# ctime-1.*: Test pragma support. +# ctime-2.*: Test function support. +# + +ifcapable !pragma||!compileoption_diags { + finish_test + return +} + +##################### +# ctime-1.*: Test pragma support. + +do_test ctime-1.1.1 { + catchsql { + PRAGMA compile_options(); + } +} {1 {near ")": syntax error}} +do_test ctime-1.1.2 { + catchsql { + PRAGMA compile_options(NULL); + } +} {1 {near "NULL": syntax error}} +do_test ctime-1.1.3 { + catchsql { + PRAGMA compile_options *; + } +} {1 {near "*": syntax error}} + +do_test ctime-1.2.1 { + set ans [ catchsql { + PRAGMA compile_options; + } ] + list [ lindex $ans 0 ] +} {0} +# the results should be in sorted order already +do_test ctime-1.2.2 { + set ans [ catchsql { + PRAGMA compile_options; + } ] + list [ lindex $ans 0 ] [ expr { [lsort [lindex $ans 1]]==[lindex $ans 1] } ] +} {0 1} + +# SQLITE_THREADSAFE should pretty much always be defined +# one way or the other, and it must have a value of 0 or 1. +do_test ctime-1.4.1 { + catchsql { + SELECT sqlite_compileoption_used('SQLITE_THREADSAFE'); + } +} {0 1} +do_test ctime-1.4.2 { + catchsql { + SELECT sqlite_compileoption_used('THREADSAFE'); + } +} {0 1} +do_test ctime-1.4.3 { + catchsql { + SELECT sqlite_compileoption_used("THREADSAFE"); + } +} {0 1} + +do_test ctime-1.5 { + set ans1 [ catchsql { + SELECT sqlite_compileoption_used('THREADSAFE=0'); + } ] + set ans2 [ catchsql { + SELECT sqlite_compileoption_used('THREADSAFE=1'); + } ] + lsort [ list $ans1 $ans2 ] +} {{0 0} {0 1}} + +do_test ctime-1.6 { + execsql { + SELECT sqlite_compileoption_used('THREADSAFE='); + } +} {0} + +do_test ctime-1.7.1 { + execsql { + SELECT sqlite_compileoption_used('SQLITE_OMIT_COMPILEOPTION_DIAGS'); + } +} {0} +do_test ctime-1.7.2 { + execsql { + SELECT sqlite_compileoption_used('OMIT_COMPILEOPTION_DIAGS'); + } +} {0} + +##################### +# ctime-2.*: Test function support. + +do_test ctime-2.1.1 { + catchsql { + SELECT sqlite_compileoption_used(); + } +} {1 {wrong number of arguments to function sqlite_compileoption_used()}} +do_test ctime-2.1.2 { + catchsql { + SELECT sqlite_compileoption_used(NULL); + } +} {0 {{}}} +do_test ctime-2.1.3 { + catchsql { + SELECT sqlite_compileoption_used(""); + } +} {0 0} +do_test ctime-2.1.4 { + catchsql { + SELECT sqlite_compileoption_used(''); + } +} {0 0} +do_test ctime-2.1.5 { + catchsql { + SELECT sqlite_compileoption_used(foo); + } +} {1 {no such column: foo}} +do_test ctime-2.1.6 { + catchsql { + SELECT sqlite_compileoption_used('THREADSAFE', 0); + } +} {1 {wrong number of arguments to function sqlite_compileoption_used()}} +do_test ctime-2.1.7 { + catchsql { + SELECT sqlite_compileoption_used(0); + } +} {0 0} +do_test ctime-2.1.8 { + catchsql { + SELECT sqlite_compileoption_used('0'); + } +} {0 0} +do_test ctime-2.1.9 { + catchsql { + SELECT sqlite_compileoption_used(1.0); + } +} {0 0} + +do_test ctime-2.2.1 { + catchsql { + SELECT sqlite_compileoption_get(); + } +} {1 {wrong number of arguments to function sqlite_compileoption_get()}} +do_test ctime-2.2.2 { + catchsql { + SELECT sqlite_compileoption_get(0, 0); + } +} {1 {wrong number of arguments to function sqlite_compileoption_get()}} + +# This assumes there is at least 1 compile time option +# (see SQLITE_THREADSAFE above). +do_test ctime-2.3 { + catchsql { + SELECT sqlite_compileoption_used(sqlite_compileoption_get(0)); + } +} {0 1} + +# This assumes there is at least 1 compile time option +# (see SQLITE_THREADSAFE above). +do_test ctime-2.4 { + set ans [ catchsql { + SELECT sqlite_compileoption_get(0); + } ] + list [lindex $ans 0] +} {0} + +# Get the list of defines using the pragma, +# then try querying each one with the functions. +set ans [ catchsql { + PRAGMA compile_options; +} ] +set opts [ lindex $ans 1 ] +set tc 1 +foreach opt $opts { + do_test ctime-2.5.$tc { + set N [ expr {$tc-1} ] + set ans1 [ catchsql { + SELECT sqlite_compileoption_get($N); + } ] + set ans2 [ catchsql { + SELECT sqlite_compileoption_used($opt); + } ] + list [ lindex $ans1 0 ] [ expr { [lindex $ans1 1]==$opt } ] \ + [ expr { $ans2 } ] + } {0 1 {0 1}} + incr tc 1 +} +# test 1 past array bounds +do_test ctime-2.5.$tc { + set N [ expr {$tc-1} ] + set ans [ catchsql { + SELECT sqlite_compileoption_get($N); + } ] +} {0 {{}}} +incr tc 1 +# test 1 before array bounds (N=-1) +do_test ctime-2.5.$tc { + set N -1 + set ans [ catchsql { + SELECT sqlite_compileoption_get($N); + } ] +} {0 {{}}} + + +finish_test diff --git a/test/fkey2.test b/test/fkey2.test index dc6529be13..10a624ef0a 100644 --- a/test/fkey2.test +++ b/test/fkey2.test @@ -1003,6 +1003,169 @@ ifcapable altertable { do_test fkey2-14.2.2.7 { execsql { INSERT INTO t3 VALUES(1, NULL, 1) } } {} + + # Repeat for TEMP tables + # + drop_all_tables + do_test fkey2-14.1tmp.1 { + # Adding a column with a REFERENCES clause is not supported. + execsql { + CREATE TEMP TABLE t1(a PRIMARY KEY); + CREATE TEMP TABLE t2(a, b); + } + catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } + } {0 {}} + do_test fkey2-14.1tmp.2 { + catchsql { ALTER TABLE t2 ADD COLUMN d DEFAULT NULL REFERENCES t1 } + } {0 {}} + do_test fkey2-14.1tmp.3 { + catchsql { ALTER TABLE t2 ADD COLUMN e REFERENCES t1 DEFAULT NULL} + } {0 {}} + do_test fkey2-14.1tmp.4 { + catchsql { ALTER TABLE t2 ADD COLUMN f REFERENCES t1 DEFAULT 'text'} + } {1 {Cannot add a REFERENCES column with non-NULL default value}} + do_test fkey2-14.1tmp.5 { + catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 } + } {1 {Cannot add a REFERENCES column with non-NULL default value}} + do_test fkey2-14.1tmp.6 { + execsql { + PRAGMA foreign_keys = off; + ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; + PRAGMA foreign_keys = on; + SELECT sql FROM sqlite_temp_master WHERE name='t2'; + } + } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} + + do_test fkey2-14.2tmp.1.1 { + test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 + } {{CREATE TABLE t1(a REFERENCES "t3")}} + do_test fkey2-14.2tmp.1.2 { + test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 + } {{CREATE TABLE t1(a REFERENCES t2)}} + do_test fkey2-14.2tmp.1.3 { + test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 + } {{CREATE TABLE t1(a REFERENCES "t3")}} + + # Test ALTER TABLE RENAME TABLE a bit. + # + do_test fkey2-14.2tmp.2.1 { + drop_all_tables + execsql { + CREATE TEMP TABLE t1(a PRIMARY KEY, b REFERENCES t1); + CREATE TEMP TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2); + CREATE TEMP TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1); + } + execsql { SELECT sql FROM sqlite_temp_master WHERE type = 'table'} + } [list \ + {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1)} \ + {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)} \ + {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \ + ] + do_test fkey2-14.2tmp.2.2 { + execsql { ALTER TABLE t1 RENAME TO t4 } + execsql { SELECT sql FROM sqlite_temp_master WHERE type = 'table'} + } [list \ + {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4")} \ + {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2)} \ + {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \ + ] + do_test fkey2-14.2tmp.2.3 { + catchsql { INSERT INTO t3 VALUES(1, 2, 3) } + } {1 {foreign key constraint failed}} + do_test fkey2-14.2tmp.2.4 { + execsql { INSERT INTO t4 VALUES(1, NULL) } + } {} + do_test fkey2-14.2tmp.2.5 { + catchsql { UPDATE t4 SET b = 5 } + } {1 {foreign key constraint failed}} + do_test fkey2-14.2tmp.2.6 { + catchsql { UPDATE t4 SET b = 1 } + } {0 {}} + do_test fkey2-14.2tmp.2.7 { + execsql { INSERT INTO t3 VALUES(1, NULL, 1) } + } {} + + # Repeat for ATTACH-ed tables + # + drop_all_tables + do_test fkey2-14.1aux.1 { + # Adding a column with a REFERENCES clause is not supported. + execsql { + ATTACH ':memory:' AS aux; + CREATE TABLE aux.t1(a PRIMARY KEY); + CREATE TABLE aux.t2(a, b); + } + catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } + } {0 {}} + do_test fkey2-14.1aux.2 { + catchsql { ALTER TABLE t2 ADD COLUMN d DEFAULT NULL REFERENCES t1 } + } {0 {}} + do_test fkey2-14.1aux.3 { + catchsql { ALTER TABLE t2 ADD COLUMN e REFERENCES t1 DEFAULT NULL} + } {0 {}} + do_test fkey2-14.1aux.4 { + catchsql { ALTER TABLE t2 ADD COLUMN f REFERENCES t1 DEFAULT 'text'} + } {1 {Cannot add a REFERENCES column with non-NULL default value}} + do_test fkey2-14.1aux.5 { + catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 } + } {1 {Cannot add a REFERENCES column with non-NULL default value}} + do_test fkey2-14.1aux.6 { + execsql { + PRAGMA foreign_keys = off; + ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; + PRAGMA foreign_keys = on; + SELECT sql FROM aux.sqlite_master WHERE name='t2'; + } + } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} + + do_test fkey2-14.2aux.1.1 { + test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 + } {{CREATE TABLE t1(a REFERENCES "t3")}} + do_test fkey2-14.2aux.1.2 { + test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 + } {{CREATE TABLE t1(a REFERENCES t2)}} + do_test fkey2-14.2aux.1.3 { + test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 + } {{CREATE TABLE t1(a REFERENCES "t3")}} + + # Test ALTER TABLE RENAME TABLE a bit. + # + do_test fkey2-14.2aux.2.1 { + drop_all_tables + execsql { + CREATE TABLE aux.t1(a PRIMARY KEY, b REFERENCES t1); + CREATE TABLE aux.t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2); + CREATE TABLE aux.t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1); + } + execsql { SELECT sql FROM aux.sqlite_master WHERE type = 'table'} + } [list \ + {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1)} \ + {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)} \ + {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \ + ] + do_test fkey2-14.2aux.2.2 { + execsql { ALTER TABLE t1 RENAME TO t4 } + execsql { SELECT sql FROM aux.sqlite_master WHERE type = 'table'} + } [list \ + {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4")} \ + {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2)} \ + {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \ + ] + do_test fkey2-14.2aux.2.3 { + catchsql { INSERT INTO t3 VALUES(1, 2, 3) } + } {1 {foreign key constraint failed}} + do_test fkey2-14.2aux.2.4 { + execsql { INSERT INTO t4 VALUES(1, NULL) } + } {} + do_test fkey2-14.2aux.2.5 { + catchsql { UPDATE t4 SET b = 5 } + } {1 {foreign key constraint failed}} + do_test fkey2-14.2aux.2.6 { + catchsql { UPDATE t4 SET b = 1 } + } {0 {}} + do_test fkey2-14.2aux.2.7 { + execsql { INSERT INTO t3 VALUES(1, NULL, 1) } + } {} } do_test fkey-2.14.3.1 { diff --git a/test/fts3snippet.test b/test/fts3snippet.test index 614691a8b1..e7adaee046 100644 --- a/test/fts3snippet.test +++ b/test/fts3snippet.test @@ -342,7 +342,7 @@ foreach {DO_MALLOC_TEST enc} { } execsql COMMIT } {} - do_snippet_test $T.7.2 {one two} -1 3 {*}$testresults + eval [list do_snippet_test $T.7.2 {one two} -1 3] $testresults ########################################################################## # Test the matchinfo function. diff --git a/test/func.test b/test/func.test index 91ae0a5925..eef0543986 100644 --- a/test/func.test +++ b/test/func.test @@ -235,6 +235,85 @@ ifcapable floatingpoint { do_test func-4.16 { catchsql {SELECT round(b,2.0) FROM t1 ORDER BY b} } {0 {-2.0 1.23 2.0}} + # Verify some values reported on the mailing list. + # Some of these fail on MSVC builds with 64-bit + # long doubles, but not on GCC builds with 80-bit + # long doubles. + for {set i 1} {$i<999} {incr i} { + set x1 [expr 40222.5 + $i] + set x2 [expr 40223.0 + $i] + do_test func-4.17.$i { + execsql {SELECT round($x1);} + } $x2 + } + for {set i 1} {$i<999} {incr i} { + set x1 [expr 40222.05 + $i] + set x2 [expr 40222.10 + $i] + do_test func-4.18.$i { + execsql {SELECT round($x1,1);} + } $x2 + } + do_test func-4.20 { + execsql {SELECT round(40223.4999999999);} + } {40223.0} + do_test func-4.21 { + execsql {SELECT round(40224.4999999999);} + } {40224.0} + do_test func-4.22 { + execsql {SELECT round(40225.4999999999);} + } {40225.0} + for {set i 1} {$i<10} {incr i} { + do_test func-4.23.$i { + execsql {SELECT round(40223.4999999999,$i);} + } {40223.5} + do_test func-4.24.$i { + execsql {SELECT round(40224.4999999999,$i);} + } {40224.5} + do_test func-4.25.$i { + execsql {SELECT round(40225.4999999999,$i);} + } {40225.5} + } + for {set i 10} {$i<32} {incr i} { + do_test func-4.26.$i { + execsql {SELECT round(40223.4999999999,$i);} + } {40223.4999999999} + do_test func-4.27.$i { + execsql {SELECT round(40224.4999999999,$i);} + } {40224.4999999999} + do_test func-4.28.$i { + execsql {SELECT round(40225.4999999999,$i);} + } {40225.4999999999} + } + do_test func-4.29 { + execsql {SELECT round(1234567890.5);} + } {1234567891.0} + do_test func-4.30 { + execsql {SELECT round(12345678901.5);} + } {12345678902.0} + do_test func-4.31 { + execsql {SELECT round(123456789012.5);} + } {123456789013.0} + do_test func-4.32 { + execsql {SELECT round(1234567890123.5);} + } {1234567890124.0} + do_test func-4.33 { + execsql {SELECT round(12345678901234.5);} + } {12345678901235.0} + do_test func-4.34 { + execsql {SELECT round(1234567890123.35,1);} + } {1234567890123.4} + do_test func-4.35 { + execsql {SELECT round(1234567890123.445,2);} + } {1234567890123.45} + do_test func-4.36 { + execsql {SELECT round(99999999999994.5);} + } {99999999999995.0} + do_test func-4.37 { + execsql {SELECT round(9999999999999.55,1);} + } {9999999999999.6} + do_test func-4.38 { + execsql {SELECT round(9999999999999.555,2);} + } {9999999999999.56} } # Test the upper() and lower() functions diff --git a/test/safety.test b/test/safety.test deleted file mode 100644 index 9cca57c1c4..0000000000 --- a/test/safety.test +++ /dev/null @@ -1,93 +0,0 @@ -# 2005 January 11 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this file is testing the sqlite3SafetyOn and sqlite3SafetyOff -# functions. Those routines are not strictly necessary - they are -# designed to detect misuse of the library. -# -# $Id: safety.test,v 1.4 2008/03/18 13:46:53 drh Exp $ - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -ifcapable !debug { - puts "Skipping safety tests since SQLITE_DEBUG is off" - finish_test - return -} - -# Return the UTF-8 representation of the supplied UTF-16 string $str. -proc utf8 {str} { - # If $str ends in two 0x00 0x00 bytes, knock these off before - # converting to UTF-8 using TCL. - binary scan $str \c* vals - if {[lindex $vals end]==0 && [lindex $vals end-1]==0} { - set str [binary format \c* [lrange $vals 0 end-2]] - } - - set r [encoding convertfrom unicode $str] - return $r -} - - -do_test safety-1.1 { - set DB [sqlite3_connection_pointer db] - db eval {CREATE TABLE t1(a)} - sqlite_set_magic $DB SQLITE_MAGIC_BUSY - catchsql { - SELECT name FROM sqlite_master; - } -} {1 {library routine called out of sequence}} -do_test safety-1.2 { - sqlite_set_magic $DB SQLITE_MAGIC_OPEN - catchsql { - SELECT name FROM sqlite_master - } -} {0 t1} - -do_test safety-2.1 { - proc safety_on {} "sqlite_set_magic $DB SQLITE_MAGIC_BUSY" - db function safety_on safety_on - catchsql { - SELECT safety_on(), name FROM sqlite_master - } -} {1 {library routine called out of sequence}} -ifcapable {utf16} { - do_test safety-2.1.1 { - utf8 [sqlite3_errmsg16 db] - } {library routine called out of sequence} -} -do_test safety-2.2 { - catchsql { - SELECT 'hello' - } -} {1 {library routine called out of sequence}} -do_test safety-2.3 { - sqlite3_close $DB -} {SQLITE_MISUSE} -do_test safety-2.4 { - sqlite_set_magic $DB SQLITE_MAGIC_OPEN - execsql { - SELECT name FROM sqlite_master - } -} {t1} - -do_test safety-3.1 { - set rc [catch { - db eval {SELECT name FROM sqlite_master} { - sqlite_set_magic $DB SQLITE_MAGIC_BUSY - } - } msg] - lappend rc $msg -} {1 {library routine called out of sequence}} -sqlite_set_magic $DB SQLITE_MAGIC_OPEN - -finish_test diff --git a/test/schema.test b/test/schema.test index 5e9f17573f..afca39ed60 100644 --- a/test/schema.test +++ b/test/schema.test @@ -376,15 +376,15 @@ do_test schema-13.1 { return SQLITE_OK } sqlite3_step $S -} {SQLITE_SCHEMA} +} {SQLITE_AUTH} do_test schema-13.2 { sqlite3_step $S -} {SQLITE_SCHEMA} +} {SQLITE_AUTH} do_test schema-13.3 { sqlite3_finalize $S -} {SQLITE_SCHEMA} +} {SQLITE_AUTH} } diff --git a/test/stmt.test b/test/stmt.test new file mode 100644 index 0000000000..45e9ac5371 --- /dev/null +++ b/test/stmt.test @@ -0,0 +1,85 @@ +# 2010 February 18 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# The tests in this file check that SQLite uses (or does not use) a +# statement journal for various SQL statements. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test stmt-1.1 { + execsql { CREATE TABLE t1(a integer primary key, b INTEGER NOT NULL) } +} {} + +# The following tests verify the method used for the tests in this file - +# that if a statement journal is required by a statement it is opened and +# remains open until the current transaction is committed or rolled back. +# +# This only work if SQLITE_TEMP_STORE!=3 +# +if {$::TEMP_STORE==3} { + finish_test + return +} +do_test stmt-1.2 { + set sqlite_open_file_count +} {1} +do_test stmt-1.3 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(1, 1); + } + set sqlite_open_file_count +} {2} +do_test stmt-1.4 { + execsql { + INSERT INTO t1 SELECT a+1, b+1 FROM t1; + } + set sqlite_open_file_count +} {3} +do_test stmt-1.5 { + execsql COMMIT + set sqlite_open_file_count +} {1} +do_test stmt-1.6 { + execsql { + BEGIN; + INSERT INTO t1 SELECT a+2, b+2 FROM t1; + } + set sqlite_open_file_count +} {3} +do_test stmt-1.7 { + execsql COMMIT + set sqlite_open_file_count +} {1} + + +proc filecount {testname sql expected} { + uplevel [list do_test $testname [subst -nocommand { + execsql BEGIN + execsql { $sql } + set ret [set sqlite_open_file_count] + execsql ROLLBACK + set ret + }] $expected] +} + +filecount stmt-2.1 { INSERT INTO t1 VALUES(5, 5) } 2 +filecount stmt-2.2 { REPLACE INTO t1 VALUES(5, 5) } 2 +filecount stmt-2.3 { INSERT INTO t1 SELECT 5, 5 } 3 + +do_test stmt-2.4 { + execsql { CREATE INDEX i1 ON t1(b) } +} {} +filecount stmt-2.5 { REPLACE INTO t1 VALUES(5, 5) } 3 + +finish_test diff --git a/test/tester.tcl b/test/tester.tcl index 136e116159..8fe877ec01 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -964,11 +964,18 @@ proc drop_all_tables {{db db}} { set pk [$db one "PRAGMA foreign_keys"] $db eval "PRAGMA foreign_keys = OFF" } - foreach {t type} [$db eval { - SELECT name, type FROM sqlite_master - WHERE type IN('table', 'view') AND name NOT like 'sqlite_%' - }] { - $db eval "DROP $type $t" + foreach {idx name file} [db eval {PRAGMA database_list}] { + if {$idx==1} { + set master sqlite_temp_master + } else { + set master $name.sqlite_master + } + foreach {t type} [$db eval " + SELECT name, type FROM $master + WHERE type IN('table', 'view') AND name NOT like 'sqlite_%' + "] { + $db eval "DROP $type $t" + } } ifcapable trigger&&foreignkey { $db eval "PRAGMA foreign_keys = $pk" diff --git a/test/triggerD.test b/test/triggerD.test index 050edc9724..08945081da 100644 --- a/test/triggerD.test +++ b/test/triggerD.test @@ -123,4 +123,52 @@ do_test triggerD-2.4 { } } {r5 1 1 1 201 r6 1 1 1 201} + +########################################################################### +# +# Ticket [985771e1161200ae5eac3162686ea6711c035d08]: +# +# When both a main database table and a TEMP table have the same name, +# and a main database trigge is created on the main table, the trigger +# is incorrectly bound to the TEMP table. For example: +# +# CREATE TABLE t1(x); +# CREATE TEMP TABLE t1(x); +# CREATE TABLE t2(z); +# CREATE TRIGGER main.r1 AFTER INSERT ON t1 BEGIN +# INSERT INTO t2 VALUES(10000 + new.x); +# END; +# INSERT INTO main.t1 VALUES(3); +# INSERT INTO temp.t1 VALUES(4); +# SELECT * FROM t2; +# +# The r1 trigger fires when the value 4 is inserted into the temp.t1 +# table, rather than when value 3 is inserted into main.t1. +# +do_test triggerD-3.1 { + db eval { + CREATE TABLE t300(x); + CREATE TEMP TABLE t300(x); + CREATE TABLE t301(y); + CREATE TRIGGER main.r300 AFTER INSERT ON t300 BEGIN + INSERT INTO t301 VALUES(10000 + new.x); + END; + INSERT INTO main.t300 VALUES(3); + INSERT INTO temp.t300 VALUES(4); + SELECT * FROM t301; + } +} {10003} +do_test triggerD-3.2 { + db eval { + DELETE FROM t301; + CREATE TRIGGER temp.r301 AFTER INSERT ON t300 BEGIN + INSERT INTO t301 VALUES(20000 + new.x); + END; + INSERT INTO main.t300 VALUES(3); + INSERT INTO temp.t300 VALUES(4); + SELECT * FROM t301; + } +} {10003 20004} + + finish_test diff --git a/test/vacuum4.test b/test/vacuum4.test new file mode 100644 index 0000000000..326d037276 --- /dev/null +++ b/test/vacuum4.test @@ -0,0 +1,67 @@ +# 2010 February 21 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements a test of ticket [da1151f97df244a1]: An +# assertion fault while VACUUMing an auto_vacuumed database with +# large schema. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# If the VACUUM statement is disabled in the current build, skip all +# the tests in this file. +# +ifcapable !vacuum { + finish_test + return +} + +do_test vacuum4-1.1 { + db eval { + PRAGMA auto_vacuum=FULL; + CREATE TABLE t1( + c000, c001, c002, c003, c004, c005, c006, c007, c008, c009, + c010, c011, c012, c013, c014, c015, c016, c017, c018, c019, + c020, c021, c022, c023, c024, c025, c026, c027, c028, c029, + c030, c031, c032, c033, c034, c035, c036, c037, c038, c039, + c040, c041, c042, c043, c044, c045, c046, c047, c048, c049, + c050, c051, c052, c053, c054, c055, c056, c057, c058, c059, + c060, c061, c062, c063, c064, c065, c066, c067, c068, c069, + c070, c071, c072, c073, c074, c075, c076, c077, c078, c079, + c080, c081, c082, c083, c084, c085, c086, c087, c088, c089, + c090, c091, c092, c093, c094, c095, c096, c097, c098, c099, + c100, c101, c102, c103, c104, c105, c106, c107, c108, c109, + c110, c111, c112, c113, c114, c115, c116, c117, c118, c119, + c120, c121, c122, c123, c124, c125, c126, c127, c128, c129, + c130, c131, c132, c133, c134, c135, c136, c137, c138, c139, + c140, c141, c142, c143, c144, c145, c146, c147, c148, c149 + ); + CREATE TABLE t2( + c000, c001, c002, c003, c004, c005, c006, c007, c008, c009, + c010, c011, c012, c013, c014, c015, c016, c017, c018, c019, + c020, c021, c022, c023, c024, c025, c026, c027, c028, c029, + c030, c031, c032, c033, c034, c035, c036, c037, c038, c039, + c040, c041, c042, c043, c044, c045, c046, c047, c048, c049, + c050, c051, c052, c053, c054, c055, c056, c057, c058, c059, + c060, c061, c062, c063, c064, c065, c066, c067, c068, c069, + c070, c071, c072, c073, c074, c075, c076, c077, c078, c079, + c080, c081, c082, c083, c084, c085, c086, c087, c088, c089, + c090, c091, c092, c093, c094, c095, c096, c097, c098, c099, + c100, c101, c102, c103, c104, c105, c106, c107, c108, c109, + c110, c111, c112, c113, c114, c115, c116, c117, c118, c119, + c120, c121, c122, c123, c124, c125, c126, c127, c128, c129, + c130, c131, c132, c133, c134, c135, c136, c137, c138, c139, + c140, c141, c142, c143, c144, c145, c146, c147, c148, c149 + ); + VACUUM; + } +} {} diff --git a/test/vtabA.test b/test/vtabA.test index 5eba5b5ffa..928cc2b703 100644 --- a/test/vtabA.test +++ b/test/vtabA.test @@ -131,4 +131,23 @@ do_test vtabA-2.4 { analyse_parse {(a whatelse can i hidden test, b HIDDEN hidden)} {a b} } {{} {whatelse can i test} hidden} + +# Ticket [d2f02d37f52bfe23e421f2c60fbb8586ac76ff01]: +# assertion failure on an UPDATE involving two virtual tables. +# +do_test vtabA-3.1 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,2); + CREATE TABLE t2(x,y); + INSERT INTO t2 VALUES(3,4); + CREATE VIRTUAL TABLE vt1 USING echo(t1); + CREATE VIRTUAL TABLE vt2 USING echo(t2); + UPDATE vt2 SET x=(SELECT a FROM vt1 WHERE b=2) WHERE y=4; + SELECT * FROM t2; + } +} {1 4} + finish_test diff --git a/test/where6.test b/test/where6.test index bcea6540a5..83e975b7dd 100644 --- a/test/where6.test +++ b/test/where6.test @@ -127,4 +127,28 @@ do_test where6-2.14 { } } {1 3 1 3} +# Ticket [ebdbadade5b]: +# If the ON close on a LEFT JOIN is of the form x=y where both x and y +# are indexed columns on tables to left of the join, then do not use that +# term with indices to either table. +# +do_test where6-3.1 { + db eval { + CREATE TABLE t4(x UNIQUE); + INSERT INTO t4 VALUES('abc'); + INSERT INTO t4 VALUES('def'); + INSERT INTO t4 VALUES('ghi'); + CREATE TABLE t5(a, b, c, PRIMARY KEY(a,b)); + INSERT INTO t5 VALUES('abc','def',123); + INSERT INTO t5 VALUES('def','ghi',456); + + SELECT t4a.x, t4b.x, t5.c, t6.v + FROM t4 AS t4a + INNER JOIN t4 AS t4b + LEFT JOIN t5 ON t5.a=t4a.x AND t5.b=t4b.x + LEFT JOIN (SELECT 1 AS v) AS t6 ON t4a.x=t4b.x + ORDER BY 1, 2, 3; + } +} {abc abc {} 1 abc def 123 {} abc ghi {} {} def abc {} {} def def {} 1 def ghi 456 {} ghi abc {} {} ghi def {} {} ghi ghi {} 1} + finish_test diff --git a/tool/lemon.c b/tool/lemon.c index f05e9e6ce1..8946f14552 100644 --- a/tool/lemon.c +++ b/tool/lemon.c @@ -2285,11 +2285,21 @@ to follow the previous rule."); psp->errorcnt++; psp->state = RESYNC_AFTER_DECL_ERROR; }else{ - struct symbol *sp = Symbol_new(x); - psp->declargslot = &sp->destructor; - psp->decllinenoslot = &sp->destLineno; - psp->insertLineMacro = 1; - psp->state = WAITING_FOR_DECL_ARG; + struct symbol *sp = Symbol_find(x); + if((sp) && (sp->datatype)){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol %%destructor \"%s\" already defined", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + if (!sp){ + sp = Symbol_new(x); + } + psp->declargslot = &sp->destructor; + psp->decllinenoslot = &sp->destLineno; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + } } break; case WAITING_FOR_DATATYPE_SYMBOL: diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index c14df4e8af..bda08bc4ab 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -266,6 +266,7 @@ foreach file { auth.c build.c callback.c + ctime.c delete.c func.c fkey.c