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/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 8f97c8be98..bc20dfa3f3 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -434,16 +434,42 @@ int sqlite3Fts3ReadLock(Fts3Table *p){ return rc; } +/* +** FTS maintains a separate indexes for each language-id (a 32-bit integer). +** Within each language id, a separate index is maintained to store the +** document terms, and each configured prefix size (configured the FTS +** "prefix=" option). And each index consists of multiple levels ("relative +** levels"). +** +** All three of these values (the language id, the specific index and the +** level within the index) are encoded in 64-bit integer values stored +** in the %_segdir table on disk. This function is used to convert three +** separate component values into the single 64-bit integer value that +** can be used to query the %_segdir table. +** +** Specifically, each language-id/index combination is allocated 1024 +** 64-bit integer level values ("absolute levels"). The main terms index +** for language-id 0 is allocate values 0-1023. The first prefix index +** (if any) for language-id 0 is allocated values 1024-2047. And so on. +** Language 1 indexes are allocated immediately following language 0. +** +** So, for a system with nPrefix prefix indexes configured, the block of +** absolute levels that corresponds to language-id iLangid and index +** iIndex starts at absolute level ((iLangid * (nPrefix+1) + iIndex) * 1024). +*/ static sqlite3_int64 getAbsoluteLevel( Fts3Table *p, int iLangid, int iIndex, int iLevel ){ + sqlite3_int64 iBase; /* First absolute level for iLangid/iIndex */ assert( iLangid>=0 ); assert( p->nIndex>0 ); assert( iIndex>=0 && iIndexnIndex ); - return (iLangid * p->nIndex + iIndex) * FTS3_SEGDIR_MAXLEVEL + iLevel; + + iBase = ((sqlite3_int64)iLangid * p->nIndex + iIndex) * FTS3_SEGDIR_MAXLEVEL; + return iBase + iLevel; } @@ -468,7 +494,7 @@ int sqlite3Fts3AllSegdirs( Fts3Table *p, /* FTS3 table */ int iLangid, /* Language being queried */ int iIndex, /* Index for p->aIndex[] */ - int iLevel, /* Level to select */ + int iLevel, /* Level to select (relative level) */ sqlite3_stmt **ppStmt /* OUT: Compiled statement */ ){ int rc; @@ -483,7 +509,7 @@ int sqlite3Fts3AllSegdirs( rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); - sqlite3_bind_int(pStmt, 2, + sqlite3_bind_int64(pStmt, 2, getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1) ); } @@ -491,7 +517,7 @@ int sqlite3Fts3AllSegdirs( /* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */ rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, iLevel)); + sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex,iLevel)); } } *ppStmt = pStmt; @@ -1763,7 +1789,7 @@ static int fts3WriteSegment( */ static int fts3WriteSegdir( Fts3Table *p, /* Virtual table handle */ - int iLevel, /* Value for "level" field */ + sqlite3_int64 iLevel, /* Value for "level" field (absolute level) */ int iIdx, /* Value for "idx" field */ sqlite3_int64 iStartBlock, /* Value for "start_block" field */ sqlite3_int64 iLeafEndBlock, /* Value for "leaves_end_block" field */ @@ -1774,7 +1800,7 @@ static int fts3WriteSegdir( sqlite3_stmt *pStmt; int rc = fts3SqlStmt(p, SQL_INSERT_SEGDIR, &pStmt, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pStmt, 1, iLevel); + sqlite3_bind_int64(pStmt, 1, iLevel); sqlite3_bind_int(pStmt, 2, iIdx); sqlite3_bind_int64(pStmt, 3, iStartBlock); sqlite3_bind_int64(pStmt, 4, iLeafEndBlock); @@ -2157,7 +2183,7 @@ static int fts3SegWriterAdd( static int fts3SegWriterFlush( Fts3Table *p, /* Virtual table handle */ SegmentWriter *pWriter, /* SegmentWriter to flush to the db */ - int iLevel, /* Value for 'level' column of %_segdir */ + sqlite3_int64 iLevel, /* Value for 'level' column of %_segdir */ int iIdx /* Value for 'idx' column of %_segdir */ ){ int rc; /* Return code */ @@ -2239,7 +2265,7 @@ static int fts3SegmentMaxLevel( Fts3Table *p, int iLangid, int iIndex, - int *pnMax + sqlite3_int64 *pnMax ){ sqlite3_stmt *pStmt; int rc; @@ -2253,12 +2279,12 @@ static int fts3SegmentMaxLevel( */ rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0); if( rc!=SQLITE_OK ) return rc; - sqlite3_bind_int(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); - sqlite3_bind_int(pStmt, 2, + sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); + sqlite3_bind_int64(pStmt, 2, getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1) ); if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *pnMax = sqlite3_column_int(pStmt, 0); + *pnMax = sqlite3_column_int64(pStmt, 0); } return sqlite3_reset(pStmt); } @@ -2307,15 +2333,17 @@ static int fts3DeleteSegdir( if( iLevel==FTS3_SEGCURSOR_ALL ){ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); - sqlite3_bind_int(pDelete, 2, + sqlite3_bind_int64(pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); + sqlite3_bind_int64(pDelete, 2, getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1) ); } }else{ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex,iLevel)); + sqlite3_bind_int64( + pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, iLevel) + ); } } @@ -2792,7 +2820,7 @@ static int fts3SegmentMerge( ){ int rc; /* Return code */ int iIdx = 0; /* Index of new segment */ - int iNewLevel = 0; /* Level/index to create new segment at */ + sqlite3_int64 iNewLevel = 0; /* Level/index to create new segment at */ SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */ Fts3SegFilter filter; /* Segment term filter condition */ Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */ 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 a8f326be2a..9ee3b96e9c 100644 --- a/src/main.c +++ b/src/main.c @@ -2725,35 +2725,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; @@ -3049,15 +3041,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 f43cfa0c55..edb8ee4d15 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4530,6 +4530,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/test1.c b/src/test1.c index 2e0bd932a2..037888aef9 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/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/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/fts4langid.test b/test/fts4langid.test index 08f1a21c17..8ad06766b7 100644 --- a/test/fts4langid.test +++ b/test/fts4langid.test @@ -382,4 +382,105 @@ do_catchsql_test 4.1.5 { INSERT INTO t4(content, lid) VALUES('hello world', 101) } {1 {SQL logic error or missing database}} +#------------------------------------------------------------------------- +# Test cases 5.* +# +# The following test cases are designed to detect a 32-bit overflow bug +# that existed at one point. +# +proc build_multilingual_db_3 {db} { + $db eval { + CREATE VIRTUAL TABLE t5 USING fts4(languageid=lid); + } + set languages [list 0 1 2 [expr 1<<30]] + + foreach lid $languages { + execsql { + INSERT INTO t5(docid, content, lid) VALUES( + $lid, 'My language is ' || $lid, $lid + ) + } + } +} + +do_test 5.1.0 { + reset_db + build_multilingual_db_3 db +} {} + +do_execsql_test 5.1.1 { + SELECT level FROM t5_segdir; +} [list 0 1024 2048 [expr 1<<40]] + +do_execsql_test 5.1.2 {SELECT docid FROM t5 WHERE t5 MATCH 'language'} 0 +foreach langid [list 0 1 2 [expr 1<<30]] { + do_execsql_test 5.2.$langid { + SELECT docid FROM t5 WHERE t5 MATCH 'language' AND lid = $langid + } $langid +} + +set lid [expr 1<<30] +do_execsql_test 5.3.1 { + CREATE VIRTUAL TABLE t6 USING fts4(languageid=lid); + INSERT INTO t6 VALUES('I belong to language 0!'); +} +do_test 5.3.2 { + for {set i 0} {$i < 20} {incr i} { + execsql { + INSERT INTO t6(content, lid) VALUES( + 'I (row '||$i||') belong to langauge N!', $lid + ); + } + } + execsql { SELECT docid FROM t6 WHERE t6 MATCH 'belong' } +} {1} + +do_test 5.3.3 { + execsql { SELECT docid FROM t6 WHERE t6 MATCH 'belong' AND lid=$lid} +} {2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21} + +do_execsql_test 5.3.4 { INSERT INTO t6(t6) VALUES('optimize') } {} +do_execsql_test 5.3.5 { SELECT docid FROM t6 WHERE t6 MATCH 'belong' } {1} +do_execsql_test 5.3.6 { + SELECT docid FROM t6 WHERE t6 MATCH 'belong' AND lid=$lid +} {2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21} + + +set lid [expr 1<<30] +foreach lid [list 4 [expr 1<<30]] { + do_execsql_test 5.4.$lid.1 { + DELETE FROM t6; + SELECT count(*) FROM t6_segdir; + SELECT count(*) FROM t6_segments; + } {0 0} + do_execsql_test 5.4.$lid.2 { + INSERT INTO t6(content, lid) VALUES('zero zero zero', $lid); + INSERT INTO t6(content, lid) VALUES('zero zero one', $lid); + INSERT INTO t6(content, lid) VALUES('zero one zero', $lid); + INSERT INTO t6(content, lid) VALUES('zero one one', $lid); + INSERT INTO t6(content, lid) VALUES('one zero zero', $lid); + INSERT INTO t6(content, lid) VALUES('one zero one', $lid); + INSERT INTO t6(content, lid) VALUES('one one zero', $lid); + INSERT INTO t6(content, lid) VALUES('one one one', $lid); + + SELECT docid FROM t6 WHERE t6 MATCH '"zero zero"' AND lid=$lid; + } {1 2 5} + + do_execsql_test 5.4.$lid.3 { + SELECT count(*) FROM t6_segdir; + SELECT count(*) FROM t6_segments; + } {8 0} + + do_execsql_test 5.4.$lid.4 { + INSERT INTO t6(t6) VALUES('optimize'); + SELECT docid FROM t6 WHERE t6 MATCH '"zero zero"' AND lid=$lid; + } {1 2 5} + + do_execsql_test 5.4.$lid.5 { + SELECT count(*) FROM t6_segdir; + SELECT count(*) FROM t6_segments; + } {1 0} +} + + 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/pager1.test b/test/pager1.test index d9c13c20c4..24c927a5ed 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -896,6 +896,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. 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