diff --git a/Makefile.in b/Makefile.in index 5593b4c6ff..3d7c74ba37 100644 --- a/Makefile.in +++ b/Makefile.in @@ -934,6 +934,7 @@ clean: rm -f mkkeywordhash$(BEXE) keywordhash.h rm -f $(PUBLISH) rm -f *.da *.bb *.bbg gmon.out + rm -rf quota2a quota2b quota2c rm -rf tsrc .target_source rm -f tclsqlite3$(TEXE) rm -f testfixture$(TEXE) test.db diff --git a/Makefile.msc b/Makefile.msc index e95b443fe1..2d8f44c733 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -985,6 +985,9 @@ clean: del /Q mkkeywordhash.exe keywordhash.h -rmdir /Q/S .deps -rmdir /Q/S .libs + -rmdir /Q/S quota2a + -rmdir /Q/S quota2b + -rmdir /Q/S quota2c -rmdir /Q/S tsrc del /Q .target_source del /Q tclsqlite3.exe diff --git a/Makefile.vxworks b/Makefile.vxworks index 8d57da7283..4398c18265 100644 --- a/Makefile.vxworks +++ b/Makefile.vxworks @@ -657,6 +657,7 @@ clean: rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h rm -f $(PUBLISH) rm -f *.da *.bb *.bbg gmon.out + rm -rf quota2a quota2b quota2c rm -rf tsrc target_source rm -f testloadext.dll libtestloadext.so rm -f sqlite3.c fts?amal.c tclsqlite3.c diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 96905fc14b..421052b937 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -741,7 +741,7 @@ static void fts3Appendf( static char *fts3QuoteId(char const *zInput){ int nRet; char *zRet; - nRet = 2 + strlen(zInput)*2 + 1; + nRet = 2 + (int)strlen(zInput)*2 + 1; zRet = sqlite3_malloc(nRet); if( zRet ){ int i; @@ -997,7 +997,7 @@ static int fts3ContentColumns( nCol = sqlite3_column_count(pStmt); for(i=0; idoclist.pList = 0; @@ -3819,7 +3820,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ pPhrase->doclist.pList = aOut; if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ pPhrase->doclist.bFreeList = 1; - pPhrase->doclist.nList = (aOut - pPhrase->doclist.pList); + pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList); }else{ sqlite3_free(aOut); pPhrase->doclist.pList = 0; @@ -3915,7 +3916,7 @@ void sqlite3Fts3DoclistPrev( iMul = (bDescIdx ? -1 : 1); } - *pnList = pEnd - pNext; + *pnList = (int)(pEnd - pNext); *ppIter = pNext; *piDocid = iDocid; }else{ @@ -3929,7 +3930,7 @@ void sqlite3Fts3DoclistPrev( }else{ char *pSave = p; fts3ReversePoslist(aDoclist, &p); - *pnList = (pSave - p); + *pnList = (int)(pSave - p); } *ppIter = p; } @@ -3989,7 +3990,7 @@ static int fts3EvalPhraseNext( } pDL->pList = pIter; fts3PoslistCopy(0, &pIter); - pDL->nList = (pIter - pDL->pList); + pDL->nList = (int)(pIter - pDL->pList); /* pIter now points just past the 0x00 that terminates the position- ** list for document pDL->iDocid. However, if this position-list was @@ -4347,8 +4348,8 @@ static int fts3EvalStart(Fts3Cursor *pCsr){ Fts3Expr **ppOr = apOr; fts3EvalTokenCosts(pCsr, 0, pCsr->pExpr, &pTC, &ppOr, &rc); - nToken = pTC-aTC; - nOr = ppOr-apOr; + nToken = (int)(pTC-aTC); + nOr = (int)(ppOr-apOr); if( rc==SQLITE_OK ){ rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken); @@ -4420,7 +4421,7 @@ static int fts3EvalNearTrim( &pOut, aTmp, nParam1, nParam2, paPoslist, &p2 ); if( res ){ - nNew = (pOut - pPhrase->doclist.pList) - 1; + nNew = (int)(pOut - pPhrase->doclist.pList) - 1; assert( pPhrase->doclist.pList[nNew]=='\0' ); assert( nNew<=pPhrase->doclist.nList && nNew>0 ); memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); diff --git a/ext/fts3/fts3_aux.c b/ext/fts3/fts3_aux.c index 89bf3ebf6e..a2bff2e1d3 100644 --- a/ext/fts3/fts3_aux.c +++ b/ext/fts3/fts3_aux.c @@ -79,9 +79,9 @@ static int fts3auxConnectMethod( } zDb = argv[1]; - nDb = strlen(zDb); + nDb = (int)strlen(zDb); zFts3 = argv[3]; - nFts3 = strlen(zFts3); + nFts3 = (int)strlen(zFts3); rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); if( rc!=SQLITE_OK ) return rc; diff --git a/ext/fts3/fts3_porter.c b/ext/fts3/fts3_porter.c index 84aa132229..579745b85f 100644 --- a/ext/fts3/fts3_porter.c +++ b/ext/fts3/fts3_porter.c @@ -630,6 +630,7 @@ static const sqlite3_tokenizer_module porterTokenizerModule = { porterOpen, porterClose, porterNext, + 0 }; /* diff --git a/ext/fts3/fts3_tokenizer1.c b/ext/fts3/fts3_tokenizer1.c index d11a499766..deea06d92b 100644 --- a/ext/fts3/fts3_tokenizer1.c +++ b/ext/fts3/fts3_tokenizer1.c @@ -218,6 +218,7 @@ static const sqlite3_tokenizer_module simpleTokenizerModule = { simpleOpen, simpleClose, simpleNext, + 0, }; /* diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 4c36f26171..ce76e61f08 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -3056,8 +3056,8 @@ static int rtreeInit( sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); /* Allocate the sqlite3_vtab structure */ - nDb = strlen(argv[1]); - nName = strlen(argv[2]); + nDb = (int)strlen(argv[1]); + nName = (int)strlen(argv[2]); pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); if( !pRtree ){ return SQLITE_NOMEM; @@ -3152,10 +3152,10 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ nodeGetCell(&tree, &node, ii, &cell); sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid); - nCell = strlen(zCell); + nCell = (int)strlen(zCell); for(jj=0; jjaSample[] */ + assert( db->lookaside.bEnabled==0 ); if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){ return SQLITE_OK; } @@ -958,7 +959,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){ if( pIdx==0 ) continue; assert( pIdx->nSample==0 ); pIdx->nSample = nSample; - pIdx->aSample = sqlite3MallocZero( nSample*sizeof(IndexSample) ); + pIdx->aSample = sqlite3DbMallocZero(db, nSample*sizeof(IndexSample)); pIdx->avgEq = pIdx->aiRowEst[1]; if( pIdx->aSample==0 ){ db->mallocFailed = 1; @@ -1031,7 +1032,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){ if( n < 1){ pSample->u.z = 0; }else{ - pSample->u.z = sqlite3Malloc(n); + pSample->u.z = sqlite3DbMallocRaw(db, n); if( pSample->u.z==0 ){ db->mallocFailed = 1; sqlite3_finalize(pStmt); @@ -1107,7 +1108,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ /* Load the statistics from the sqlite_stat3 table. */ #ifdef SQLITE_ENABLE_STAT3 if( rc==SQLITE_OK ){ + int lookasideEnabled = db->lookaside.bEnabled; + db->lookaside.bEnabled = 0; rc = loadStat3(db, sInfo.zDatabase); + db->lookaside.bEnabled = lookasideEnabled; } #endif diff --git a/src/main.c b/src/main.c index 3a1dff576a..d148b4b42c 100644 --- a/src/main.c +++ b/src/main.c @@ -2704,35 +2704,27 @@ int sqlite3_extended_result_codes(sqlite3 *db, int onoff){ */ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ int rc = SQLITE_ERROR; - int iDb; + Btree *pBtree; + sqlite3_mutex_enter(db->mutex); - if( zDbName==0 ){ - iDb = 0; - }else{ - for(iDb=0; iDbnDb; iDb++){ - if( strcmp(db->aDb[iDb].zName, zDbName)==0 ) break; - } - } - if( iDbnDb ){ - Btree *pBtree = db->aDb[iDb].pBt; - if( pBtree ){ - Pager *pPager; - sqlite3_file *fd; - sqlite3BtreeEnter(pBtree); - pPager = sqlite3BtreePager(pBtree); - assert( pPager!=0 ); - fd = sqlite3PagerFile(pPager); - assert( fd!=0 ); - if( op==SQLITE_FCNTL_FILE_POINTER ){ - *(sqlite3_file**)pArg = fd; - rc = SQLITE_OK; - }else if( fd->pMethods ){ - rc = sqlite3OsFileControl(fd, op, pArg); - }else{ - rc = SQLITE_NOTFOUND; - } - sqlite3BtreeLeave(pBtree); + pBtree = sqlite3DbNameToBtree(db, zDbName); + if( pBtree ){ + Pager *pPager; + sqlite3_file *fd; + sqlite3BtreeEnter(pBtree); + pPager = sqlite3BtreePager(pBtree); + assert( pPager!=0 ); + fd = sqlite3PagerFile(pPager); + assert( fd!=0 ); + if( op==SQLITE_FCNTL_FILE_POINTER ){ + *(sqlite3_file**)pArg = fd; + rc = SQLITE_OK; + }else if( fd->pMethods ){ + rc = sqlite3OsFileControl(fd, op, pArg); + }else{ + rc = SQLITE_NOTFOUND; } + sqlite3BtreeLeave(pBtree); } sqlite3_mutex_leave(db->mutex); return rc; @@ -3028,15 +3020,34 @@ sqlite3_int64 sqlite3_uri_int64( } /* -** Return the filename of the database associated with a database -** connection. +** Return the Btree pointer identified by zDbName. Return NULL if not found. */ -const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ +Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ int i; for(i=0; inDb; i++){ - if( db->aDb[i].pBt && sqlite3StrICmp(zDbName, db->aDb[i].zName)==0 ){ - return sqlite3BtreeGetFilename(db->aDb[i].pBt); + if( db->aDb[i].pBt + && (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0) + ){ + return db->aDb[i].pBt; } } return 0; } + +/* +** Return the filename of the database associated with a database +** connection. +*/ +const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ + Btree *pBt = sqlite3DbNameToBtree(db, zDbName); + return pBt ? sqlite3BtreeGetFilename(pBt) : 0; +} + +/* +** Return 1 if database is read-only or 0 if read/write. Return -1 if +** no such database exists. +*/ +int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ + Btree *pBt = sqlite3DbNameToBtree(db, zDbName); + return pBt ? sqlite3PagerIsreadonly(sqlite3BtreePager(pBt)) : -1; +} diff --git a/src/sqlite.h.in b/src/sqlite.h.in index b43fe208f8..15a1b603a3 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4494,6 +4494,15 @@ sqlite3 *sqlite3_db_handle(sqlite3_stmt*); */ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); +/* +** CAPI3REF: Determine if a database is read-only +** +** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N +** of connection D is read-only, 0 if it is read/write, or -1 if N is not +** the name of a database on connection D. +*/ +int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); + /* ** CAPI3REF: Find the next prepared statement ** diff --git a/src/sqliteInt.h b/src/sqliteInt.h index cfe8fd64fb..15c3f302e2 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2701,6 +2701,7 @@ void sqlite3AddCollateType(Parse*, Token*); void sqlite3EndTable(Parse*,Token*,Token*,Select*); int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); +Btree *sqlite3DbNameToBtree(sqlite3*,const char*); int sqlite3CodeOnce(Parse *); Bitvec *sqlite3BitvecCreate(u32); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 967b1a004f..9161088e08 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -3108,23 +3108,19 @@ EXTERN int Sqlite3_Init(Tcl_Interp *interp){ return TCL_OK; } EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } -EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; } -EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; } EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } -EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; } -EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;} +/* Because it accesses the file-system and uses persistent state, SQLite +** is not considered appropriate for safe interpreters. Hence, we deliberately +** omit the _SafeInit() interfaces. +*/ #ifndef SQLITE_3_SUFFIX_ONLY int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } -int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; } -int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; } int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } -int Sqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; } -int Tclsqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;} #endif #ifdef TCLSH diff --git a/src/test1.c b/src/test1.c index 281823d5a8..e1220cf11f 100644 --- a/src/test1.c +++ b/src/test1.c @@ -4665,6 +4665,30 @@ static int test_db_filename( return TCL_OK; } +/* +** Usage: sqlite3_db_readonly DB DBNAME +** +** Return 1 or 0 if DBNAME is readonly or not. Return -1 if DBNAME does +** not exist. +*/ +static int test_db_readonly( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + const char *zDbName; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zDbName = Tcl_GetString(objv[2]); + Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_db_readonly(db, zDbName))); + return TCL_OK; +} + /* ** Usage: sqlite3_soft_heap_limit ?N? ** @@ -6055,6 +6079,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_release_memory", test_release_memory, 0}, { "sqlite3_db_release_memory", test_db_release_memory, 0}, { "sqlite3_db_filename", test_db_filename, 0}, + { "sqlite3_db_readonly", test_db_readonly, 0}, { "sqlite3_soft_heap_limit", test_soft_heap_limit, 0}, { "sqlite3_thread_cleanup", test_thread_cleanup, 0}, { "sqlite3_pager_refcounts", test_pager_refcounts, 0}, diff --git a/src/test6.c b/src/test6.c index 89f4a090b0..5f64cacca0 100644 --- a/src/test6.c +++ b/src/test6.c @@ -476,7 +476,15 @@ static int cfSync(sqlite3_file *pFile, int flags){ if( nName>nCrashFile ) nName = nCrashFile; } +#ifdef TRACE_CRASHTEST + printf("cfSync(): nName = %d, nCrashFile = %d, zName = %s, zCrashFile = %s\n", + nName, nCrashFile, zName, zCrashFile); +#endif + if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){ +#ifdef TRACE_CRASHTEST + printf("cfSync(): name matched, g.iCrash = %d\n", g.iCrash); +#endif if( (--g.iCrash)==0 ) isCrash = 1; } diff --git a/src/test8.c b/src/test8.c index 283d790b7d..80e7adaf2c 100644 --- a/src/test8.c +++ b/src/test8.c @@ -831,13 +831,10 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ if( !isIgnoreUsable && !pConstraint->usable ) continue; iCol = pConstraint->iColumn; - if( pVtab->aIndex[iCol] || iCol<0 ){ - char *zCol = pVtab->aCol[iCol]; + if( iCol<0 || pVtab->aIndex[iCol] ){ + char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid"; char *zOp = 0; useIdx = 1; - if( iCol<0 ){ - zCol = "rowid"; - } switch( pConstraint->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: zOp = "="; break; @@ -870,13 +867,12 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ** on a column that this virtual table has an index for, then consume ** the ORDER BY clause. */ - if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){ + if( pIdxInfo->nOrderBy==1 && ( + pIdxInfo->aOrderBy->iColumn<0 || + pVtab->aIndex[pIdxInfo->aOrderBy->iColumn]) ){ int iCol = pIdxInfo->aOrderBy->iColumn; - char *zCol = pVtab->aCol[iCol]; + char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid"; char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC"; - if( iCol<0 ){ - zCol = "rowid"; - } zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir); string_concat(&zQuery, zNew, 1, &rc); pIdxInfo->orderByConsumed = 1; diff --git a/src/util.c b/src/util.c index 325c75aae1..dd3b08ae46 100644 --- a/src/util.c +++ b/src/util.c @@ -216,11 +216,11 @@ int sqlite3Dequote(char *z){ ** Some systems have stricmp(). Others have strcasecmp(). Because ** there is no consistency, we will define our own. ** -** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows -** applications and extensions to compare the contents of two buffers -** containing UTF-8 strings in a case-independent fashion, using the same -** definition of case independence that SQLite uses internally when -** comparing identifiers. +** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and +** sqlite3_strnicmp() APIs allow applications and extensions to compare +** the contents of two buffers containing UTF-8 strings in a +** case-independent fashion, using the same definition of "case +** independence" that SQLite uses internally when comparing identifiers. */ int sqlite3_stricmp(const char *zLeft, const char *zRight){ register unsigned char *a, *b; diff --git a/src/where.c b/src/where.c index eda8d5fb8e..e6083f0033 100644 --- a/src/where.c +++ b/src/where.c @@ -3802,8 +3802,7 @@ static Bitmask codeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ - Bitmask notReady, /* Which tables are currently available */ - Expr *pWhere /* Complete WHERE clause */ + Bitmask notReady /* Which tables are currently available */ ){ int j, k; /* Loop counters */ int iCur; /* The VDBE cursor for the table */ @@ -4342,10 +4341,25 @@ static Bitmask codeOneLoopStart( ** Then for every term xN, evaluate as the subexpression: xN AND z ** That way, terms in y that are factored into the disjunction will ** be picked up by the recursive calls to sqlite3WhereBegin() below. + ** + ** Actually, each subexpression is converted to "xN AND w" where w is + ** the "interesting" terms of z - terms that did not originate in the + ** ON or USING clause of a LEFT JOIN, and terms that are usable as + ** indices. */ if( pWC->nTerm>1 ){ - pAndExpr = sqlite3ExprAlloc(pParse->db, TK_AND, 0, 0); - pAndExpr->pRight = pWhere; + int iTerm; + for(iTerm=0; iTermnTerm; iTerm++){ + Expr *pExpr = pWC->a[iTerm].pExpr; + if( ExprHasProperty(pExpr, EP_FromJoin) ) continue; + if( pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_ORINFO) ) continue; + if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; + pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); + pAndExpr = sqlite3ExprAnd(pParse->db, pAndExpr, pExpr); + } + if( pAndExpr ){ + pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0); + } } for(ii=0; iinTerm; ii++){ @@ -4387,7 +4401,10 @@ static Bitmask codeOneLoopStart( } } } - sqlite3DbFree(pParse->db, pAndExpr); + if( pAndExpr ){ + pAndExpr->pLeft = 0; + sqlite3ExprDelete(pParse->db, pAndExpr); + } sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); sqlite3VdbeResolveLabel(v, iLoopBody); @@ -5043,7 +5060,7 @@ WhereInfo *sqlite3WhereBegin( for(i=0; ia[i]; explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags); - notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady, pWhere); + notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady); pWInfo->iContinue = pLevel->addrCont; } diff --git a/test/bigfile.test b/test/bigfile.test index 52d74edbe6..d9470acfba 100644 --- a/test/bigfile.test +++ b/test/bigfile.test @@ -69,7 +69,7 @@ do_test bigfile-1.1 { # large files. So skip all of the remaining tests in this file. # db close -if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} { +if {[catch {fake_big_file 4096 [get_pwd]/test.db} msg]} { puts "**** Unable to create a file larger than 4096 MB. *****" finish_test return @@ -109,7 +109,7 @@ do_test bigfile-1.4 { } $::MAGIC_SUM db close -if {[catch {fake_big_file 8192 [pwd]/test.db}]} { +if {[catch {fake_big_file 8192 [get_pwd]/test.db}]} { puts "**** Unable to create a file larger than 8192 MB. *****" finish_test return @@ -148,7 +148,7 @@ do_test bigfile-1.9 { } $::MAGIC_SUM db close -if {[catch {fake_big_file 16384 [pwd]/test.db}]} { +if {[catch {fake_big_file 16384 [get_pwd]/test.db}]} { puts "**** Unable to create a file larger than 16384 MB. *****" finish_test return diff --git a/test/bigfile2.test b/test/bigfile2.test index 9810d3a0f3..b13b75641b 100644 --- a/test/bigfile2.test +++ b/test/bigfile2.test @@ -29,7 +29,7 @@ do_execsql_test 1.1 { # are actually in use and new pages will be appended to the file. # db close -if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} { +if {[catch {fake_big_file 4096 [get_pwd]/test.db} msg]} { puts "**** Unable to create a file larger than 4096 MB. *****" finish_test return diff --git a/test/crash5.test b/test/crash5.test index 42248d719e..a786712354 100644 --- a/test/crash5.test +++ b/test/crash5.test @@ -47,7 +47,7 @@ for {set ii 0} {$ii < 10} {incr ii} { do_test crash5-$ii.$jj.1 { crashsql -delay 1 -file test.db-journal -seed $ii -tclbody [join [list \ [list set iFail $jj] { - sqlite3_crashparams 0 [file join [pwd] test.db-journal] + sqlite3_crashparams 0 [file join [get_pwd] test.db-journal] # Begin a transaction and evaluate a "CREATE INDEX" statement # with the iFail'th malloc() set to fail. This operation will @@ -89,7 +89,7 @@ for {set ii 0} {$ii < 10} {incr ii} { # by writing page 4 out to the db file. If it crashes later on, # before syncing the journal... Corruption! # - sqlite3_crashparams 1 [file join [pwd] test.db-journal] + sqlite3_crashparams 1 [file join [get_pwd] test.db-journal] sqlite3_release_memory 8092 }]] {} expr 1 diff --git a/test/e_insert.test b/test/e_insert.test index a10d5283de..ac4361f8df 100644 --- a/test/e_insert.test +++ b/test/e_insert.test @@ -50,7 +50,7 @@ proc do_insert_tests {args} { uplevel do_select_tests $args } -# EVIDENCE-OF: R-55375-41353 -- syntax diagram insert-stmt +# EVIDENCE-OF: R-21350-31508 -- syntax diagram insert-stmt # do_insert_tests e_insert-0 { 1 "INSERT INTO a1 DEFAULT VALUES" {} @@ -123,6 +123,20 @@ do_insert_tests e_insert-0 { 68 "INSERT OR IGNORE INTO a1 (b, a) SELECT c, b FROM a2" {} 69 "REPLACE INTO a1 (b, a) SELECT c, b FROM a2" {} 70 "REPLACE INTO main.a1 (b, a) SELECT c, b FROM a2" {} + 71 "INSERT INTO a1 (b, a) VALUES(1, 2),(3,4)" {} + 72 "INSERT INTO main.a1 (b, a) VALUES(1, 2),(3,4)" {} + 73 "INSERT OR ROLLBACK INTO main.a1 (b, a) VALUES(1, 2),(3,4)" {} + 74 "INSERT OR ROLLBACK INTO a1 (b, a) VALUES(1, 2),(3,4)" {} + 75 "INSERT OR ABORT INTO main.a1 (b, a) VALUES(1, 2),(3,4)" {} + 76 "INSERT OR ABORT INTO a1 (b, a) VALUES(1, 2),(3,4)" {} + 77 "INSERT OR REPLACE INTO main.a1 (b, a) VALUES(1, 2),(3,4)" {} + 78 "INSERT OR REPLACE INTO a1 (b, a) VALUES(1, 2),(3,4)" {} + 79 "INSERT OR FAIL INTO main.a1 (b, a) VALUES(1, 2),(3,4)" {} + 80 "INSERT OR FAIL INTO a1 (b, a) VALUES(1, 2),(3,4)" {} + 81 "INSERT OR FAIL INTO main.a1 (b, a) VALUES(1, 2),(3,4)" {} + 82 "INSERT OR IGNORE INTO a1 (b, a) VALUES(1, 2),(3,4)" {} + 83 "REPLACE INTO a1 (b, a) VALUES(1, 2),(3,4)" {} + 84 "REPLACE INTO main.a1 (b, a) VALUES(1, 2),(3,4)" {} } delete_all_data diff --git a/test/e_uri.test b/test/e_uri.test index 5275ec1b54..8110d70497 100644 --- a/test/e_uri.test +++ b/test/e_uri.test @@ -131,10 +131,10 @@ sqlite3_config_uri 1 if {$tcl_platform(platform) == "unix"} { set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI] foreach {tn uri error} " - 1 {file://localhost[pwd]/test.db} {not an error} - 2 {file://[pwd]/test.db} {not an error} - 3 {file://x[pwd]/test.db} {invalid uri authority: x} - 4 {file://invalid[pwd]/test.db} {invalid uri authority: invalid} + 1 {file://localhost[get_pwd]/test.db} {not an error} + 2 {file://[get_pwd]/test.db} {not an error} + 3 {file://x[get_pwd]/test.db} {invalid uri authority: x} + 4 {file://invalid[get_pwd]/test.db} {invalid uri authority: invalid} " { do_test 2.$tn { set DB [sqlite3_open_v2 $uri $flags ""] @@ -153,9 +153,9 @@ if {$tcl_platform(platform) == "unix"} { # parameters passed through to the VFS xOpen() methods. # foreach {tn uri parse} " - 1 {file:test.db#abc} {[pwd]/test.db {}} - 2 {file:test.db?a=b#abc} {[pwd]/test.db {a b}} - 3 {file:test.db?a=b#?c=d} {[pwd]/test.db {a b}} + 1 {file:test.db#abc} {[get_pwd]/test.db {}} + 2 {file:test.db?a=b#abc} {[get_pwd]/test.db {a b}} + 3 {file:test.db?a=b#?c=d} {[get_pwd]/test.db {a b}} " { do_filepath_test 3.$tn { parse_uri $uri } $parse } @@ -171,7 +171,7 @@ foreach {tn uri parse} " # path is interpreted as a relative path. # foreach {tn uri parse} " - 1 {file:test.db} {[pwd]/test.db {}} + 1 {file:test.db} {[get_pwd]/test.db {}} 2 {file:/test.db} {/test.db {}} 3 {file:///test.db} {/test.db {}} 4 {file://localhost/test.db} {/test.db {}} diff --git a/test/filectrl.test b/test/filectrl.test index 9f077d523c..1e4ec59853 100644 --- a/test/filectrl.test +++ b/test/filectrl.test @@ -34,7 +34,7 @@ do_test filectrl-1.4 { do_test filectrl-1.5 { db close sqlite3 db test_control_lockproxy.db - file_control_lockproxy_test db [pwd] + file_control_lockproxy_test db [get_pwd] } {} db close forcedelete .test_control_lockproxy.db-conch test.proxy diff --git a/test/fts4langid.test b/test/fts4langid.test index 012924c70d..843e11f9ab 100644 --- a/test/fts4langid.test +++ b/test/fts4langid.test @@ -482,6 +482,4 @@ foreach lid [list 4 [expr 1<<30]] { SELECT count(*) FROM t6_segments; } {4 4} } - - finish_test diff --git a/test/ioerr2.test b/test/ioerr2.test index 325c0baded..5150ace3ab 100644 --- a/test/ioerr2.test +++ b/test/ioerr2.test @@ -130,7 +130,7 @@ do_test ioerr2-5 { } } msg] list $rc $msg -} {1 {callback requested query abort}} +} {1 {abort due to ROLLBACK}} if {$::tcl_platform(platform) == "unix"} { # Cause the call to xAccess used by [pragma temp_store_directory] to diff --git a/test/misc7.test b/test/misc7.test index c69e60bb7c..146dca0412 100644 --- a/test/misc7.test +++ b/test/misc7.test @@ -483,7 +483,7 @@ do_test misc7-20.1 { # Try to open a really long file name. # do_test misc7-21.1 { - set zFile [file join [pwd] "[string repeat abcde 104].db"] + set zFile [file join [get_pwd] "[string repeat abcde 104].db"] set rc [catch {sqlite3 db2 $zFile} msg] list $rc $msg } {1 {unable to open database file}} diff --git a/test/pager1.test b/test/pager1.test index 415eb6ab81..45253857c3 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -535,7 +535,7 @@ proc copy_on_mj_delete {method filename args} { return SQLITE_OK } -set pwd [pwd] +set pwd [get_pwd] foreach {tn1 tcl} { 1 { set prefix "test.db" } 2 { @@ -887,6 +887,24 @@ do_test pager1.4.7.3 { delete_file test.db-journal file exists test.db-journal } {0} +do_test pager1.4.8.1 { + catch {file attributes test.db -permissions r--------} + catch {file attributes test.db -readonly 1} + sqlite3 db test.db + db eval { SELECT * FROM t1 } + sqlite3_db_readonly db main +} {1} +do_test pager1.4.8.2 { + sqlite3_db_readonly db xyz +} {-1} +do_test pager1.4.8.3 { + db close + catch {file attributes test.db -readonly 0} + catch {file attributes test.db -permissions rw-rw-rw-} msg + sqlite3 db test.db + db eval { SELECT * FROM t1 } + sqlite3_db_readonly db main +} {0} #------------------------------------------------------------------------- # The following tests deal with multi-file commits. @@ -1001,7 +1019,7 @@ do_test pager1-5.4.1 { # the master-journal name encoded as utf-8 with no nul term. # set mj_pointer [expr { - 20 + [string length [pwd]] + [string length "/test.db-mjXXXXXX9XX"] + 20 + [string length [get_pwd]] + [string length "/test.db-mjXXXXXX9XX"] }] expr {$::max_journal==(512+2*(1024+8)+$mj_pointer)} } 1 @@ -1020,7 +1038,7 @@ do_test pager1-5.4.2 { # written starting at the next (in this case 512 byte) sector boundary. # set mj_pointer [expr { - 20 + [string length [pwd]] + [string length "/test.db-mjXXXXXX9XX"] + 20 + [string length [get_pwd]] + [string length "/test.db-mjXXXXXX9XX"] }] expr {$::max_journal==(((512+2*(1024+8)+511)/512)*512 + $mj_pointer)} } 1 diff --git a/test/pragma.test b/test/pragma.test index 3f17bd742b..bb10327c3a 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -990,7 +990,7 @@ do_test pragma-9.4 { } {} ifcapable wsd { do_test pragma-9.5 { - set pwd [string map {' ''} [file nativename [pwd]]] + set pwd [string map {' ''} [file nativename [get_pwd]]] execsql " PRAGMA temp_store_directory='$pwd'; " @@ -999,7 +999,7 @@ ifcapable wsd { execsql { PRAGMA temp_store_directory; } - } [list [file nativename [pwd]]] + } [list [file nativename [get_pwd]]] do_test pragma-9.7 { catchsql { PRAGMA temp_store_directory='/NON/EXISTENT/PATH/FOOBAR'; diff --git a/test/quota.test b/test/quota.test index e12b83a429..ec89086d35 100644 --- a/test/quota.test +++ b/test/quota.test @@ -221,7 +221,7 @@ do_test quota-3.3.1 { execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b set ::quota -} [list [file join [pwd] test.db] 5120] +} [list [file join [get_pwd] test.db] 5120] do_test quota-3.2.X { foreach db {db1a db2a db2b db1b} { catch { $db close } } diff --git a/test/quota2.test b/test/quota2.test index cf3449dafc..fae4d2dbd7 100644 --- a/test/quota2.test +++ b/test/quota2.test @@ -28,7 +28,7 @@ foreach dir {quota2a quota2a/x1 quota2a/x2 quota2b quota2c} { # that is the same across platforms. # unset -nocomplain ::quota_pwd ::quota_mapping -set ::quota_pwd [string map {\\ /} [pwd]] +set ::quota_pwd [string map {\\ /} [get_pwd]] set ::quota_mapping [list $::quota_pwd PWD] proc standard_path {x} { set x [string map {\\ /} $x] diff --git a/test/tester.tcl b/test/tester.tcl index e27a483f41..5bc7eb704d 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -19,6 +19,7 @@ # # Commands to manipulate the db and the file-system at a high level: # +# get_pwd # copy_file FROM TO # delete_file FILENAME # drop_all_tables ?DB? @@ -148,6 +149,24 @@ proc getFileRetryDelay {} { return $::G(file-retry-delay) } +# Return the string representing the name of the current directory. On +# Windows, the result is "normalized" to whatever our parent command shell +# is using to prevent case-mismatch issues. +# +proc get_pwd {} { + if {$::tcl_platform(platform) eq "windows"} { + # + # NOTE: Cannot use [file normalize] here because it would alter the + # case of the result to what Tcl considers canonical, which would + # defeat the purpose of this procedure. + # + return [string map [list \\ /] \ + [string trim [exec -- $::env(ComSpec) /c echo %CD%]]] + } else { + return [pwd] + } +} + # Copy file $from into $to. This is used because some versions of # TCL for windows (notably the 8.4.1 binary package shipped with the # current mingw release) have a broken "file copy" command. @@ -984,7 +1003,7 @@ proc crashsql {args} { # $crashfile gets compared to the native filename in # cfSync(), which can be different then what TCL uses by # default, so here we force it to the "nativename" format. - set cfile [string map {\\ \\\\} [file nativename [file join [pwd] $crashfile]]] + set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]] set f [open crash.tcl w] puts $f "sqlite3_crash_enable 1" diff --git a/test/tkt-94c04eaadb.test b/test/tkt-94c04eaadb.test index cce8a98eb4..0063de664d 100644 --- a/test/tkt-94c04eaadb.test +++ b/test/tkt-94c04eaadb.test @@ -27,7 +27,7 @@ do_test tkt-94c94-1.1 { # Grow the file to larger than 4096MB (2^32 bytes) db close -if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} { +if {[catch {fake_big_file 4096 [get_pwd]/test.db} msg]} { puts "**** Unable to create a file larger than 4096 MB. *****" finish_test return diff --git a/test/trace2.test b/test/trace2.test index 42738db3aa..b1a53e77b5 100644 --- a/test/trace2.test +++ b/test/trace2.test @@ -134,18 +134,19 @@ ifcapable fts3 { "-- REPLACE INTO 'main'.'x1_stat' VALUES(0,?)" "-- SELECT (SELECT max(idx) FROM 'main'.'x1_segdir' WHERE level = ?) + 1" "-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)" - "-- INSERT INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)" + "-- REPLACE INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)" } do_trace_test 2.3 { INSERT INTO x1(x1) VALUES('optimize'); } { "INSERT INTO x1(x1) VALUES('optimize');" + "-- SELECT DISTINCT level / (1024 * ?) FROM 'main'.'x1_segdir'" "-- SELECT idx, start_block, leaves_end_block, end_block, root FROM 'main'.'x1_segdir' WHERE level BETWEEN ? AND ?ORDER BY level DESC, idx ASC" "-- SELECT max(level) FROM 'main'.'x1_segdir' WHERE level BETWEEN ? AND ?" "-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)" "-- DELETE FROM 'main'.'x1_segdir' WHERE level BETWEEN ? AND ?" - "-- INSERT INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)" + "-- REPLACE INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)" } } diff --git a/test/uri.test b/test/uri.test index 90074d0d27..93a32b773e 100644 --- a/test/uri.test +++ b/test/uri.test @@ -54,9 +54,9 @@ foreach {tn uri file} { if {$tcl_platform(platform)=="windows"} { if {$tn>14} break - set uri [string map [list PWD /[pwd]] $uri] + set uri [string map [list PWD /[get_pwd]] $uri] } else { - set uri [string map [list PWD [pwd]] $uri] + set uri [string map [list PWD [get_pwd]] $uri] } if {[file isdir $file]} {error "$file is a directory"} @@ -274,9 +274,9 @@ foreach {tn uri res} { } { if {$tcl_platform(platform)=="windows"} { - set uri [string map [list PWD [string range [pwd] 3 end]] $uri] + set uri [string map [list PWD [string range [get_pwd] 3 end]] $uri] } else { - set uri [string map [list PWD [string range [pwd] 1 end]] $uri] + set uri [string map [list PWD [string range [get_pwd] 1 end]] $uri] } do_test 6.$tn { diff --git a/test/wal.test b/test/wal.test index 3b63d3e792..9d2c6b14b4 100644 --- a/test/wal.test +++ b/test/wal.test @@ -1477,7 +1477,7 @@ foreach pgsz {512 1024 2048 4096 8192 16384 32768 65536} { # Test that when 1 or more pages are recovered from a WAL file, # sqlite3_log() is invoked to report this to the user. # -set walfile [file nativename [file join [pwd] test.db-wal]] +set walfile [file nativename [file join [get_pwd] test.db-wal]] catch {db close} forcedelete test.db do_test wal-23.1 { diff --git a/test/walbig.test b/test/walbig.test index 092db2381b..c43b7e2503 100644 --- a/test/walbig.test +++ b/test/walbig.test @@ -52,7 +52,7 @@ do_test walbig-1.0 { } {wal} db close -if {[catch {fake_big_file 5000 [pwd]/test.db}]} { +if {[catch {fake_big_file 5000 [get_pwd]/test.db}]} { puts "**** Unable to create a file larger than 5000 MB. *****" finish_test return diff --git a/test/where7.test b/test/where7.test index ffb7173d21..b6cd7ccbbc 100644 --- a/test/where7.test +++ b/test/where7.test @@ -23339,7 +23339,7 @@ do_execsql_test where7-3.1 { OR t301.c8 = 1407424651264000) ORDER BY t302.c5 LIMIT 200; } { - 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~5 rows)} + 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~10 rows)} 0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?) (~2 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} diff --git a/test/where9.test b/test/where9.test index 8c5c96e2a8..23260a6b65 100644 --- a/test/where9.test +++ b/test/where9.test @@ -364,7 +364,7 @@ ifcapable explain { } { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} - 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} } do_execsql_test where9-3.2 { EXPLAIN QUERY PLAN @@ -374,7 +374,7 @@ ifcapable explain { } { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} - 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} } } @@ -453,8 +453,8 @@ ifcapable explain { do_execsql_test where9-5.1 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL) } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~2 rows)} - 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~2 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~3 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~3 rows)} } # In contrast, b=1000 is preferred over any OR-clause. @@ -856,5 +856,25 @@ do_test where9-7.3.2 { } } {79 81} +# Fix for ticket [b7c8682cc17f32903f03a610bd0d35ffd3c1e6e4] +# "Incorrect result from LEFT JOIN with OR in the WHERE clause" +# +do_test where9-8.1 { + db eval { + CREATE TABLE t81(a INTEGER PRIMARY KEY, b, c, d); + CREATE TABLE t82(x INTEGER PRIMARY KEY, y); + CREATE TABLE t83(p INTEGER PRIMARY KEY, q); + + INSERT INTO t81 VALUES(2,3,4,5); + INSERT INTO t81 VALUES(3,4,5,6); + INSERT INTO t82 VALUES(2,4); + INSERT INTO t83 VALUES(5,55); + + SELECT * + FROM t81 LEFT JOIN t82 ON y=b JOIN t83 + WHERE c==p OR d==p + ORDER BY +a; + } +} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} finish_test