Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -942,11 +942,11 @@ rm -f tclsqlite3$(TEXE) rm -f testfixture$(TEXE) test.db rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def rm -f sqlite3.c rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c - rm -f sqlite-output.vsix + rm -f sqlite-*-output.vsix distclean: clean rm -f config.log config.status libtool Makefile sqlite3.pc # Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -1261,11 +1261,11 @@ del /Q testfixture.exe testfixture.exp test.db del /Q sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def del /Q sqlite3.c del /Q sqlite3rc.h del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp sqlite3_analyzer.c - del /Q sqlite-output.vsix + del /Q sqlite-*-output.vsix # Dynamic link library section. # dll: sqlite3.dll Index: ext/fts2/fts2_icu.c ================================================================== --- ext/fts2/fts2_icu.c +++ ext/fts2/fts2_icu.c @@ -116,19 +116,19 @@ nInput = strlen(zInput); } nChar = nInput+1; pCsr = (IcuCursor *)sqlite3_malloc( sizeof(IcuCursor) + /* IcuCursor */ - nChar * sizeof(UChar) + /* IcuCursor.aChar[] */ + ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */ (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */ ); if( !pCsr ){ return SQLITE_NOMEM; } memset(pCsr, 0, sizeof(IcuCursor)); pCsr->aChar = (UChar *)&pCsr[1]; - pCsr->aOffset = (int *)&pCsr->aChar[nChar]; + pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3]; pCsr->aOffset[iOut] = iInput; U8_NEXT(zInput, iInput, nInput, c); while( c>0 ){ int isError = 0; Index: ext/fts3/fts3.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -4741,39 +4741,43 @@ /* Allocate temporary working space. */ for(p=pExpr; p->pLeft; p=p->pLeft){ nTmp += p->pRight->pPhrase->doclist.nList; } nTmp += p->pPhrase->doclist.nList; - aTmp = sqlite3_malloc(nTmp*2); - if( !aTmp ){ - *pRc = SQLITE_NOMEM; + if( nTmp==0 ){ res = 0; }else{ - char *aPoslist = p->pPhrase->doclist.pList; - int nToken = p->pPhrase->nToken; - - for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){ - Fts3Phrase *pPhrase = p->pRight->pPhrase; - int nNear = p->nNear; - res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); - } - - aPoslist = pExpr->pRight->pPhrase->doclist.pList; - nToken = pExpr->pRight->pPhrase->nToken; - for(p=pExpr->pLeft; p && res; p=p->pLeft){ - int nNear; - Fts3Phrase *pPhrase; - assert( p->pParent && p->pParent->pLeft==p ); - nNear = p->pParent->nNear; - pPhrase = ( - p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase - ); - res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); - } - } - - sqlite3_free(aTmp); + aTmp = sqlite3_malloc(nTmp*2); + if( !aTmp ){ + *pRc = SQLITE_NOMEM; + res = 0; + }else{ + char *aPoslist = p->pPhrase->doclist.pList; + int nToken = p->pPhrase->nToken; + + for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){ + Fts3Phrase *pPhrase = p->pRight->pPhrase; + int nNear = p->nNear; + res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); + } + + aPoslist = pExpr->pRight->pPhrase->doclist.pList; + nToken = pExpr->pRight->pPhrase->nToken; + for(p=pExpr->pLeft; p && res; p=p->pLeft){ + int nNear; + Fts3Phrase *pPhrase; + assert( p->pParent && p->pParent->pLeft==p ); + nNear = p->pParent->nNear; + pPhrase = ( + p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase + ); + res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); + } + } + + sqlite3_free(aTmp); + } } return res; } Index: ext/fts3/fts3_expr.c ================================================================== --- ext/fts3/fts3_expr.c +++ ext/fts3/fts3_expr.c @@ -183,11 +183,11 @@ int nConsumed = 0; rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); if( rc==SQLITE_OK ){ const char *zToken; - int nToken, iStart, iEnd, iPosition; + int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; int nByte; /* total space to allocate */ rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; @@ -298,11 +298,11 @@ pTokenizer, pParse->iLangid, zInput, nInput, &pCursor); if( rc==SQLITE_OK ){ int ii; for(ii=0; rc==SQLITE_OK; ii++){ const char *zByte; - int nByte, iBegin, iEnd, iPos; + int nByte = 0, iBegin = 0, iEnd = 0, iPos = 0; rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); if( rc==SQLITE_OK ){ Fts3PhraseToken *pToken; p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); Index: ext/fts3/fts3_icu.c ================================================================== --- ext/fts3/fts3_icu.c +++ ext/fts3/fts3_icu.c @@ -117,19 +117,19 @@ nInput = strlen(zInput); } nChar = nInput+1; pCsr = (IcuCursor *)sqlite3_malloc( sizeof(IcuCursor) + /* IcuCursor */ - nChar * sizeof(UChar) + /* IcuCursor.aChar[] */ + ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */ (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */ ); if( !pCsr ){ return SQLITE_NOMEM; } memset(pCsr, 0, sizeof(IcuCursor)); pCsr->aChar = (UChar *)&pCsr[1]; - pCsr->aOffset = (int *)&pCsr->aChar[nChar]; + pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3]; pCsr->aOffset[iOut] = iInput; U8_NEXT(zInput, iInput, nInput, c); while( c>0 ){ int isError = 0; Index: ext/fts3/fts3_snippet.c ================================================================== --- ext/fts3/fts3_snippet.c +++ ext/fts3/fts3_snippet.c @@ -574,11 +574,11 @@ rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, iLangid, zDoc, nDoc, &pC); if( rc!=SQLITE_OK ){ return rc; } while( rc==SQLITE_OK && iCurrent<(nSnippet+nDesired) ){ - const char *ZDUMMY; int DUMMY1, DUMMY2, DUMMY3; + const char *ZDUMMY; int DUMMY1 = 0, DUMMY2 = 0, DUMMY3 = 0; rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &DUMMY2, &DUMMY3, &iCurrent); } pMod->xClose(pC); if( rc!=SQLITE_OK && rc!=SQLITE_DONE ){ return rc; } @@ -618,12 +618,10 @@ int iPos = pFragment->iPos; /* First token of snippet */ u64 hlmask = pFragment->hlmask; /* Highlight-mask for snippet */ int iCol = pFragment->iCol+1; /* Query column to extract text from */ sqlite3_tokenizer_module *pMod; /* Tokenizer module methods object */ sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor open on zDoc/nDoc */ - const char *ZDUMMY; /* Dummy argument used with tokenizer */ - int DUMMY1; /* Dummy argument used with tokenizer */ zDoc = (const char *)sqlite3_column_text(pCsr->pStmt, iCol); if( zDoc==0 ){ if( sqlite3_column_type(pCsr->pStmt, iCol)!=SQLITE_NULL ){ return SQLITE_NOMEM; @@ -638,14 +636,27 @@ if( rc!=SQLITE_OK ){ return rc; } while( rc==SQLITE_OK ){ - int iBegin; /* Offset in zDoc of start of token */ - int iFin; /* Offset in zDoc of end of token */ - int isHighlight; /* True for highlighted terms */ + const char *ZDUMMY; /* Dummy argument used with tokenizer */ + int DUMMY1 = -1; /* Dummy argument used with tokenizer */ + int iBegin = 0; /* Offset in zDoc of start of token */ + int iFin = 0; /* Offset in zDoc of end of token */ + int isHighlight = 0; /* True for highlighted terms */ + /* Variable DUMMY1 is initialized to a negative value above. Elsewhere + ** in the FTS code the variable that the third argument to xNext points to + ** is initialized to zero before the first (*but not necessarily + ** subsequent*) call to xNext(). This is done for a particular application + ** that needs to know whether or not the tokenizer is being used for + ** snippet generation or for some other purpose. + ** + ** Extreme care is required when writing code to depend on this + ** initialization. It is not a documented part of the tokenizer interface. + ** If a tokenizer is used directly by any code outside of FTS, this + ** convention might not be respected. */ rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &iBegin, &iFin, &iCurrent); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ /* Special case - the last token of the snippet is also the last token ** of the column. Append any punctuation that occurred between the end @@ -1331,12 +1342,10 @@ sqlite3_context *pCtx, /* SQLite function call context */ Fts3Cursor *pCsr /* Cursor object */ ){ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; sqlite3_tokenizer_module const *pMod = pTab->pTokenizer->pModule; - const char *ZDUMMY; /* Dummy argument used with xNext() */ - int NDUMMY; /* Dummy argument used with xNext() */ int rc; /* Return Code */ int nToken; /* Number of tokens in query */ int iCol; /* Column currently being processed */ StrBuffer res = {0, 0, 0}; /* Result string */ TermOffsetCtx sCtx; /* Context for fts3ExprTermOffsetInit() */ @@ -1365,13 +1374,15 @@ /* Loop through the table columns, appending offset information to ** string-buffer res for each column. */ for(iCol=0; iColnColumn; iCol++){ sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor */ - int iStart; - int iEnd; - int iCurrent; + const char *ZDUMMY; /* Dummy argument used with xNext() */ + int NDUMMY = 0; /* Dummy argument used with xNext() */ + int iStart = 0; + int iEnd = 0; + int iCurrent = 0; const char *zDoc; int nDoc; /* Initialize the contents of sCtx.aTerm[] for column iCol. There is ** no way that this operation can fail, so the return code from Index: ext/fts3/fts3_tokenizer.c ================================================================== --- ext/fts3/fts3_tokenizer.c +++ ext/fts3/fts3_tokenizer.c @@ -249,14 +249,14 @@ int nInput; const char *azArg[64]; const char *zToken; - int nToken; - int iStart; - int iEnd; - int iPos; + int nToken = 0; + int iStart = 0; + int iEnd = 0; + int iPos = 0; int i; Tcl_Obj *pRet; if( argc<2 ){ Index: ext/fts3/fts3_write.c ================================================================== --- ext/fts3/fts3_write.c +++ ext/fts3/fts3_write.c @@ -777,17 +777,17 @@ const char *zText, /* Text of document to be inserted */ int iCol, /* Column into which text is being inserted */ u32 *pnWord /* OUT: Number of tokens inserted */ ){ int rc; - int iStart; - int iEnd; - int iPos; + int iStart = 0; + int iEnd = 0; + int iPos = 0; int nWord = 0; char const *zToken; - int nToken; + int nToken = 0; sqlite3_tokenizer *pTokenizer = p->pTokenizer; sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; sqlite3_tokenizer_cursor *pCsr; int (*xNext)(sqlite3_tokenizer_cursor *pCursor, @@ -4932,13 +4932,13 @@ sqlite3_tokenizer_cursor *pT = 0; rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText, &pT); while( rc==SQLITE_OK ){ char const *zToken; /* Buffer containing token */ - int nToken; /* Number of bytes in token */ - int iDum1, iDum2; /* Dummy variables */ - int iPos; /* Position of token in zText */ + int nToken = 0; /* Number of bytes in token */ + int iDum1 = 0, iDum2 = 0; /* Dummy variables */ + int iPos = 0; /* Position of token in zText */ rc = pModule->xNext(pT, &zToken, &nToken, &iDum1, &iDum2, &iPos); if( rc==SQLITE_OK ){ int i; cksum2 = cksum2 ^ fts3ChecksumEntry( @@ -5101,13 +5101,13 @@ sqlite3_tokenizer_cursor *pTC = 0; rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC); while( rc==SQLITE_OK ){ char const *zToken; /* Buffer containing token */ - int nToken; /* Number of bytes in token */ - int iDum1, iDum2; /* Dummy variables */ - int iPos; /* Position of token in zText */ + int nToken = 0; /* Number of bytes in token */ + int iDum1 = 0, iDum2 = 0; /* Dummy variables */ + int iPos = 0; /* Position of token in zText */ rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos); for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){ Fts3PhraseToken *pPT = pDef->pToken; if( (pDef->iCol>=p->nColumn || pDef->iCol==i) Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -547,11 +547,11 @@ TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ - -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) libsqlite3.a + -o testfixture$(EXE) $(LIBTCL) libsqlite3.a $(THREADLIB) amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) @@ -620,6 +620,6 @@ rm -f fts3-testfixture fts3-testfixture.exe rm -f testfixture testfixture.exe rm -f threadtest3 threadtest3.exe rm -f sqlite3.c fts?amal.c tclsqlite3.c rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c - rm -f sqlite-output.vsix + rm -f sqlite-*-output.vsix Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -5742,11 +5742,11 @@ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); /* This error condition is now caught prior to reaching this function */ - if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT; + if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell ** may be inserted. If both these operations are successful, proceed. */ Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -110,10 +110,11 @@ assert( pFrom->a[0].pUsing==0 ); }else{ sqlite3SelectDelete(db, pDup); } pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0); + if( pDup ) pDup->selFlags |= SF_Materialize; } sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur); sqlite3Select(pParse, pDup, &dest); sqlite3SelectDelete(db, pDup); } Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -937,10 +937,11 @@ pNewItem->jointype = pOldItem->jointype; pNewItem->iCursor = pOldItem->iCursor; pNewItem->addrFillSub = pOldItem->addrFillSub; pNewItem->regReturn = pOldItem->regReturn; pNewItem->isCorrelated = pOldItem->isCorrelated; + pNewItem->viaCoroutine = pOldItem->viaCoroutine; pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex); pNewItem->notIndexed = pOldItem->notIndexed; pNewItem->pIndex = pOldItem->pIndex; pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ Index: src/hash.c ================================================================== --- src/hash.c +++ src/hash.c @@ -191,11 +191,11 @@ pEntry->count--; assert( pEntry->count>=0 ); } sqlite3_free( elem ); pH->count--; - if( pH->count<=0 ){ + if( pH->count==0 ){ assert( pH->first==0 ); assert( pH->count==0 ); sqlite3HashClear(pH); } } Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -3038,11 +3038,11 @@ ** with various optimizations disabled to verify that the same answer ** is obtained in every case. */ case SQLITE_TESTCTRL_OPTIMIZATIONS: { sqlite3 *db = va_arg(ap, sqlite3*); - db->dbOptFlags = (u8)(va_arg(ap, int) & 0xff); + db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff); break; } #ifdef SQLITE_N_KEYWORD /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord) Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -216,10 +216,14 @@ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ +#ifdef __QNXNTO__ + int sectorSize; /* Device sector size */ + int deviceCharacteristics; /* Precomputed device characteristics */ +#endif #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ @@ -3636,14 +3640,96 @@ ** SQLite code assumes this function cannot fail. It also assumes that ** if two files are created in the same file-system directory (i.e. ** a database and its journal file) that the sector size will be the ** same for both. */ -static int unixSectorSize(sqlite3_file *pFile){ - (void)pFile; +#ifndef __QNXNTO__ +static int unixSectorSize(sqlite3_file *NotUsed){ + UNUSED_PARAMETER(NotUsed); return SQLITE_DEFAULT_SECTOR_SIZE; } +#endif + +/* +** The following version of unixSectorSize() is optimized for QNX. +*/ +#ifdef __QNXNTO__ +#include +#include +static int unixSectorSize(sqlite3_file *id){ + unixFile *pFile = (unixFile*)id; + if( pFile->sectorSize == 0 ){ + struct statvfs fsInfo; + + /* Set defaults for non-supported filesystems */ + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; + pFile->deviceCharacteristics = 0; + if( fstatvfs(pFile->h, &fsInfo) == -1 ) { + return pFile->sectorSize; + } + + if( !strcmp(fsInfo.f_basetype, "tmp") ) { + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */ + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( strstr(fsInfo.f_basetype, "etfs") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* etfs cluster size writes are atomic */ + (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) | + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */ + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* full bitset of atomics from max sector size and smaller */ + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( strstr(fsInfo.f_basetype, "dos") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* full bitset of atomics from max sector size and smaller */ + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else{ + pFile->deviceCharacteristics = + SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */ + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + 0; + } + } + /* Last chance verification. If the sector size isn't a multiple of 512 + ** then it isn't valid.*/ + if( pFile->sectorSize % 512 != 0 ){ + pFile->deviceCharacteristics = 0; + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; + } + return pFile->sectorSize; +} +#endif /* __QNXNTO__ */ /* ** Return the device characteristics for the file. ** ** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. @@ -3656,15 +3742,19 @@ ** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control ** available to turn it off and URI query parameter available to turn it off. */ static int unixDeviceCharacteristics(sqlite3_file *id){ unixFile *p = (unixFile*)id; + int rc = 0; +#ifdef __QNXNTO__ + if( p->sectorSize==0 ) unixSectorSize(id); + rc = p->deviceCharacteristics; +#endif if( p->ctrlFlags & UNIXFILE_PSOW ){ - return SQLITE_IOCAP_POWERSAFE_OVERWRITE; - }else{ - return 0; + rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; } + return rc; } #ifndef SQLITE_OMIT_WAL Index: src/os_win.c ================================================================== --- src/os_win.c +++ src/os_win.c @@ -2190,11 +2190,12 @@ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ #endif if( retryIoerr(&nRetry, &lastErrno) ) continue; break; } - if( nWrite<=0 ){ + assert( nWrite==0 || nWrite<=(DWORD)nRem ); + if( nWrite==0 || nWrite>(DWORD)nRem ){ lastErrno = osGetLastError(); break; } #if !SQLITE_OS_WINCE offset += nWrite; Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -5883,19 +5883,18 @@ #endif if( rc!=SQLITE_OK ) goto commit_phase_one_exit; /* If this transaction has made the database smaller, then all pages ** being discarded by the truncation must be written to the journal - ** file. This can only happen in auto-vacuum mode. + ** file. ** ** Before reading the pages with page numbers larger than the ** current value of Pager.dbSize, set dbSize back to the value ** that it took at the start of the transaction. Otherwise, the ** calls to sqlite3PagerGet() return zeroed pages instead of ** reading data from the database file. */ - #ifndef SQLITE_OMIT_AUTOVACUUM if( pPager->dbSizedbOrigSize && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ Pgno i; /* Iterator variable */ const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ @@ -5911,11 +5910,10 @@ if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } } pPager->dbSize = dbSize; } - #endif /* Write the master journal name into the journal file. If a master ** journal file name has already been written to the journal file, ** or if zMaster is NULL (no master journal), then this call is a no-op. */ Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -3096,14 +3096,13 @@ ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". */ pList = pParent->pEList; for(i=0; inExpr; i++){ if( pList->a[i].zName==0 ){ - const char *zSpan = pList->a[i].zSpan; - if( ALWAYS(zSpan) ){ - pList->a[i].zName = sqlite3DbStrDup(db, zSpan); - } + char *zName = sqlite3DbStrDup(db, pList->a[i].zSpan); + sqlite3Dequote(zName); + pList->a[i].zName = zName; } } substExprList(db, pParent->pEList, iParent, pSub->pEList); if( isAgg ){ substExprList(db, pParent->pGroupBy, iParent, pSub->pEList); @@ -3908,12 +3907,21 @@ SelectDest dest; Select *pSub = pItem->pSelect; int isAggSub; if( pSub==0 ) continue; + + /* Sometimes the code for a subquery will be generated more than + ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, + ** for example. In that case, do not regenerate the code to manifest + ** a view or the co-routine to implement a view. The first instance + ** is sufficient, though the subroutine to manifest the view does need + ** to be invoked again. */ if( pItem->addrFillSub ){ - sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); + if( pItem->viaCoroutine==0 ){ + sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); + } continue; } /* Increment Parse.nHeight by the height of the largest expression ** tree refered to by this, the parent select. The child select @@ -3930,10 +3938,39 @@ if( isAggSub ){ isAgg = 1; p->selFlags |= SF_Aggregate; } i = -1; + }else if( pTabList->nSrc==1 && (p->selFlags & SF_Materialize)==0 + && OptimizationEnabled(db, SQLITE_SubqCoroutine) + ){ + /* Implement a co-routine that will return a single row of the result + ** set on each invocation. + */ + int addrTop; + int addrEof; + pItem->regReturn = ++pParse->nMem; + addrEof = ++pParse->nMem; + sqlite3VdbeAddOp0(v, OP_Goto); + addrTop = sqlite3VdbeAddOp1(v, OP_OpenPseudo, pItem->iCursor); + sqlite3VdbeChangeP5(v, 1); + VdbeComment((v, "coroutine for %s", pItem->pTab->zName)); + pItem->addrFillSub = addrTop; + sqlite3VdbeAddOp2(v, OP_Integer, 0, addrEof); + sqlite3VdbeChangeP5(v, 1); + sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); + explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); + sqlite3Select(pParse, pSub, &dest); + pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; + pItem->viaCoroutine = 1; + sqlite3VdbeChangeP2(v, addrTop, dest.iSdst); + sqlite3VdbeChangeP3(v, addrTop, dest.nSdst); + sqlite3VdbeAddOp2(v, OP_Integer, 1, addrEof); + sqlite3VdbeAddOp1(v, OP_Yield, pItem->regReturn); + VdbeComment((v, "end %s", pItem->pTab->zName)); + sqlite3VdbeJumpHere(v, addrTop-1); + sqlite3ClearTempRegCache(pParse); }else{ /* Generate a subroutine that will fill an ephemeral table with ** the content of this subquery. pItem->addrFillSub will point ** to the address of the generated subroutine. pItem->regReturn ** is a register allocated to hold the subroutine return address Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -2822,25 +2822,28 @@ */ static const char zOptions[] = " -bail stop after hitting an error\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" - " -cmd command run \"command\" before reading stdin\n" + " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" " -echo print commands before execution\n" - " -init filename read/process named file\n" + " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" +#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) + " -heap SIZE Size of heap for memsys3 or memsys5\n" +#endif " -help show this message\n" " -html set output mode to HTML\n" " -interactive force interactive I/O\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif - " -nullvalue 'text' set text string for NULL values\n" - " -separator 'x' set output field separator (|)\n" + " -nullvalue TEXT set text string for NULL values. Default ''\n" + " -separator SEP set output field separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" @@ -2871,10 +2874,23 @@ sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); } + +/* +** Get the argument to an --option. Throw an error and die if no argument +** is available. +*/ +static char *cmdline_option_value(int argc, char **argv, int i){ + if( i==argc ){ + fprintf(stderr, "%s: Error: missing argument to %s\n", + argv[0], argv[argc-1]); + exit(1); + } + return argv[i]; +} int main(int argc, char **argv){ char *zErrMsg = 0; struct callback_data data; const char *zInitFile = 0; @@ -2901,36 +2917,47 @@ /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** the size of the alternative malloc heap, ** and the first command to execute. */ - for(i=1; i=argc){ - fprintf(stderr,"%s: Error: missing argument for option: %s\n", - Argv0, z); - fprintf(stderr,"Use -help for a list of options.\n"); - return 1; - } sqlite3_snprintf(sizeof(data.separator), data.separator, - "%.*s",(int)sizeof(data.separator)-1,argv[i]); + "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ - i++; - if(i>=argc){ - fprintf(stderr,"%s: Error: missing argument for option: %s\n", - Argv0, z); - fprintf(stderr,"Use -help for a list of options.\n"); - return 1; - } sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, - "%.*s",(int)sizeof(data.nullvalue)-1,argv[i]); + "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ @@ -3080,12 +3078,11 @@ #endif }else if( strcmp(z,"-help")==0 ){ usage(1); }else if( strcmp(z,"-cmd")==0 ){ if( i==argc-1 ) break; - i++; - z = argv[i]; + z = cmdline_option_value(argc,argv,++i); if( z[0]=='.' ){ rc = do_meta_command(z, &data); if( rc && bail_on_error ) return rc; }else{ open_db(&data); Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -826,11 +826,11 @@ int flags; /* Miscellaneous flags. See below */ i64 lastRowid; /* ROWID of most recent insert (see above) */ unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ - u8 dbOptFlags; /* Flags to enable/disable optimizations */ + u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ @@ -978,11 +978,12 @@ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ -#define SQLITE_AllOpts 0x00ff /* All optimizations */ +#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ +#define SQLITE_AllOpts 0xffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. */ #ifndef SQLITE_OMIT_BUILTIN_TEST @@ -1884,12 +1885,13 @@ Table *pTab; /* An SQL table corresponding to zName */ Select *pSelect; /* A SELECT statement used in place of a table name */ int addrFillSub; /* Address of subroutine to manifest a subquery */ int regReturn; /* Register holding return address of addrFillSub */ u8 jointype; /* Type of join between this able and the previous */ - u8 notIndexed; /* True if there is a NOT INDEXED clause */ - u8 isCorrelated; /* True if sub-query is correlated */ + unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ + unsigned isCorrelated :1; /* True if sub-query is correlated */ + unsigned viaCoroutine :1; /* Implemented as a co-routine */ #ifndef SQLITE_OMIT_EXPLAIN u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ #endif int iCursor; /* The VDBE cursor number used to access this table */ Expr *pOn; /* The ON clause of a join */ @@ -2109,18 +2111,19 @@ /* ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". */ -#define SF_Distinct 0x01 /* Output should be DISTINCT */ -#define SF_Resolved 0x02 /* Identifiers have been resolved */ -#define SF_Aggregate 0x04 /* Contains aggregate functions */ -#define SF_UsesEphemeral 0x08 /* Uses the OpenEphemeral opcode */ -#define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */ -#define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */ -#define SF_UseSorter 0x40 /* Sort using a sorter */ -#define SF_Values 0x80 /* Synthesized from VALUES clause */ +#define SF_Distinct 0x0001 /* Output should be DISTINCT */ +#define SF_Resolved 0x0002 /* Identifiers have been resolved */ +#define SF_Aggregate 0x0004 /* Contains aggregate functions */ +#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ +#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ +#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ +#define SF_UseSorter 0x0040 /* Sort using a sorter */ +#define SF_Values 0x0080 /* Synthesized from VALUES clause */ +#define SF_Materialize 0x0100 /* Force materialization of views */ /* ** The results of a select can be distributed in several ways. The ** "SRT" prefix means "SELECT Result Type". Index: src/status.c ================================================================== --- src/status.c +++ src/status.c @@ -206,11 +206,11 @@ struct Vdbe *pVdbe; /* Used to iterate through VMs */ int nByte = 0; /* Used to accumulate return value */ db->pnBytesFreed = &nByte; for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ - sqlite3VdbeDeleteObject(db, pVdbe); + sqlite3VdbeClearObject(db, pVdbe); } db->pnBytesFreed = 0; *pHighwater = 0; *pCurrent = nByte; Index: src/test6.c ================================================================== --- src/test6.c +++ src/test6.c @@ -310,12 +310,12 @@ int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize); assert(pWrite->zBuf); #ifdef TRACE_CRASHTEST - printf("Trashing %d sectors @ sector %d (%s)\n", - 1+iLast-iFirst, iFirst, pWrite->pFile->zName + printf("Trashing %d sectors @ %lld (sector %d) (%s)\n", + 1+iLast-iFirst, pWrite->iOffset, iFirst, pWrite->pFile->zName ); #endif zGarbage = crash_malloc(g.iSectorSize); if( zGarbage ){ @@ -626,22 +626,23 @@ if( pWrapper->zData ){ /* os_unix.c contains an assert() that fails if the caller attempts ** to read data from the 512-byte locking region of a file opened ** with the SQLITE_OPEN_MAIN_DB flag. This region of a database file ** never contains valid data anyhow. So avoid doing such a read here. + ** + ** UPDATE: It also contains an assert() verifying that each call + ** to the xRead() method reads less than 128KB of data. */ const int isDb = (flags&SQLITE_OPEN_MAIN_DB); - i64 iChunk = pWrapper->iSize; - if( iChunk>PENDING_BYTE && isDb ){ - iChunk = PENDING_BYTE; - } + i64 iOff; + memset(pWrapper->zData, 0, pWrapper->nData); - rc = sqlite3OsRead(pReal, pWrapper->zData, (int)iChunk, 0); - if( SQLITE_OK==rc && pWrapper->iSize>(PENDING_BYTE+512) && isDb ){ - i64 iOff = PENDING_BYTE+512; - iChunk = pWrapper->iSize - iOff; - rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], (int)iChunk, iOff); + for(iOff=0; iOffiSize; iOff += 512){ + int nRead = pWrapper->iSize - (int)iOff; + if( nRead>512 ) nRead = 512; + if( isDb && iOff==PENDING_BYTE ) continue; + rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff); } }else{ rc = SQLITE_NOMEM; } } Index: src/test_intarray.h ================================================================== --- src/test_intarray.h +++ src/test_intarray.h @@ -74,10 +74,17 @@ ** closes so the application does not normally need to take any special ** action to free the intarray objects. */ #include "sqlite3.h" +/* +** Make sure we can call this stuff from C++. +*/ +#ifdef __cplusplus +extern "C" { +#endif + /* ** An sqlite3_intarray is an abstract type to stores an instance of ** an integer array. */ typedef struct sqlite3_intarray sqlite3_intarray; @@ -110,5 +117,9 @@ sqlite3_intarray *pIntArray, /* The intarray object to bind to */ int nElements, /* Number of elements in the intarray */ sqlite3_int64 *aElements, /* Content of the intarray */ void (*xFree)(void*) /* How to dispose of the intarray when done */ ); + +#ifdef __cplusplus +} /* End of the 'extern "C"' block */ +#endif Index: src/test_multiplex.h ================================================================== --- src/test_multiplex.h +++ src/test_multiplex.h @@ -44,10 +44,14 @@ */ #define MULTIPLEX_CTRL_ENABLE 214014 #define MULTIPLEX_CTRL_SET_CHUNK_SIZE 214015 #define MULTIPLEX_CTRL_SET_MAX_CHUNKS 214016 +#ifdef __cplusplus +extern "C" { +#endif + /* ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() ** ** Use the VFS named zOrigVfsName as the VFS that does the actual work. ** Use the default if zOrigVfsName==NULL. @@ -85,7 +89,11 @@ ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while ** shutting down in order to free all remaining multiplex groups. */ extern int sqlite3_multiplex_shutdown(void); + +#ifdef __cplusplus +} /* End of the 'extern "C"' block */ +#endif #endif Index: src/vacuum.c ================================================================== --- src/vacuum.c +++ src/vacuum.c @@ -83,10 +83,11 @@ */ void sqlite3Vacuum(Parse *pParse){ Vdbe *v = sqlite3GetVdbe(pParse); if( v ){ sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0); + sqlite3VdbeUsesBtree(v, 0); } return; } /* Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2245,10 +2245,15 @@ VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &payloadSize); assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } }else if( ALWAYS(pC->pseudoTableReg>0) ){ pReg = &aMem[pC->pseudoTableReg]; + if( pC->multiPseudo ){ + sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); + Deephemeralize(pDest); + goto op_column_out; + } assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); payloadSize = pReg->n; zRec = pReg->z; pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; @@ -3326,16 +3331,17 @@ pc--; #endif break; } -/* Opcode: OpenPseudo P1 P2 P3 * * +/* Opcode: OpenPseudo P1 P2 P3 * P5 ** ** Open a new cursor that points to a fake table that contains a single ** row of data. The content of that one row in the content of memory -** register P2. In other words, cursor P1 becomes an alias for the -** MEM_Blob content contained in register P2. +** register P2 when P5==0. In other words, cursor P1 becomes an alias for the +** MEM_Blob content contained in register P2. When P5==1, then the +** row is represented by P3 consecutive registers beginning with P2. ** ** 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. @@ -3351,10 +3357,11 @@ if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->pseudoTableReg = pOp->p2; pCx->isTable = 1; pCx->isIndex = 0; + pCx->multiPseudo = pOp->p5; break; } /* Opcode: Close P1 * * * * ** @@ -4357,11 +4364,11 @@ const sqlite3_module *pModule; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pseudoTableReg==0 ); + assert( pC->pseudoTableReg==0 || pC->nullRow ); if( pC->nullRow ){ pOut->flags = MEM_Null; break; }else if( pC->deferredMoveto ){ v = pC->movetoTarget; Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -188,11 +188,11 @@ void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); void sqlite3VdbeRunOnlyOnce(Vdbe*); void sqlite3VdbeDelete(Vdbe*); -void sqlite3VdbeDeleteObject(sqlite3*,Vdbe*); +void sqlite3VdbeClearObject(sqlite3*,Vdbe*); void sqlite3VdbeMakeReady(Vdbe*,Parse*); int sqlite3VdbeFinalize(Vdbe*); void sqlite3VdbeResolveLabel(Vdbe*, int); int sqlite3VdbeCurrentAddr(Vdbe*); #ifdef SQLITE_DEBUG Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -61,10 +61,11 @@ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ Bool isSorter; /* True if a new-style sorter */ + Bool multiPseudo; /* Multi-register pseudo-cursor */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite3_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -2433,16 +2433,18 @@ } } } /* -** Free all memory associated with the Vdbe passed as the second argument. +** Free all memory associated with the Vdbe passed as the second argument, +** except for object itself, which is preserved. +** ** The difference between this function and sqlite3VdbeDelete() is that ** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with -** the database connection. +** the database connection and frees the object itself. */ -void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){ +void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ SubProgram *pSub, *pNext; int i; assert( p->db==0 || p->db==db ); releaseMemArray(p->aVar, p->nVar); releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); @@ -2459,11 +2461,10 @@ sqlite3DbFree(db, p->pFree); #if defined(SQLITE_ENABLE_TREE_EXPLAIN) sqlite3DbFree(db, p->zExplain); sqlite3DbFree(db, p->pExplain); #endif - sqlite3DbFree(db, p); } /* ** Delete an entire VDBE. */ @@ -2471,10 +2472,11 @@ sqlite3 *db; if( NEVER(p==0) ) return; db = p->db; assert( sqlite3_mutex_held(db->mutex) ); + sqlite3VdbeClearObject(db, p); if( p->pPrev ){ p->pPrev->pNext = p->pNext; }else{ assert( db->pVdbe==p ); db->pVdbe = p->pNext; @@ -2482,11 +2484,11 @@ if( p->pNext ){ p->pNext->pPrev = p->pPrev; } p->magic = VDBE_MAGIC_DEAD; p->db = 0; - sqlite3VdbeDeleteObject(db, p); + sqlite3DbFree(db, p); } /* ** Make sure the cursor p is ready to read or write the row to which it ** was last positioned. Return an error code if an OOM fault or I/O error Index: src/vdbesort.c ================================================================== --- src/vdbesort.c +++ src/vdbesort.c @@ -193,12 +193,15 @@ if( iBuf==0 ){ int nRead; /* Bytes to read from disk */ int rc; /* sqlite3OsRead() return code */ /* Determine how many bytes of data to read. */ - nRead = (int)(p->iEof - p->iReadOff); - if( nRead>p->nBuffer ) nRead = p->nBuffer; + if( (p->iEof - p->iReadOff) > (i64)p->nBuffer ){ + nRead = p->nBuffer; + }else{ + nRead = (int)(p->iEof - p->iReadOff); + } assert( nRead>0 ); /* Read data from the file. Return early if an error occurs. */ rc = sqlite3OsRead(p->pFile, p->aBuffer, nRead, p->iReadOff); assert( rc!=SQLITE_IOERR_SHORT_READ ); Index: src/vtab.c ================================================================== --- src/vtab.c +++ src/vtab.c @@ -493,11 +493,10 @@ return SQLITE_NOMEM; } pVTable->db = db; pVTable->pMod = pMod; - assert( pTab->azModuleArg[1]==0 ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); pTab->azModuleArg[1] = db->aDb[iDb].zName; /* Invoke the virtual table constructor */ assert( &db->pVtabCtx ); @@ -507,11 +506,10 @@ pPriorCtx = db->pVtabCtx; db->pVtabCtx = &sCtx; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); db->pVtabCtx = pPriorCtx; if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - pTab->azModuleArg[1] = 0; if( SQLITE_OK!=rc ){ if( zErr==0 ){ *pzErr = sqlite3MPrintf(db, "vtable constructor failed: %s", zModuleName); }else { Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -1813,13 +1813,19 @@ } if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){ /* Automatic indices are disabled at run-time */ return; } - if( (p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 ){ + if( (p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 + && (p->cost.plan.wsFlags & WHERE_COVER_SCAN)==0 + ){ /* We already have some kind of index in use for this query. */ return; + } + if( pSrc->viaCoroutine ){ + /* Cannot index a co-routine */ + return; } if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; } @@ -2994,11 +3000,11 @@ ** the SQL statement, then this function only considers plans using the ** named index. If no such plan is found, then the returned cost is ** SQLITE_BIG_DBL. If a plan is found that uses the named index, ** then the cost is calculated in the usual way. ** -** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table +** If a NOT INDEXED clause was attached to the table ** in the SELECT statement, then no indexes are considered. However, the ** selected plan may still take advantage of the built-in rowid primary key ** index. */ static void bestBtreeIndex(WhereBestIdx *p){ @@ -4021,10 +4027,20 @@ if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN no-match flag")); } + + /* Special case of a FROM clause subquery implemented as a co-routine */ + if( pTabItem->viaCoroutine ){ + int regYield = pTabItem->regReturn; + sqlite3VdbeAddOp2(v, OP_Integer, pTabItem->addrFillSub-1, regYield); + pLevel->p2 = sqlite3VdbeAddOp1(v, OP_Yield, regYield); + VdbeComment((v, "next row of co-routine %s", pTabItem->pTab->zName)); + sqlite3VdbeAddOp2(v, OP_If, regYield+1, addrBrk); + pLevel->op = OP_Goto; + }else #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ /* Case 0: The table is a virtual-table. Use the VFilter and VNext ** to access the data. Index: test/crash7.test ================================================================== --- test/crash7.test +++ test/crash7.test @@ -11,10 +11,11 @@ # # $Id: crash7.test,v 1.1 2008/04/03 14:36:26 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix crash7 ifcapable !crashtest { finish_test return } @@ -76,7 +77,40 @@ sqlite3 db test.db integrity_check crash7-1.$ii.integrity } } + +db close +forcedelete test.db +sqlite3 db test.db +do_execsql_test 2.0 { + CREATE TABLE t1(a, b, UNIQUE(a, b)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + DELETE FROM t1 WHERE rowid%2; +} +db_save_and_close + +for {set i 0} {$i < 20} {incr i} { + db_restore_and_reopen + do_test 2.[expr $i+1].1 { + crashsql -file test.db -seed $i {VACUUM} + } {1 {child process exited abnormally}} + do_execsql_test 2.[expr $i+1].2 { PRAGMA integrity_check } {ok} +} + finish_test Index: test/fts3matchinfo.test ================================================================== --- test/fts3matchinfo.test +++ test/fts3matchinfo.test @@ -404,8 +404,27 @@ do_execsql_test 8.4.3.1 { UPDATE t11_stat SET value = NULL; } do_catchsql_test 8.5.3.2 { SELECT mit(matchinfo(t11, 'nxa')) FROM t11 WHERE t11 MATCH 'a*' } {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +do_execsql_test 8.1 { + CREATE VIRTUAL TABLE t12 USING fts4; + INSERT INTO t12 VALUES('a b c d'); + SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'a NEAR/1 d OR a'; +} {{0 0 0 0 0 0 1 1 1}} +do_execsql_test 8.2 { + INSERT INTO t12 VALUES('a d c d'); + SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'a NEAR/1 d OR a'; +} { + {0 1 1 0 1 1 1 2 2} {1 1 1 1 1 1 1 2 2} +} +do_execsql_test 8.3 { + INSERT INTO t12 VALUES('a d d a'); + SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'a NEAR/1 d OR a'; +} { + {0 3 2 0 3 2 1 4 3} {1 3 2 1 3 2 1 4 3} {2 3 2 2 3 2 2 4 3} +} finish_test Index: test/minmax.test ================================================================== --- test/minmax.test +++ test/minmax.test @@ -297,11 +297,11 @@ execsql { SELECT max(rowid) FROM ( SELECT max(rowid) FROM t4 UNION SELECT max(rowid) FROM t5 ) } - } {1} + } {{}} do_test minmax-9.2 { execsql { SELECT max(rowid) FROM ( SELECT max(rowid) FROM t4 EXCEPT SELECT max(rowid) FROM t5 ) Index: test/minmax2.test ================================================================== --- test/minmax2.test +++ test/minmax2.test @@ -287,11 +287,11 @@ execsql { SELECT max(rowid) FROM ( SELECT max(rowid) FROM t4 UNION SELECT max(rowid) FROM t5 ) } - } {1} + } {{}} do_test minmax2-9.2 { execsql { SELECT max(rowid) FROM ( SELECT max(rowid) FROM t4 EXCEPT SELECT max(rowid) FROM t5 ) Index: test/shell1.test ================================================================== --- test/shell1.test +++ test/shell1.test @@ -147,11 +147,11 @@ } {0 {}} do_test shell1-1.14.3 { set res [catchcmd "-separator" ""] set rc [lindex $res 0] list $rc \ - [regexp {Error: missing argument for option: -separator} $res] + [regexp {Error: missing argument to -separator} $res] } {1 1} # -stats print memory stats before each finalize do_test shell1-1.14b.1 { catchcmd "-stats test.db" "" @@ -166,11 +166,11 @@ } {0 {}} do_test shell1-1.15.3 { set res [catchcmd "-nullvalue" ""] set rc [lindex $res 0] list $rc \ - [regexp {Error: missing argument for option: -nullvalue} $res] + [regexp {Error: missing argument to -nullvalue} $res] } {1 1} # -version show SQLite version do_test shell1-1.16.1 { set x [catchcmd "-version test.db" ""] Index: test/tkt-31338dca7e.test ================================================================== --- test/tkt-31338dca7e.test +++ test/tkt-31338dca7e.test @@ -89,11 +89,11 @@ CREATE INDEX t1b ON t1(b); CREATE TABLE t3(g); INSERT INTO t3 VALUES(4); CREATE TABLE t4(h); INSERT INTO t4 VALUES(5); - + SELECT * FROM t3 LEFT JOIN t1 ON d=g LEFT JOIN t4 ON c=h WHERE (a=1 AND h=4) OR (b IN ( SELECT x FROM (SELECT e+f AS x, e FROM t2 ORDER BY 1 LIMIT 2) GROUP BY e Index: test/tkt3527.test ================================================================== --- test/tkt3527.test +++ test/tkt3527.test @@ -50,12 +50,12 @@ INSERT INTO Element VALUES(3,'Elem3'); INSERT INTO Element VALUES(4,'Elem4'); INSERT INTO Element VALUES(5,'Elem5'); INSERT INTO ElemOr Values(3,4); INSERT INTO ElemOr Values(3,5); - INSERT INTO ElemAnd VALUES(1,3,1,1,1); - INSERT INTO ElemAnd VALUES(1,2,1,1,1); + INSERT INTO ElemAnd VALUES(1,3,'a','b','c'); + INSERT INTO ElemAnd VALUES(1,2,'x','y','z'); CREATE VIEW ElemView1 AS SELECT CAST(Element.Code AS VARCHAR(50)) AS ElemId, Element.Code AS ElemCode, @@ -110,14 +110,14 @@ ON Element.Level=0 AND Element.InnerCode=InnerElem.ElemCode ORDER BY ElemId, InnerCode; SELECT * FROM ElemView1; } -} {1 1 Elem1 2 1 1 1 0 0 1 1 Elem1 3 1 1 1 0 0 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} +} {1 1 Elem1 2 x y z 0 0 1 1 Elem1 3 a b c 0 0 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} do_test tkt3527-1.2 { db eval { SELECT * FROM ElemView2; } -} {1 1 Elem1 2 1 1 1 0 0 1 1 Elem1 3 1 1 1 0 0 1.3 3 Elem3 4 {} {} {} 1 1 1.3 3 Elem3 5 {} {} {} 1 1 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} +} {1 1 Elem1 2 x y z 0 0 1 1 Elem1 3 a b c 0 0 1.3 3 Elem3 4 {} {} {} 1 1 1.3 3 Elem3 5 {} {} {} 1 1 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} finish_test Index: tool/build-all-msvc.bat ================================================================== --- tool/build-all-msvc.bat +++ tool/build-all-msvc.bat @@ -4,10 +4,53 @@ :: build-all-msvc.bat -- :: :: Multi-Platform Build Tool for MSVC :: +REM +REM This batch script is used to build the SQLite DLL for multiple platforms +REM and configurations using MSVC. The built SQLite DLLs, their associated +REM import libraries, and optionally their symbols files, are placed within +REM the directory specified on the command line, in sub-directories named for +REM their respective platforms and configurations. This batch script must be +REM run from inside a Visual Studio Command Prompt for the desired version of +REM Visual Studio ^(the initial platform configured for the command prompt does +REM not really matter^). Exactly one command line argument is required, the +REM name of an existing directory to be used as the final destination directory +REM for the generated output files, which will be placed in sub-directories +REM created therein. Ideally, the directory specified should be empty. +REM +REM Example: +REM +REM CD /D C:\dev\sqlite\core +REM tool\build-all-msvc.bat C:\Temp +REM +REM In the example above, "C:\dev\sqlite\core" represents the root of the +REM source tree for SQLite and "C:\Temp" represents the final destination +REM directory for the generated output files. +REM +REM There are several environment variables that may be set to modify the +REM behavior of this batch script and its associated Makefile. The list of +REM platforms to build may be overriden by using the PLATFORMS environment +REM variable, which should contain a list of platforms ^(e.g. x86 x86_amd64 +REM x86_arm^). All platforms must be supported by the version of Visual Studio +REM being used. The list of configurations to build may be overridden by +REM setting the CONFIGURATIONS environment variable, which should contain a +REM list of configurations to build ^(e.g. Debug Retail^). Neither of these +REM variable values may contain any double quotes, surrounding or embedded. +REM Finally, the NCRTLIBPATH and NSDKLIBPATH environment variables may be set +REM to specify the location of the CRT and SDK, respectively, needed to compile +REM executables native to the architecture of the build machine during any +REM cross-compilation that may be necessary, depending on the platforms to be +REM built. These values in these two variables should be surrounded by double +REM quotes if they contain spaces. +REM +REM Please note that the SQLite build process performed by the Makefile +REM associated with this batch script requires both Gawk ^(gawk.exe^) and Tcl +REM 8.5 ^(tclsh85.exe^) to be present in a directory contained in the PATH +REM environment variable unless a pre-existing amalgamation file is used. +REM SETLOCAL REM SET __ECHO=ECHO REM SET __ECHO2=ECHO REM SET __ECHO3=ECHO @@ -91,31 +134,67 @@ SET PLATFORMS=x86 x86_amd64 x86_arm ) %_VECHO% Platforms = '%PLATFORMS%' +REM +REM NOTE: If the list of configurations is not already set, use the default +REM list. +REM +IF NOT DEFINED CONFIGURATIONS ( + SET CONFIGURATIONS=Debug Retail +) + +%_VECHO% Configurations = '%CONFIGURATIONS%' + REM REM NOTE: Setup environment variables to translate between the MSVC platform REM names and the names to be used for the platform-specific binary REM directories. REM +SET amd64_NAME=x64 +SET arm_NAME=ARM +SET x64_NAME=x64 SET x86_NAME=x86 SET x86_amd64_NAME=x64 SET x86_arm_NAME=ARM +SET x86_x64_NAME=x64 +%_VECHO% amd64_Name = '%amd64_NAME%' +%_VECHO% arm_Name = '%arm_NAME%' +%_VECHO% x64_Name = '%x64_NAME%' %_VECHO% x86_Name = '%x86_NAME%' %_VECHO% x86_amd64_Name = '%x86_amd64_NAME%' %_VECHO% x86_arm_Name = '%x86_arm_NAME%' +%_VECHO% x86_x64_Name = '%x86_x64_NAME%' REM REM NOTE: Check for the external tools needed during the build process ^(i.e. REM those that do not get compiled as part of the build process itself^) REM along the PATH. REM FOR %%T IN (gawk.exe tclsh85.exe) DO ( SET %%T_PATH=%%~dp$PATH:T ) + +REM +REM NOTE: The Gawk executable "gawk.exe" is required during the SQLite build +REM process unless a pre-existing amalgamation file is used. +REM +IF NOT DEFINED gawk.exe_PATH ( + ECHO The Gawk executable "gawk.exe" is required to be in the PATH. + GOTO errors +) + +REM +REM NOTE: The Tcl 8.5 executable "tclsh85.exe" is required during the SQLite +REM build process unless a pre-existing amalgamation file is used. +REM +IF NOT DEFINED tclsh85.exe_PATH ( + ECHO The Tcl 8.5 executable "tclsh85.exe" is required to be in the PATH. + GOTO errors +) REM REM NOTE: Set the TOOLPATH variable to contain all the directories where the REM external tools were found in the search above. REM @@ -126,15 +205,34 @@ REM REM NOTE: Check for MSVC 2012 because the Windows SDK directory handling is REM slightly different for that version. REM IF "%VisualStudioVersion%" == "11.0" ( - SET SET_NSDKLIBPATH=1 + REM + REM NOTE: If the Windows SDK library path has already been set, do not set + REM it to something else later on. + REM + IF NOT DEFINED NSDKLIBPATH ( + SET SET_NSDKLIBPATH=1 + ) ) ELSE ( CALL :fn_UnsetVariable SET_NSDKLIBPATH ) +REM +REM NOTE: Check if this is the Windows Phone SDK. If so, a different batch +REM file is necessary to setup the build environment. Since the variable +REM values involved here may contain parenthesis, using GOTO instead of +REM an IF block is required. +REM +IF DEFINED WindowsPhoneKitDir GOTO set_vcvarsall_phone +SET VCVARSALL=%VCINSTALLDIR%\vcvarsall.bat +GOTO set_vcvarsall_done +:set_vcvarsall_phone +SET VCVARSALL=%VCINSTALLDIR%\WPSDK\WP80\vcvarsphoneall.bat +:set_vcvarsall_done + REM REM NOTE: This is the outer loop. There should be exactly one iteration per REM platform. REM FOR %%P IN (%PLATFORMS%) DO ( @@ -171,20 +269,21 @@ CALL :fn_UnsetVariable LIB CALL :fn_UnsetVariable LIBPATH CALL :fn_UnsetVariable Platform REM CALL :fn_UnsetVariable VCINSTALLDIR CALL :fn_UnsetVariable VSINSTALLDIR + CALL :fn_UnsetVariable WindowsPhoneKitDir CALL :fn_UnsetVariable WindowsSdkDir CALL :fn_UnsetVariable WindowsSdkDir_35 CALL :fn_UnsetVariable WindowsSdkDir_old REM REM NOTE: Reset the PATH here to the absolute bare minimum required. REM SET PATH=%TOOLPATH%;%SystemRoot%\System32;%SystemRoot% - FOR %%B IN (Debug Retail) DO ( + FOR %%B IN (%CONFIGURATIONS%) DO ( REM REM NOTE: When preparing the debug build, set the DEBUG and MEMDEBUG REM environment variables to be picked up by the MSVC makefile REM itself. REM @@ -213,27 +312,29 @@ REM "%ComSpec%" /C ( REM REM NOTE: Attempt to setup the MSVC environment for this platform. REM - %__ECHO3% CALL "%VCINSTALLDIR%\vcvarsall.bat" %%P + %__ECHO3% CALL "%VCVARSALL%" %%P IF ERRORLEVEL 1 ( - ECHO Failed to call "%VCINSTALLDIR%\vcvarsall.bat" for platform %%P. + ECHO Failed to call "%VCVARSALL%" for platform %%P. GOTO errors ) REM REM NOTE: If this batch file is not running in "what-if" mode, check to REM be sure we were actually able to setup the MSVC environment REM as current versions of their official batch file do not set REM the exit code upon failure. REM - IF NOT DEFINED __ECHO ( - IF NOT DEFINED WindowsSdkDir ( - ECHO Cannot build, Windows SDK not found for platform %%P. - GOTO errors + IF NOT DEFINED __ECHO3 ( + IF NOT DEFINED WindowsPhoneKitDir ( + IF NOT DEFINED WindowsSdkDir ( + ECHO Cannot build, Windows SDK not found for platform %%P. + GOTO errors + ) ) ) REM REM NOTE: When using MSVC 2012, the native SDK path cannot simply use @@ -243,12 +344,17 @@ REM This must be done for each iteration because it relies upon REM the WindowsSdkDir environment variable being set by the batch REM file used to setup the MSVC environment. REM IF DEFINED SET_NSDKLIBPATH ( - CALL :fn_CopyVariable WindowsSdkDir NSDKLIBPATH - CALL :fn_AppendVariable NSDKLIBPATH \lib\win8\um\x86 + IF DEFINED WindowsPhoneKitDir ( + CALL :fn_CopyVariable WindowsPhoneKitDir NSDKLIBPATH + CALL :fn_AppendVariable NSDKLIBPATH \lib\x86 + ) ELSE IF DEFINED WindowsSdkDir ( + CALL :fn_CopyVariable WindowsSdkDir NSDKLIBPATH + CALL :fn_AppendVariable NSDKLIBPATH \lib\win8\um\x86 + ) ) REM REM NOTE: Unless prevented from doing so, invoke NMAKE with the MSVC REM makefile to clean any stale build output from previous Index: tool/mkvsix.tcl ================================================================== --- tool/mkvsix.tcl +++ tool/mkvsix.tcl @@ -1,19 +1,108 @@ #!/usr/bin/tclsh # # This script is used to generate a VSIX (Visual Studio Extension) file for # SQLite usable by Visual Studio. - +# +# PREREQUISITES +# +# 1. Tcl 8.4 and later are supported, earlier versions have not been tested. +# +# 2. The "sqlite3.h" file is assumed to exist in the parent directory of the +# directory containing this script. The [optional] second command line +# argument to this script may be used to specify an alternate location. +# This script also assumes that the "sqlite3.h" file corresponds with the +# version of the binaries to be packaged. This assumption is not verified +# by this script. +# +# 3. The temporary directory specified in the TEMP or TMP environment variables +# must refer to an existing directory writable by the current user. +# +# 4. The "zip" and "unzip" command line tools must be located either in a +# directory contained in the PATH environment variable or specified as the +# exact file names to execute in the "ZipTool" and "UnZipTool" environment +# variables, respectively. +# +# 5. The template VSIX file (which is basically a zip file) must be located in +# a "win" directory inside the directory containing this script. It should +# not contain any executable binaries. It should only contain dynamic +# textual content files to be processed using [subst] and/or static content +# files to be copied verbatim. +# +# 6. The executable and other compiled binary files to be packaged into the +# final VSIX file (e.g. DLLs, LIBs, and PDBs) must be located in a single +# directory tree. The top-level directory of the tree must be specified as +# the first command line argument to this script. The second level +# sub-directory names must match those of the build configuration (e.g. +# "Debug" or "Retail"). The third level sub-directory names must match +# those of the platform (e.g. "x86", "x64", and "ARM"). For example, the +# binary files to be packaged would need to be organized as follows when +# packaging the "Debug" and "Retail" build configurations for the "x86" and +# "x64" platforms (in this example, "C:\temp" is the top-level directory as +# specified in the first command line argument): +# +# C:\Temp\Debug\x86\sqlite3.lib +# C:\Temp\Debug\x86\sqlite3.dll +# C:\Temp\Debug\x86\sqlite3.pdb +# C:\Temp\Debug\x64\sqlite3.lib +# C:\Temp\Debug\x64\sqlite3.dll +# C:\Temp\Debug\x64\sqlite3.pdb +# C:\Temp\Retail\x86\sqlite3.lib +# C:\Temp\Retail\x86\sqlite3.dll +# C:\Temp\Retail\x86\sqlite3.pdb +# C:\Temp\Retail\x64\sqlite3.lib +# C:\Temp\Retail\x64\sqlite3.dll +# C:\Temp\Retail\x64\sqlite3.pdb +# +# The above directory tree organization is performed automatically if the +# "tool\build-all-msvc.bat" batch script is used to build the binary files +# to be packaged. +# +# USAGE +# +# The first argument to this script is required and must be the name of the +# top-level directory containing the directories and files organized into a +# tree as described in item 6 of the PREREQUISITES section, above. The second +# argument is optional and if present must contain the name of the directory +# containing the root of the source tree for SQLite. The third argument is +# optional and if present must contain the flavor the VSIX package to build. +# Currently, the only supported package flavors are "WinRT" and "WP80". The +# fourth argument is optional and if present must be a string containing a list +# of platforms to include in the VSIX package. The format of the platform list +# string is "platform1,platform2,platform3". Typically, when on Windows, this +# script is executed using commands similar to the following from a normal +# Windows command prompt: +# +# CD /D C:\dev\sqlite\core +# tclsh85 tool\mkvsix.tcl C:\Temp +# +# In the example above, "C:\dev\sqlite\core" represents the root of the source +# tree for SQLite and "C:\Temp" represents the top-level directory containing +# the executable and other compiled binary files, organized into a directory +# tree as described in item 6 of the PREREQUISITES section, above. +# +# This script should work on non-Windows platforms as well, provided that all +# the requirements listed in the PREREQUISITES section are met. +# +# NOTES +# +# The temporary directory is used as a staging area for the final VSIX file. +# The template VSIX file is extracted, its contents processed, and then the +# resulting files are packaged into the final VSIX file. +# +package require Tcl 8.4 + proc fail { {error ""} {usage false} } { if {[string length $error] > 0} then { puts stdout $error if {!$usage} then {exit 1} } puts stdout "usage:\ [file tail [info nameofexecutable]]\ -[file tail [info script]] \[sourceDirectory\]" +[file tail [info script]] \[sourceDirectory\]\ +\[packageFlavor\] \[platformNames\]" exit 1 } proc getEnvironmentVariable { name } { @@ -82,25 +171,28 @@ } proc substFile { fileName } { # # NOTE: Performs all Tcl command, variable, and backslash substitutions in - # the specified file and then re-writes the contents of that same file + # the specified file and then rewrites the contents of that same file # with the substituted data. # return [writeFile $fileName [uplevel 1 [list subst [readFile $fileName]]]] } -proc replaceBuildAndPlatform { fileName buildName platformName } { +proc replaceFileNameTokens { fileName name buildName platformName } { # # NOTE: Returns the specified file name containing the platform name instead # of platform placeholder tokens. # - return [string map [list $buildName $platformName] \ - $fileName] + return [string map [list $buildName $platformName \ + $name] $fileName] } +# +# NOTE: This is the entry point for this script. +# set script [file normalize [info script]] if {[string length $script] == 0} then { fail "script file currently being evaluated is unknown" true } @@ -112,11 +204,11 @@ # # NOTE: Process and verify all the command line arguments. # set argc [llength $argv] -if {$argc != 1 && $argc != 2} then {fail} +if {$argc < 1 || $argc > 4} then {fail} set binaryDirectory [lindex $argv 0] if {[string length $binaryDirectory] == 0} then { fail "invalid binary directory" @@ -125,11 +217,11 @@ if {![file exists $binaryDirectory] || \ ![file isdirectory $binaryDirectory]} then { fail "binary directory does not exist" } -if {$argc == 2} then { +if {$argc >= 2} then { set sourceDirectory [lindex $argv 1] } else { # # NOTE: Assume that the source directory is the parent directory of the one # that contains this script file. @@ -143,10 +235,51 @@ if {![file exists $sourceDirectory] || \ ![file isdirectory $sourceDirectory]} then { fail "source directory does not exist" } + +if {$argc >= 3} then { + set packageFlavor [lindex $argv 2] +} else { + # + # NOTE: Assume the package flavor is WinRT. + # + set packageFlavor WinRT +} + +if {[string length $packageFlavor] == 0} then { + fail "invalid package flavor" +} + +if {[string equal -nocase $packageFlavor WinRT]} then { + set shortName SQLite.WinRT + set displayName "SQLite for Windows Runtime" + set targetPlatformIdentifier Windows + set extraSdkPath "" + set extraFileListAttributes [appendArgs \ + "\r\n " {AppliesTo="WindowsAppContainer"} \ + "\r\n " {DependsOn="Microsoft.VCLibs, version=11.0"}] +} elseif {[string equal -nocase $packageFlavor WP80]} then { + set shortName SQLite.WP80 + set displayName "SQLite for Windows Phone" + set targetPlatformIdentifier "Windows Phone" + set extraSdkPath "\\..\\$targetPlatformIdentifier" + set extraFileListAttributes "" +} else { + fail "unsupported package flavor, must be \"WinRT\" or \"WP80\"" +} + +if {$argc >= 4} then { + set platformNames [list] + + foreach platformName [split [lindex $argv 3] ", "] { + if {[string length $platformName] > 0} then { + lappend platformNames $platformName + } + } +} ############################################################################### # # NOTE: Evaluate the user-specific customizations file, if it exists. @@ -167,11 +300,12 @@ ![file isfile $templateFile]} then { fail [appendArgs "template file \"" $templateFile "\" does not exist"] } set currentDirectory [pwd] -set outputFile [file join $currentDirectory sqlite-output.vsix] +set outputFile [file join $currentDirectory [appendArgs sqlite- \ + $packageFlavor -output.vsix]] if {[file exists $outputFile]} then { fail [appendArgs "output file \"" $outputFile "\" already exists"] } @@ -243,93 +377,96 @@ } ############################################################################### # -# NOTE: Setup the master file list data, including the necessary flags. +# NOTE: Setup all the master file list data. This includes the source file +# names, the destination file names, and the file processing flags. The +# possible file processing flags are: +# +# "buildNeutral" -- This flag indicates the file location and content do +# not depend on the build configuration. +# +# "platformNeutral" -- This flag indicates the file location and content +# do not depend on the build platform. +# +# "subst" -- This flag indicates that the file contains dynamic textual +# content that needs to be processed using [subst] prior to +# packaging the file into the final VSIX package. The primary +# use of this flag is to insert the name of the VSIX package, +# some package flavor-specific value, or the SQLite version +# into a file. +# +# "noDebug" -- This flag indicates that the file should be skipped when +# processing the debug build. +# +# "noRetail" -- This flag indicates that the file should be skipped when +# processing the retail build. +# +# "move" -- This flag indicates that the file should be moved from the +# source to the destination instead of being copied. +# +# This file metadata may be overridden, either in whole or in part, via +# the user-specific customizations file. # if {![info exists fileNames(source)]} then { - set fileNames(source) [list "" "" "" \ - [file join $sourceDirectory sqlite3.h] \ - [file join $binaryDirectory sqlite3.lib] \ - [file join $binaryDirectory sqlite3.dll]] + set fileNames(source) [list "" "" \ + [file join $stagingDirectory DesignTime sqlite3.props] \ + [file join $sourceDirectory sqlite3.h] \ + [file join $binaryDirectory sqlite3.lib] \ + [file join $binaryDirectory sqlite3.dll]] if {![info exists no(symbols)]} then { lappend fileNames(source) \ [file join $binaryDirectory sqlite3.pdb] } } if {![info exists fileNames(destination)]} then { set fileNames(destination) [list \ - [file join $stagingDirectory extension.vsixmanifest] \ - [file join $stagingDirectory SDKManifest.xml] \ - [file join $stagingDirectory DesignTime \ - SQLite.WinRT.props] \ - [file join $stagingDirectory DesignTime sqlite3.h] \ - [file join $stagingDirectory DesignTime sqlite3.lib] \ - [file join $stagingDirectory Redist sqlite3.dll]] + [file join $stagingDirectory extension.vsixmanifest] \ + [file join $stagingDirectory SDKManifest.xml] \ + [file join $stagingDirectory DesignTime .props] \ + [file join $stagingDirectory DesignTime sqlite3.h] \ + [file join $stagingDirectory DesignTime sqlite3.lib] \ + [file join $stagingDirectory Redist sqlite3.dll]] if {![info exists no(symbols)]} then { lappend fileNames(destination) \ [file join $stagingDirectory Redist sqlite3.pdb] } } -if {![info exists fileNames(buildNeutral)]} then { - set fileNames(buildNeutral) [list 1 1 1 1 0 0] - - if {![info exists no(symbols)]} then { - lappend fileNames(buildNeutral) 0 - } -} - -if {![info exists fileNames(platformNeutral)]} then { - set fileNames(platformNeutral) [list 1 1 1 1 0 0] - - if {![info exists no(symbols)]} then { - lappend fileNames(platformNeutral) 0 - } -} - -if {![info exists fileNames(subst)]} then { - set fileNames(subst) [list 1 1 1 0 0 0] - - if {![info exists no(symbols)]} then { - lappend fileNames(subst) 0 - } -} - -if {![info exists fileNames(noDebug)]} then { - set fileNames(noDebug) [list 0 0 0 0 0 0] - - if {![info exists no(symbols)]} then { - lappend fileNames(noDebug) 0 - } -} - -if {![info exists fileNames(noRetail)]} then { - set fileNames(noRetail) [list 0 0 0 0 0 0] - - if {![info exists no(symbols)]} then { - lappend fileNames(noRetail) 1 +if {![info exists fileNames(flags)]} then { + set fileNames(flags) [list \ + [list buildNeutral platformNeutral subst] \ + [list buildNeutral platformNeutral subst] \ + [list buildNeutral platformNeutral subst move] \ + [list buildNeutral platformNeutral] \ + [list] [list] [list noRetail]] + + if {![info exists no(symbols)]} then { + lappend fileNames(flags) [list noRetail] } } ############################################################################### # -# NOTE: Setup the list of builds supported by this script. +# NOTE: Setup the list of builds supported by this script. These may be +# overridden via the user-specific customizations file. # if {![info exists buildNames]} then { set buildNames [list Debug Retail] } ############################################################################### # -# NOTE: Setup the list of platforms supported by this script. +# NOTE: Setup the list of platforms supported by this script. These may be +# overridden via the command line or the user-specific customizations +# file. # if {![info exists platformNames]} then { set platformNames [list x86 x64 ARM] } @@ -339,66 +476,83 @@ # NOTE: Make sure the staging directory exists, creating it if necessary. # file mkdir $stagingDirectory # -# NOTE: Build the Tcl command used to extract the template package to the -# staging directory. +# NOTE: Build the Tcl command used to extract the template VSIX package to +# the staging directory. # set extractCommand [list exec -- $unzip $templateFile -d $stagingDirectory] # -# NOTE: Extract the template package to the staging directory. +# NOTE: Extract the template VSIX package to the staging directory. # eval $extractCommand ############################################################################### # -# NOTE: Process each file in the master file list. There are actually four -# parallel lists that contain the source file names, destination file -# names, the platform-neutral flags, and the use-subst flags. When the -# platform-neutral flag is non-zero, the file is not platform-specific. -# When the use-subst flag is non-zero, the file is considered to be a -# text file that may contain Tcl variable and/or command replacements, -# to be dynamically replaced during processing. If the source file name -# is an empty string, then the destination file name will be assumed to -# already exist in the staging directory and will not be copied; however, -# dynamic replacements may still be performed on the destination file -# prior to the package being re-zipped. -# -foreach sourceFileName $fileNames(source) \ - destinationFileName $fileNames(destination) \ - buildNeutral $fileNames(buildNeutral) platformNeutral \ - $fileNames(platformNeutral) useSubst $fileNames(subst) \ - noDebug $fileNames(noDebug) noRetail $fileNames(noRetail) { +# NOTE: Process each file in the master file list. There are actually three +# parallel lists that contain the source file names, the destination file +# names, and the file processing flags. If the "buildNeutral" flag is +# present, the file location and content do not depend on the build +# configuration and "CommonConfiguration" will be used in place of the +# build configuration name. If the "platformNeutral" flag is present, +# the file location and content do not depend on the build platform and +# "neutral" will be used in place of the build platform name. If the +# "subst" flag is present, the file is assumed to be a text file that may +# contain Tcl variable, command, and backslash replacements, to be +# dynamically replaced during processing using the Tcl [subst] command. +# If the "noDebug" flag is present, the file will be skipped when +# processing for the debug build. If the "noRetail" flag is present, the +# file will be skipped when processing for the retail build. If the +# "move" flag is present, the source file will be deleted after it is +# copied to the destination file. If the source file name is an empty +# string, the destination file name will be assumed to already exist in +# the staging directory and will not be copied; however, Tcl variable, +# command, and backslash replacements may still be performed on the +# destination file prior to the final VSIX package being built if the +# "subst" flag is present. +# +foreach sourceFileName $fileNames(source) \ + destinationFileName $fileNames(destination) \ + fileFlags $fileNames(flags) { + # + # NOTE: Process the file flags into separate boolean variables that may be + # used within the loop. + # + set isBuildNeutral [expr {[lsearch $fileFlags buildNeutral] != -1}] + set isPlatformNeutral [expr {[lsearch $fileFlags platformNeutral] != -1}] + set isMove [expr {[lsearch $fileFlags move] != -1}] + set useSubst [expr {[lsearch $fileFlags subst] != -1}] + # # NOTE: If the current file is build-neutral, then only one build will # be processed for it, namely "CommonConfiguration"; otherwise, each # supported build will be processed for it individually. # foreach buildName \ - [expr {$buildNeutral ? [list CommonConfiguration] : $buildNames}] { + [expr {$isBuildNeutral ? [list CommonConfiguration] : $buildNames}] { # # NOTE: Should the current file be skipped for this build? # - if {[info exists no${buildName}] && [set no${buildName}]} then { + if {[lsearch $fileFlags no${buildName}] != -1} then { continue } # # NOTE: If the current file is platform-neutral, then only one platform # will be processed for it, namely "neutral"; otherwise, each # supported platform will be processed for it individually. # foreach platformName \ - [expr {$platformNeutral ? [list neutral] : $platformNames}] { + [expr {$isPlatformNeutral ? [list neutral] : $platformNames}] { # # NOTE: Use the actual platform name in the destination file name. # - set newDestinationFileName [replaceBuildAndPlatform \ - $destinationFileName $buildName $platformName] + set newDestinationFileName [replaceFileNameTokens $destinationFileName \ + $shortName $buildName $platformName] # # NOTE: Does the source file need to be copied to the destination file? # if {[string length $sourceFileName] > 0} then { @@ -408,12 +562,22 @@ file mkdir [file dirname $newDestinationFileName] # # NOTE: Then, copy the source file to the destination file verbatim. # - file copy [replaceBuildAndPlatform $sourceFileName $buildName \ - $platformName] $newDestinationFileName + set newSourceFileName [replaceFileNameTokens $sourceFileName \ + $shortName $buildName $platformName] + + file copy $newSourceFileName $newDestinationFileName + + # + # NOTE: If this is a move instead of a copy, delete the source file + # now. + # + if {$isMove} then { + file delete $newSourceFileName + } } # # NOTE: Does the destination file contain dynamic replacements that must # be processed now? @@ -437,17 +601,17 @@ # relative paths. # cd $stagingDirectory # -# NOTE: Build the Tcl command used to archive the final package in the +# NOTE: Build the Tcl command used to archive the final VSIX package in the # output directory. # set archiveCommand [list exec -- $zip -r $outputFile *] # -# NOTE: Build the final package archive in the output directory. +# NOTE: Build the final VSIX package archive in the output directory. # eval $archiveCommand # # NOTE: Change back to the previously saved current directory. Index: tool/win/sqlite.vsix ================================================================== --- tool/win/sqlite.vsix +++ tool/win/sqlite.vsix cannot compute difference between binary files