diff --git a/manifest b/manifest index 933db6b26c..eb2854b561 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sfrom\strunk:\s\s(1)\sRecent\sbug\sfixes\s(2)\sSTAT4\ssupport\s(3)\swin32-longpath\nsupport. -D 2013-08-29T15:08:38.537 +C Merge\sin\sall\sthe\slatest\strunk\schanges,\sincluding\sthe\swin32-longpath\sVFS\sand\nthe\sfix\sfor\sthe\ssegfault\sin\sthe\somit-left-join\soptimization. +D 2013-09-03T14:49:07.725 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in aff38bc64c582dd147f18739532198372587b0f0 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -171,7 +171,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083 -F src/analyze.c 7f869a251f3d58d3b95883eeae425d4ee17a66bd +F src/analyze.c 66903e3e5a36eee728c0dc1d574e8cb16059210f F src/attach.c fea00cab11c854646a27641a263f5876569a51f9 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 2f1987981139bd2f6d8c728d64bf09fb387443c3 @@ -199,11 +199,11 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303 -F src/main.c 525985af588234b3d01b48fe856b83d9bdc2d912 +F src/main.c 97cc7970a6010e9c7a3cc96d2845b6be947909b6 F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 437c7c4af964895d4650f29881df63535caaa1fa -F src/mem2.c e243acb034f91e75e4c06aed5d3d960025c62d1f +F src/mem2.c dce31758da87ec2cfa52ba4c5df1aed6e07d8e8f F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 F src/mem5.c 0025308a93838022bd5696cf9627ff4e40b19918 F src/memjournal.c 0683aac6cab6ec2b5374c0db37c0deb2436a3785 @@ -216,8 +216,8 @@ F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 -F src/os_unix.c c27a14a05061e4e690bd3949dc0246bda35e399d -F src/os_win.c 26d752736dff0c7e4e384ab65b353cce1e7e19c5 +F src/os_unix.c 243fb37f47dc072fc59839ea241ff0a17c8d76e6 +F src/os_win.c bb592dfb850e4ee3e4caecce461dca74dc40936d F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8 F src/pager.h f094af9f6ececfaa8a1e93876905a4f34233fb0c F src/parse.y 27c6b4138497d6f8360ba7847da6ed48033f957f @@ -232,7 +232,7 @@ F src/resolve.c 9d53899cc6e1f4ec0b4632d07e97d57827bf63b9 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c 8b148eb851f384412aea57091659d14b369918ca F src/shell.c dbe064d404bb497acd8a44c066cd6b8460a71236 -F src/sqlite.h.in dcca940021f8f6643630fb7711bfd4e3fa002566 +F src/sqlite.h.in d8c9a296c52e115e07eeea9360a43949e2c842fe F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc F src/sqliteInt.h 7806162e2d9796e9d9b6025f0757904b605ff91c @@ -297,20 +297,20 @@ F src/vdbeInt.h 9bf236dc477fb8f1f54b078cf80064663f8a409a F src/vdbeapi.c ae87d198eb3ce67c02886601baee0fd478092076 F src/vdbeaux.c 20ec92cdc43374b6537fbd7bc2cf7a28967bbe44 F src/vdbeblob.c 1268e0bcb8e21fa32520b0fc376e1bcdfaa0c642 -F src/vdbemem.c 4511e1d2304a7d7916d14be20080036331740fcf +F src/vdbemem.c 75ad7fd8f068a8baaba1cf3a84714ef7d3b7a2b8 F src/vdbesort.c 3937e06b2a0e354500e17dc206ef4c35770a5017 F src/vdbetrace.c e7ec40e1999ff3c6414424365d5941178966dcbc F src/vtab.c 165ce0e797c2cd23badb104c9f2ae9042d6d942c F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73 -F src/where.c cad6497f79307c2c94aaf72e64866bb33cf99d59 +F src/where.c 99477e9dd4d4d33be32115b4fbd18b6f033d045a F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783 -F test/alter.test 7e771c3c3f401198557dbbcf4a2c21950ba934f3 +F test/alter.test 775a1dded3f8247983c9a3e767b011d9a5114442 F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d F test/alter4.test 8e93bf7a7e6919b14b0c9a6c1e4908bcf21b0165 @@ -322,7 +322,7 @@ F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4 F test/analyze6.test 19151da2c4e918905d2081b74ac5c4d47fc850ab F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88 -F test/analyze9.test 3095a9ebfea4a2b1f9db60375320ae7f219595ba +F test/analyze9.test 3b23fc97bcc0f4b5629aacdd8e1aa267114dcc79 F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944 F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b @@ -571,7 +571,7 @@ F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891 F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7 F test/fts4merge4.test c19c85ca1faa7b6d536832b49c12e1867235f584 F test/fts4noti.test aed33ba44808852dcb24bf70fa132e7bf530f057 -F test/fts4unicode.test c8ac44217bf6c17812b03eaafa6c06995ad304c2 +F test/fts4unicode.test 5fa8e0a7899d906d114345c605250ebfa9d8ed28 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d F test/func.test cd25cf605c5a345d038dc7b84232204c6a901c84 F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f @@ -730,7 +730,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0 F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54 -F test/permutations.test f71ed33a1e9939ea6e2cde406afedfc46a958088 +F test/permutations.test e47491436e9fc8647535ab92602e01f3012a5b9d F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178 F test/pragma2.test 224f0381f9411a78ae685cac24c13656a62021b7 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 @@ -1056,7 +1056,7 @@ F test/walro.test 6cc247a0cc9b36aeea2057dd28a922a1cdfbd630 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e -F test/where.test da54153a4c1571ea1b95659e5bec8119edf786aa +F test/where.test 8d66dfbfd4d12816f74f854dbf67dee492c06267 F test/where2.test 76d5346f7edb2a6a0442dcf3e9d088ac6903e0b5 F test/where3.test d28c51f257e60be30f74308fa385ceeddfb54a6e F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 @@ -1071,7 +1071,7 @@ F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a F test/whereD.test 6c2feb79ef1f68381b07f39017fe5f9b96da8d62 F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f -F test/whereF.test 136a7301512d72a08a272806c8767066311b7bc1 +F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361 @@ -1094,7 +1094,7 @@ F tool/mkkeywordhash.c bb52064aa614e1426445e4b2b9b00eeecd23cc79 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 8bce31074e4cbe631bb7676526a048335f4c9f02 -F tool/mksqlite3c.tcl 3e55715d165688d969392d792c81f79552049add +F tool/mksqlite3c.tcl 72e4608a3b53994b11cf2d2a6fcd7d868543cd13 F tool/mksqlite3h.tcl 2d0f1b3768f8d000b7881217d5fd4c776eb27467 F tool/mksqlite3internalh.tcl 3dca7bb5374cee003379b8cbac73714f610ef795 F tool/mkvsix.tcl 0be7f7a591f1e83f9199cb82911b66668ca484c9 @@ -1124,7 +1124,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 6cc54de88bba00e07fac9de36caac216d94bf070 d4b6ad3333cc3bad500c2ebf7a6ea552b6762b69 -R d689ec9368fec2d3c01e607f2975aafd +P e7ebc8f74fe91dee26f952fdf49e427b45448667 0303d6bc7112e6f810ae1bd75cafc5ffc51f5212 +R cdd791bbe55c5aec396c46dfbe20e9e5 U drh -Z 0eee6dedcb9d483d45a308318f1cb363 +Z 7030b9cbfaf54b170da506be7cca2cb4 diff --git a/manifest.uuid b/manifest.uuid index bc88aac0d3..66c3a7fdb8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e7ebc8f74fe91dee26f952fdf49e427b45448667 \ No newline at end of file +cdd3838b7876639cf99904b2054884247fbff529 \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index 46ebac4c3a..89dc84f2c1 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -33,7 +33,7 @@ ** version of sqlite_stat3 and is only available when compiled with ** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.0 and later. It is ** not possible to enable both STAT3 and STAT4 at the same time. If they -** are both enabled, then STAT4 is precedence. +** are both enabled, then STAT4 takes precedence. ** ** For most applications, sqlite_stat1 provides all the statisics required ** for the query planner to make good choices. @@ -345,7 +345,7 @@ static void statInit( p->iGet = -1; p->mxSample = mxSample; - p->nPSample = sqlite3_value_int64(argv[1])/(mxSample/3+1) + 1; + p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[1])/(mxSample/3+1) + 1); p->current.anLt = &p->current.anEq[nColUp]; sqlite3_randomness(sizeof(p->iPrn), &p->iPrn); @@ -424,7 +424,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ Stat4Sample *pSample; int i; i64 iSeq; - i64 iPos; + int iPos; assert( IsStat4 || nEqZero==0 ); @@ -468,14 +468,25 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ p->nSample = p->mxSample-1; } - /* Figure out where in the a[] array the new sample should be inserted. */ + /* The "rows less-than" for the rowid column must be greater than that + ** for the last sample in the p->a[] array. Otherwise, the samples would + ** be out of order. */ +#ifdef SQLITE_ENABLE_STAT4 + assert( p->nSample==0 + || pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] ); +#endif + + /* Insert the new sample */ + pSample = &p->a[p->nSample]; + sampleCopy(p, pSample, pNew); + p->nSample++; + +#if 0 iSeq = pNew->anLt[p->nCol-1]; for(iPos=p->nSample; iPos>0; iPos--){ if( iSeq>p->a[iPos-1].anLt[p->nCol-1] ) break; } - /* Insert the new sample */ - pSample = &p->a[iPos]; if( iPos!=p->nSample ){ Stat4Sample *pEnd = &p->a[p->nSample]; tRowcnt *anEq = pEnd->anEq; @@ -486,8 +497,8 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ pSample->anDLt = anDLt; pSample->anLt = anLt; } - p->nSample++; - sampleCopy(p, pSample, pNew); +#endif + /* Zero the first nEqZero entries in the anEq[] array. */ memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero); @@ -584,8 +595,7 @@ static void statPush( assert( iChngnCol ); if( p->nRow==0 ){ - /* anEq[0] is only zero for the very first call to this function. Do - ** appropriate initialization */ + /* This is the first call to this function. Do initialization. */ for(i=0; inCol; i++) p->current.anEq[i] = 1; }else{ /* Second and subsequent calls get processed here */ @@ -1333,7 +1343,7 @@ static void initAvgEq(Index *pIdx){ for(iCol=0; iColnColumn; iCol++){ int i; /* Used to iterate through samples */ tRowcnt sumEq = 0; /* Sum of the nEq values */ - int nSum = 0; /* Number of terms contributing to sumEq */ + tRowcnt nSum = 0; /* Number of terms contributing to sumEq */ tRowcnt avgEq = 0; tRowcnt nDLt = pFinal->anDLt[iCol]; diff --git a/src/main.c b/src/main.c index 1f01853c60..0ddd700dec 100644 --- a/src/main.c +++ b/src/main.c @@ -1107,6 +1107,7 @@ const char *sqlite3ErrName(int rc){ case SQLITE_IOERR_DELETE_NOENT: zName = "SQLITE_IOERR_DELETE_NOENT";break; case SQLITE_IOERR_MMAP: zName = "SQLITE_IOERR_MMAP"; break; case SQLITE_IOERR_GETTEMPPATH: zName = "SQLITE_IOERR_GETTEMPPATH"; break; + case SQLITE_IOERR_CONVPATH: zName = "SQLITE_IOERR_CONVPATH"; break; case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break; case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break; @@ -1115,6 +1116,7 @@ const char *sqlite3ErrName(int rc){ case SQLITE_CANTOPEN_NOTEMPDIR: zName = "SQLITE_CANTOPEN_NOTEMPDIR";break; case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break; case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break; + case SQLITE_CANTOPEN_CONVPATH: zName = "SQLITE_CANTOPEN_CONVPATH"; break; case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; diff --git a/src/mem2.c b/src/mem2.c index d461dffab1..99ea42517e 100644 --- a/src/mem2.c +++ b/src/mem2.c @@ -348,7 +348,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){ pOldHdr = sqlite3MemsysGetHeader(pPrior); pNew = sqlite3MemMalloc(nByte); if( pNew ){ - memcpy(pNew, pPrior, nByteiSize ? nByte : pOldHdr->iSize); + memcpy(pNew, pPrior, (int)(nByteiSize ? nByte : pOldHdr->iSize)); if( nByte>pOldHdr->iSize ){ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize); } diff --git a/src/os_unix.c b/src/os_unix.c index 580f8e60e7..cd8cec0046 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -551,6 +551,15 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){ return 0; } +/* +** Do not accept any file descriptor less than this value, in order to avoid +** opening database file using file descriptors that are commonly used for +** standard input, output, and error. +*/ +#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR +# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3 +#endif + /* ** Invoke open(). Do so multiple times, until it either succeeds or ** fails for some reason other than EINTR. @@ -571,13 +580,23 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){ static int robust_open(const char *z, int f, mode_t m){ int fd; mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; - do{ + while(1){ #if defined(O_CLOEXEC) fd = osOpen(z,f|O_CLOEXEC,m2); #else fd = osOpen(z,f,m2); #endif - }while( fd<0 && errno==EINTR ); + if( fd<0 ){ + if( errno==EINTR ) continue; + break; + } + if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; + osClose(fd); + sqlite3_log(SQLITE_WARNING, + "attempt to open \"%s\" as file descriptor %d", z, fd); + fd = -1; + if( osOpen("/dev/null", f, m)<0 ) break; + } if( fd>=0 ){ if( m!=0 ){ struct stat statbuf; @@ -3099,6 +3118,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ #endif TIMER_START; assert( cnt==(cnt&0x1ffff) ); + assert( id->h>2 ); cnt &= 0x1ffff; do{ #if defined(USE_PREAD) @@ -3213,6 +3233,7 @@ static int seekAndWriteFd( int rc = 0; /* Value returned by system call */ assert( nBuf==(nBuf&0x1ffff) ); + assert( fd>2 ); nBuf &= 0x1ffff; TIMER_START; @@ -5317,6 +5338,7 @@ static int fillInUnixFile( */ static const char *unixTempFileDir(void){ static const char *azDirs[] = { + 0, 0, 0, "/var/tmp", @@ -5329,7 +5351,8 @@ static const char *unixTempFileDir(void){ const char *zDir = 0; azDirs[0] = sqlite3_temp_directory; - if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); + if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR"); + if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR"); for(i=0; i -# include +# include /* amalgamator: keep */ #endif /* @@ -38,7 +38,7 @@ ** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions ** based on the sub-platform)? */ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI) # define SQLITE_WIN32_HAS_ANSI #endif @@ -46,10 +46,19 @@ ** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions ** based on the sub-platform)? */ -#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT +#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \ + !defined(SQLITE_WIN32_NO_WIDE) # define SQLITE_WIN32_HAS_WIDE #endif +/* +** Make sure at least one set of Win32 APIs is available. +*/ +#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE) +# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\ + must be defined." +#endif + /* ** Maximum pathname length (in chars) for Win32. This should normally be ** MAX_PATH. @@ -161,7 +170,7 @@ WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); #endif #ifndef SQLITE_OMIT_WAL -/* Forward references */ +/* Forward references to structures used for WAL */ typedef struct winShm winShm; /* A connection to shared-memory */ typedef struct winShmNode winShmNode; /* A region of shared-memory */ #endif @@ -1116,7 +1125,7 @@ void sqlite3_win32_sleep(DWORD milliseconds){ ** WinNT/2K/XP so that we will know whether or not we can safely call ** the LockFileEx() API. */ -#if SQLITE_OS_WINCE || SQLITE_OS_WINRT +#if SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI) # define osIsNT() (1) #elif !defined(SQLITE_WIN32_HAS_WIDE) # define osIsNT() (0) @@ -1761,10 +1770,9 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){ pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); if (!pFile->hMutex){ pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_IOERR, pFile->lastErrno, - "winceCreateLock1", zFilename); sqlite3_free(zName); - return SQLITE_IOERR; + return winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock1", zFilename); } /* Acquire the mutex before continuing */ @@ -2100,7 +2108,7 @@ static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ pFile->lastErrno = lastErrno; winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "winSeekFile", pFile->zPath); + "winSeekFile", pFile->zPath); OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); return 1; } @@ -2121,7 +2129,7 @@ static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ if(!bRet){ pFile->lastErrno = osGetLastError(); winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "winSeekFile", pFile->zPath); + "winSeekFile", pFile->zPath); OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); return 1; } @@ -2132,7 +2140,8 @@ static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ } #if SQLITE_MAX_MMAP_SIZE>0 -/* Forward references to VFS methods */ +/* Forward references to VFS helper methods used for memory mapped files */ +static int winMapfile(winFile*, sqlite3_int64); static int winUnmapfile(winFile*); #endif @@ -2253,7 +2262,7 @@ static int winRead( pFile->lastErrno = lastErrno; OSTRACE(("READ file=%p, rc=SQLITE_IOERR_READ\n", pFile->h)); return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, - "winRead", pFile->zPath); + "winRead", pFile->zPath); } winLogIoerr(nRetry); if( nRead<(DWORD)amt ){ @@ -2359,11 +2368,12 @@ static int winWrite( if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) || ( pFile->lastErrno==ERROR_DISK_FULL )){ OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h)); - return SQLITE_FULL; + return winLogError(SQLITE_FULL, pFile->lastErrno, + "winWrite1", pFile->zPath); } OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h)); return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, - "winWrite", pFile->zPath); + "winWrite2", pFile->zPath); }else{ winLogIoerr(nRetry); } @@ -2487,7 +2497,7 @@ static int winSync(sqlite3_file *id, int flags){ pFile->lastErrno = osGetLastError(); OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h)); return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, - "winSync", pFile->zPath); + "winSync", pFile->zPath); } #endif } @@ -2528,7 +2538,7 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ && ((lastErrno = osGetLastError())!=NO_ERROR) ){ pFile->lastErrno = lastErrno; rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, - "winFileSize", pFile->zPath); + "winFileSize", pFile->zPath); } } #endif @@ -2623,7 +2633,7 @@ static int winUnlockReadLock(winFile *pFile){ if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ pFile->lastErrno = lastErrno; winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, - "winUnlockReadLock", pFile->zPath); + "winUnlockReadLock", pFile->zPath); } OSTRACE(("READ-UNLOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res))); return res; @@ -2836,7 +2846,7 @@ static int winUnlock(sqlite3_file *id, int locktype){ /* This should never happen. We should always be able to ** reacquire the read lock */ rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), - "winUnlock", pFile->zPath); + "winUnlock", pFile->zPath); } } if( type>=RESERVED_LOCK ){ @@ -2870,11 +2880,10 @@ static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ } } -/* Forward declaration */ +/* Forward references to VFS helper methods used for temporary files */ static int winGetTempname(sqlite3_vfs *, char **); -#if SQLITE_MAX_MMAP_SIZE>0 -static int winMapfile(winFile*, sqlite3_int64); -#endif +static int winIsDir(const void *); +static BOOL winIsDriveLetterAndColon(const char *); /* ** Control and query of the open file handle. @@ -2952,7 +2961,7 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ if( rc==SQLITE_OK ){ *(char**)pArg = zTFile; } - OSTRACE(("FCNTL file=%p, rc=%d\n", pFile->h, rc)); + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); return rc; } #if SQLITE_MAX_MMAP_SIZE>0 @@ -2970,7 +2979,7 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ rc = winMapfile(pFile, -1); } } - OSTRACE(("FCNTL file=%p, rc=%d\n", pFile->h, rc)); + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); return rc; } #endif @@ -3286,7 +3295,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); if( rc!=SQLITE_OK ){ rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), - "winOpenShm", pDbFd->zPath); + "winOpenShm", pDbFd->zPath); } } if( rc==SQLITE_OK ){ @@ -3546,7 +3555,7 @@ static int winShmMap( rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); if( rc!=SQLITE_OK ){ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), - "winShmMap1", pDbFd->zPath); + "winShmMap1", pDbFd->zPath); goto shmpage_out; } @@ -3561,7 +3570,7 @@ static int winShmMap( rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); if( rc!=SQLITE_OK ){ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), - "winShmMap2", pDbFd->zPath); + "winShmMap2", pDbFd->zPath); goto shmpage_out; } } @@ -3615,7 +3624,7 @@ static int winShmMap( if( !pMap ){ pShmNode->lastErrno = osGetLastError(); rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno, - "winShmMap3", pDbFd->zPath); + "winShmMap3", pDbFd->zPath); if( hMap ) osCloseHandle(hMap); goto shmpage_out; } @@ -3663,7 +3672,7 @@ static int winUnmapfile(winFile *pFile){ "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile, pFile->pMapRegion)); return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, - "winUnmap1", pFile->zPath); + "winUnmapfile1", pFile->zPath); } pFile->pMapRegion = 0; pFile->mmapSize = 0; @@ -3675,7 +3684,7 @@ static int winUnmapfile(winFile *pFile){ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile, pFile->hMap)); return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, - "winUnmap2", pFile->zPath); + "winUnmapfile2", pFile->zPath); } pFile->hMap = NULL; } @@ -3750,10 +3759,10 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ if( pFd->hMap==NULL ){ pFd->lastErrno = osGetLastError(); rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, - "winMapfile", pFd->zPath); + "winMapfile1", pFd->zPath); /* Log the error, but continue normal operation using xRead/xWrite */ - OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n", - osGetCurrentProcessId(), pFd)); + OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n", + osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); return SQLITE_OK; } assert( (nMap % winSysInfo.dwPageSize)==0 ); @@ -3767,10 +3776,11 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ osCloseHandle(pFd->hMap); pFd->hMap = NULL; pFd->lastErrno = osGetLastError(); - winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, - "winMapfile", pFd->zPath); - OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n", - osGetCurrentProcessId(), pFd)); + rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, + "winMapfile2", pFd->zPath); + /* Log the error, but continue normal operation using xRead/xWrite */ + OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n", + osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); return SQLITE_OK; } pFd->pMapRegion = pNew; @@ -3909,13 +3919,32 @@ static const sqlite3_io_methods winIoMethod = { ** sqlite3_vfs object. */ +/* +** Convert a filename from whatever the underlying operating system +** supports for filenames into UTF-8. Space to hold the result is +** obtained from malloc and must be freed by the calling function. +*/ +static char *winConvertToUtf8Filename(const void *zFilename){ + char *zConverted = 0; + if( osIsNT() ){ + zConverted = winUnicodeToUtf8(zFilename); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + zConverted = sqlite3_win32_mbcs_to_utf8(zFilename); + } +#endif + /* caller will handle out of memory */ + return zConverted; +} + /* ** Convert a UTF-8 filename into whatever form the underlying ** operating system wants filenames in. Space to hold the result ** is obtained from malloc and must be freed by the calling ** function. */ -static void *winConvertUtf8Filename(const char *zFilename){ +static void *winConvertFromUtf8Filename(const char *zFilename){ void *zConverted = 0; if( osIsNT() ){ zConverted = winUtf8ToUnicode(zFilename); @@ -3980,7 +4009,90 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ winEndsInDirSep(sqlite3_temp_directory) ? "" : winGetDirDep()); } -#if !SQLITE_OS_WINRT +#if defined(__CYGWIN__) + else{ + static const char *azDirs[] = { + 0, /* getenv("SQLITE_TMPDIR") */ + 0, /* getenv("TMPDIR") */ + 0, /* getenv("TMP") */ + 0, /* getenv("TEMP") */ + 0, /* getenv("USERPROFILE") */ + "/var/tmp", + "/usr/tmp", + "/tmp", + ".", + 0 /* List terminator */ + }; + unsigned int i; + const char *zDir = 0; + + if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); + if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); + if( !azDirs[2] ) azDirs[2] = getenv("TMP"); + if( !azDirs[3] ) azDirs[3] = getenv("TEMP"); + if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE"); + for(i=0; i= nBuf ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); - return SQLITE_ERROR; + return winLogError(SQLITE_ERROR, 0, "winGetTempname3", 0); } sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX); @@ -4198,7 +4312,7 @@ static int winOpen( zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 ); /* Convert the filename to the system encoding. */ - zConverted = winConvertUtf8Filename(zUtf8Name); + zConverted = winConvertFromUtf8Filename(zUtf8Name); if( zConverted==0 ){ sqlite3_free(zTmpname); OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name)); @@ -4399,7 +4513,7 @@ static int winDelete( SimulateIOError(return SQLITE_IOERR_DELETE); OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir)); - zConverted = winConvertUtf8Filename(zFilename); + zConverted = winConvertFromUtf8Filename(zFilename); if( zConverted==0 ){ OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); return SQLITE_IOERR_NOMEM; @@ -4479,8 +4593,7 @@ static int winDelete( } #endif if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ - rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, - "winDelete", zFilename); + rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); }else{ winLogIoerr(cnt); } @@ -4508,7 +4621,7 @@ static int winAccess( OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", zFilename, flags, pResOut)); - zConverted = winConvertUtf8Filename(zFilename); + zConverted = winConvertFromUtf8Filename(zFilename); if( zConverted==0 ){ OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); return SQLITE_IOERR_NOMEM; @@ -4534,9 +4647,9 @@ static int winAccess( }else{ winLogIoerr(cnt); if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ - winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename); sqlite3_free(zConverted); - return SQLITE_IOERR_ACCESS; + return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", + zFilename); }else{ attr = INVALID_FILE_ATTRIBUTES; } @@ -4566,6 +4679,15 @@ static int winAccess( return SQLITE_OK; } +/* +** Returns non-zero if the specified path name starts with a drive letter +** followed by a colon character. +*/ +static BOOL winIsDriveLetterAndColon( + const char *zPathname +){ + return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ); +} /* ** Returns non-zero if the specified path name should be used verbatim. If @@ -4593,7 +4715,7 @@ static BOOL winIsVerbatimPathname( ** attempt to treat it as a relative path name (i.e. they should simply use ** it verbatim). */ - if ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ){ + if ( winIsDriveLetterAndColon(zPathname) ){ return TRUE; } @@ -4629,24 +4751,21 @@ static int winFullPathname( */ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); if( !zOut ){ - winLogError(SQLITE_IOERR_NOMEM, 0, "winFullPathname", zRelative); return SQLITE_IOERR_NOMEM; } if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ - winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path", - zRelative); sqlite3_free(zOut); - return SQLITE_CANTOPEN_FULLPATH; + return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, + "winFullPathname1", zRelative); } sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s", sqlite3_data_directory, winGetDirDep(), zOut); sqlite3_free(zOut); }else{ if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){ - winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path", - zRelative); - return SQLITE_CANTOPEN_FULLPATH; + return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, + "winFullPathname2", zRelative); } } return SQLITE_OK; @@ -4679,7 +4798,7 @@ static int winFullPathname( /* If this path name begins with "/X:", where "X" is any alphabetic ** character, discard the initial "/" from the pathname. */ - if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){ + if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){ zRelative++; } @@ -4700,7 +4819,7 @@ static int winFullPathname( sqlite3_data_directory, winGetDirDep(), zRelative); return SQLITE_OK; } - zConverted = winConvertUtf8Filename(zRelative); + zConverted = winConvertFromUtf8Filename(zRelative); if( zConverted==0 ){ return SQLITE_IOERR_NOMEM; } @@ -4708,10 +4827,9 @@ static int winFullPathname( LPWSTR zTemp; nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0); if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameW1", zConverted); sqlite3_free(zConverted); - return SQLITE_CANTOPEN_FULLPATH; + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname1", zRelative); } nByte += 3; zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); @@ -4721,11 +4839,10 @@ static int winFullPathname( } nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0); if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameW2", zConverted); sqlite3_free(zConverted); sqlite3_free(zTemp); - return SQLITE_CANTOPEN_FULLPATH; + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname2", zRelative); } sqlite3_free(zConverted); zOut = winUnicodeToUtf8(zTemp); @@ -4736,10 +4853,9 @@ static int winFullPathname( char *zTemp; nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0); if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameA1", zConverted); sqlite3_free(zConverted); - return SQLITE_CANTOPEN_FULLPATH; + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname3", zRelative); } nByte += 3; zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); @@ -4749,11 +4865,10 @@ static int winFullPathname( } nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameA2", zConverted); sqlite3_free(zConverted); sqlite3_free(zTemp); - return SQLITE_CANTOPEN_FULLPATH; + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname4", zRelative); } sqlite3_free(zConverted); zOut = sqlite3_win32_mbcs_to_utf8(zTemp); @@ -4781,7 +4896,7 @@ static int winFullPathname( */ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ HANDLE h; - void *zConverted = winConvertUtf8Filename(zFilename); + void *zConverted = winConvertFromUtf8Filename(zFilename); UNUSED_PARAMETER(pVfs); if( zConverted==0 ){ return 0; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 08aae5494a..20f8d650ec 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -474,12 +474,14 @@ int sqlite3_exec( #define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) #define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8)) #define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8)) +#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) +#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) diff --git a/src/vdbemem.c b/src/vdbemem.c index e6ad291532..dbdfc53074 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1311,6 +1311,9 @@ int sqlite3Stat4ProbeSetValue( alloc.ppRec = ppRec; alloc.iVal = iVal; + /* Skip over any TK_COLLATE nodes */ + pExpr = sqlite3ExprSkipCollate(pExpr); + if( !pExpr ){ pVal = valueNew(pParse->db, &alloc); if( pVal ){ diff --git a/src/where.c b/src/where.c index 9e3a72e2d8..b633dfd574 100644 --- a/src/where.c +++ b/src/where.c @@ -185,9 +185,9 @@ static int whereLoopResize(sqlite3*, WhereLoop*, int); ** that implement some or all of a query plan. ** ** Think of each WhereLoop object as a node in a graph with arcs -** showing dependences and costs for travelling between nodes. (That is +** showing dependencies and costs for travelling between nodes. (That is ** not a completely accurate description because WhereLoop costs are a -** vector, not a scalar, and because dependences are many-to-one, not +** vector, not a scalar, and because dependencies are many-to-one, not ** one-to-one as are graph nodes. But it is a useful visualization aid.) ** Then a WherePath object is a path through the graph that visits some ** or all of the WhereLoop objects once. @@ -2553,7 +2553,12 @@ static int whereRangeScanEst( ){ UnpackedRecord *pRec = pBuilder->pRec; tRowcnt a[2]; - u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; + u8 aff; + if( nEq==p->nColumn ){ + aff = SQLITE_AFF_INTEGER; + }else{ + aff = p->pTable->aCol[p->aiColumn[nEq]].affinity; + } /* Variable iLower will be set to the estimate of the number of rows in ** the index that are less than the lower bound of the range query. The @@ -4169,15 +4174,17 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ if( (p->prereq & pTemplate->prereq)==p->prereq && p->rSetup<=pTemplate->rSetup && p->rRun<=pTemplate->rRun + && p->nOut<=pTemplate->nOut ){ /* This branch taken when p is equal or better than pTemplate in - ** all of (1) dependences (2) setup-cost, and (3) run-cost. */ + ** all of (1) dependencies (2) setup-cost, (3) run-cost, and + ** (4) number of output rows. */ assert( p->rSetup==pTemplate->rSetup ); - if( p->nLTermnLTerm + if( p->prereq==pTemplate->prereq + && p->nLTermnLTerm && (p->wsFlags & WHERE_INDEXED)!=0 && (pTemplate->wsFlags & WHERE_INDEXED)!=0 && p->u.btree.pIndex==pTemplate->u.btree.pIndex - && p->prereq==pTemplate->prereq ){ /* Overwrite an existing WhereLoop with an similar one that uses ** more terms of the index */ @@ -4191,11 +4198,13 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ } if( (p->prereq & pTemplate->prereq)==pTemplate->prereq && p->rRun>=pTemplate->rRun + && p->nOut>=pTemplate->nOut && ALWAYS(p->rSetup>=pTemplate->rSetup) /* See SETUP-INVARIANT above */ ){ /* Overwrite an existing WhereLoop with a better one: one that is - ** better at one of (1) dependences, (2) setup-cost, or (3) run-cost - ** and is no worse in any of those categories. */ + ** better at one of (1) dependencies, (2) setup-cost, (3) run-cost + ** or (4) number of output rows, and is no worse in any of those + ** categories. */ pNext = p->pNextLoop; break; } @@ -5900,7 +5909,7 @@ WhereInfo *sqlite3WhereBegin( && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ){ Bitmask tabUsed = exprListTableUsage(pMaskSet, pResultSet); - if( pOrderBy ) tabUsed |= exprListTableUsage(pMaskSet, pOrderBy); + if( sWLB.pOrderBy ) tabUsed |= exprListTableUsage(pMaskSet, sWLB.pOrderBy); while( pWInfo->nLevel>=2 ){ WhereTerm *pTerm, *pEnd; pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop; diff --git a/test/alter.test b/test/alter.test index faa6b0d258..1ccea22624 100644 --- a/test/alter.test +++ b/test/alter.test @@ -847,6 +847,7 @@ do_test alter-14.2 { set system_table_list {1 sqlite_master} catchsql ANALYZE ifcapable analyze { lappend system_table_list 2 sqlite_stat1 } +ifcapable stat3 { lappend system_table_list 3 sqlite_stat3 } ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 } foreach {tn tbl} $system_table_list { diff --git a/test/analyze9.test b/test/analyze9.test index be52eb94b2..777e462fdb 100644 --- a/test/analyze9.test +++ b/test/analyze9.test @@ -422,5 +422,499 @@ do_eqp_test 9.4.2 { SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5 } {/t1 USING INDEX i1/} +#------------------------------------------------------------------------- +# Check that the planner takes stat4 data into account when considering +# "IS NULL" and "IS NOT NULL" constraints. +# +do_execsql_test 10.1.1 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a, b); + CREATE INDEX t3a ON t3(a); + CREATE INDEX t3b ON t3(b); +} +do_test 10.1.2 { + for {set i 1} {$i < 100} {incr i} { + if {$i>90} { set a $i } else { set a NULL } + set b [expr $i % 5] + execsql "INSERT INTO t3 VALUES($a, $b)" + } + execsql ANALYZE +} {} +do_eqp_test 10.1.3 { + SELECT * FROM t3 WHERE a IS NULL AND b = 2 +} {/t3 USING INDEX t3b/} +do_eqp_test 10.1.4 { + SELECT * FROM t3 WHERE a IS NOT NULL AND b = 2 +} {/t3 USING INDEX t3a/} + +do_execsql_test 10.2.1 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(x, a, b); + CREATE INDEX t3a ON t3(x, a); + CREATE INDEX t3b ON t3(x, b); +} +do_test 10.2.2 { + for {set i 1} {$i < 100} {incr i} { + if {$i>90} { set a $i } else { set a NULL } + set b [expr $i % 5] + execsql "INSERT INTO t3 VALUES('xyz', $a, $b)" + } + execsql ANALYZE +} {} +do_eqp_test 10.2.3 { + SELECT * FROM t3 WHERE x = 'xyz' AND a IS NULL AND b = 2 +} {/t3 USING INDEX t3b/} +do_eqp_test 10.2.4 { + SELECT * FROM t3 WHERE x = 'xyz' AND a IS NOT NULL AND b = 2 +} {/t3 USING INDEX t3a/} + +#------------------------------------------------------------------------- +# Check that stat4 data is used correctly with non-default collation +# sequences. +# +foreach {tn schema} { + 1 { + CREATE TABLE t4(a COLLATE nocase, b); + CREATE INDEX t4a ON t4(a); + CREATE INDEX t4b ON t4(b); + } + 2 { + CREATE TABLE t4(a, b); + CREATE INDEX t4a ON t4(a COLLATE nocase); + CREATE INDEX t4b ON t4(b); + } +} { + drop_all_tables + do_test 11.$tn.1 { execsql $schema } {} + + do_test 11.$tn.2 { + for {set i 0} {$i < 100} {incr i} { + if { ($i % 10)==0 } { set a ABC } else { set a DEF } + set b [expr $i % 5] + execsql { INSERT INTO t4 VALUES($a, $b) } + } + execsql ANALYZE + } {} + + do_eqp_test 11.$tn.3 { + SELECT * FROM t4 WHERE a = 'def' AND b = 3; + } {/t4 USING INDEX t4b/} + + if {$tn==1} { + set sql "SELECT * FROM t4 WHERE a = 'abc' AND b = 3;" + do_eqp_test 11.$tn.4 $sql {/t4 USING INDEX t4a/} + } else { + + set sql "SELECT * FROM t4 WHERE a = 'abc' COLLATE nocase AND b = 3;" + do_eqp_test 11.$tn.5 $sql {/t4 USING INDEX t4a/} + + set sql "SELECT * FROM t4 WHERE a COLLATE nocase = 'abc' AND b = 3;" + do_eqp_test 11.$tn.6 $sql {/t4 USING INDEX t4a/} + } +} + +foreach {tn schema} { + 1 { + CREATE TABLE t4(x, a COLLATE nocase, b); + CREATE INDEX t4a ON t4(x, a); + CREATE INDEX t4b ON t4(x, b); + } + 2 { + CREATE TABLE t4(x, a, b); + CREATE INDEX t4a ON t4(x, a COLLATE nocase); + CREATE INDEX t4b ON t4(x, b); + } +} { + drop_all_tables + do_test 12.$tn.1 { execsql $schema } {} + + do_test 12.$tn.2 { + for {set i 0} {$i < 100} {incr i} { + if { ($i % 10)==0 } { set a ABC } else { set a DEF } + set b [expr $i % 5] + execsql { INSERT INTO t4 VALUES(X'abcdef', $a, $b) } + } + execsql ANALYZE + } {} + + do_eqp_test 12.$tn.3 { + SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'def' AND b = 3; + } {/t4 USING INDEX t4b/} + + if {$tn==1} { + set sql "SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'abc' AND b = 3;" + do_eqp_test 12.$tn.4 $sql {/t4 USING INDEX t4a/} + } else { + set sql { + SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'abc' COLLATE nocase AND b = 3 + } + do_eqp_test 12.$tn.5 $sql {/t4 USING INDEX t4a/} + set sql { + SELECT * FROM t4 WHERE x=X'abcdef' AND a COLLATE nocase = 'abc' AND b = 3 + } + do_eqp_test 12.$tn.6 $sql {/t4 USING INDEX t4a/} + } +} + +#------------------------------------------------------------------------- +# Check that affinities are taken into account when using stat4 data to +# estimate the number of rows scanned by a rowid constraint. +# +drop_all_tables +do_test 13.1 { + execsql { + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(a); + CREATE INDEX i2 ON t1(b, c); + } + for {set i 0} {$i<100} {incr i} { + if {$i %2} {set a abc} else {set a def} + execsql { INSERT INTO t1(rowid, a, b, c) VALUES($i, $a, $i, $i) } + } + execsql ANALYZE +} {} +do_eqp_test 13.2.1 { + SELECT * FROM t1 WHERE a='abc' AND rowid<15 AND b<20 +} {/SEARCH TABLE t1 USING INDEX i1/} +do_eqp_test 13.2.2 { + SELECT * FROM t1 WHERE a='abc' AND rowid<'15' AND b<20 +} {/SEARCH TABLE t1 USING INDEX i1/} +do_eqp_test 13.3.1 { + SELECT * FROM t1 WHERE a='abc' AND rowid<100 AND b<20 +} {/SEARCH TABLE t1 USING INDEX i2/} +do_eqp_test 13.3.2 { + SELECT * FROM t1 WHERE a='abc' AND rowid<'100' AND b<20 +} {/SEARCH TABLE t1 USING INDEX i2/} + +#------------------------------------------------------------------------- +# Check also that affinities are taken into account when using stat4 data +# to estimate the number of rows scanned by any other constraint on a +# column other than the leftmost. +# +drop_all_tables +do_test 14.1 { + execsql { CREATE TABLE t1(a, b INTEGER, c) } + for {set i 0} {$i<100} {incr i} { + set c [expr $i % 3] + execsql { INSERT INTO t1 VALUES('ott', $i, $c) } + } + execsql { + CREATE INDEX i1 ON t1(a, b); + CREATE INDEX i2 ON t1(c); + ANALYZE; + } +} {} +do_eqp_test 13.2.1 { + SELECT * FROM t1 WHERE a='ott' AND b<10 AND c=1 +} {/SEARCH TABLE t1 USING INDEX i1/} +do_eqp_test 13.2.2 { + SELECT * FROM t1 WHERE a='ott' AND b<'10' AND c=1 +} {/SEARCH TABLE t1 USING INDEX i1/} + +#------------------------------------------------------------------------- +# By default, 16 non-periodic samples are collected for the stat4 table. +# The following tests attempt to verify that the most common keys are +# being collected. +# +proc check_stat4 {tn} { + db eval ANALYZE + db eval {SELECT a, b, c, d FROM t1} { + incr k($a) + incr k([list $a $b]) + incr k([list $a $b $c]) + if { [info exists k([list $a $b $c $d])]==0 } { incr nRow } + incr k([list $a $b $c $d]) + } + + set L [list] + foreach key [array names k] { + lappend L [list $k($key) $key] + } + + set nSample $nRow + if {$nSample>16} {set nSample 16} + + set nThreshold [lindex [lsort -decr -integer -index 0 $L] [expr $nSample-1] 0] + foreach key [array names k] { + if {$k($key)>$nThreshold} { + set expect($key) 1 + } + if {$k($key)==$nThreshold} { + set possible($key) 1 + } + } + + + set nPossible [expr $nSample - [llength [array names expect]]] + + #puts "EXPECT: [array names expect]" + #puts "POSSIBLE($nPossible/[array size possible]): [array names possible]" + #puts "HAVE: [db eval {SELECT test_decode(sample) FROM sqlite_stat4 WHERE idx='i1'}]" + + db eval {SELECT test_decode(sample) AS s FROM sqlite_stat4 WHERE idx='i1'} { + set seen 0 + for {set i 0} {$i<4} {incr i} { + unset -nocomplain expect([lrange $s 0 $i]) + if {[info exists possible([lrange $s 0 $i])]} { + set seen 1 + unset -nocomplain possible([lrange $s 0 $i]) + } + } + if {$seen} {incr nPossible -1} + } + if {$nPossible<0} {set nPossible 0} + + set res [list [llength [array names expect]] $nPossible] + uplevel [list do_test $tn [list set {} $res] {0 0}] +} + +drop_all_tables +do_test 14.1.1 { + execsql { + CREATE TABLE t1(a,b,c,d); + CREATE INDEX i1 ON t1(a,b,c,d); + } + for {set i 0} {$i < 160} {incr i} { + execsql { INSERT INTO t1 VALUES($i,$i,$i,$i) } + if {($i % 10)==0} { execsql { INSERT INTO t1 VALUES($i,$i,$i,$i) } } + } +} {} +check_stat4 14.1.2 + +do_test 14.2.1 { + execsql { DELETE FROM t1 } + for {set i 0} {$i < 1600} {incr i} { + execsql { INSERT INTO t1 VALUES($i/10,$i/17,$i/27,$i/37) } + } +} {} +check_stat4 14.2.2 + +do_test 14.3.1 { + for {set i 0} {$i < 10} {incr i} { + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } + } +} {} +check_stat4 14.3.2 + +do_test 14.4.1 { + execsql {DELETE FROM t1} + for {set i 1} {$i < 160} {incr i} { + set b [expr $i % 10] + if {$b==0 || $b==2} {set b 1} + execsql { INSERT INTO t1 VALUES($i/10,$b,$i,$i) } + } +} {} +check_stat4 14.4.2 +db func lrange lrange +db func lindex lindex +do_execsql_test 14.4.3 { + SELECT lrange(test_decode(sample), 0, 1) AS s FROM sqlite_stat4 + WHERE lindex(s, 1)=='1' ORDER BY rowid +} { + {0 1} {1 1} {2 1} {3 1} + {4 1} {5 1} {6 1} {7 1} + {8 1} {9 1} {10 1} {11 1} + {12 1} {13 1} {14 1} {15 1} +} + +#------------------------------------------------------------------------- +# Test that nothing untoward happens if the stat4 table contains entries +# for indexes that do not exist. Or NULL values in the idx column. +# Or NULL values in any of the other columns. +# +drop_all_tables +do_execsql_test 15.1 { + CREATE TABLE x1(a, b, UNIQUE(a, b)); + INSERT INTO x1 VALUES(1, 2); + INSERT INTO x1 VALUES(3, 4); + INSERT INTO x1 VALUES(5, 6); + ANALYZE; + INSERT INTO sqlite_stat4 VALUES(NULL, NULL, NULL, NULL, NULL, NULL); +} +db close +sqlite3 db test.db +do_execsql_test 15.2 { SELECT * FROM x1 } {1 2 3 4 5 6} + +do_execsql_test 15.3 { + INSERT INTO sqlite_stat4 VALUES(42, 42, 42, 42, 42, 42); +} +db close +sqlite3 db test.db +do_execsql_test 15.4 { SELECT * FROM x1 } {1 2 3 4 5 6} + +do_execsql_test 15.5 { + UPDATE sqlite_stat1 SET stat = NULL; +} +db close +sqlite3 db test.db +do_execsql_test 15.6 { SELECT * FROM x1 } {1 2 3 4 5 6} + +do_execsql_test 15.7 { + ANALYZE; + UPDATE sqlite_stat1 SET tbl = 'no such tbl'; +} +db close +sqlite3 db test.db +do_execsql_test 15.8 { SELECT * FROM x1 } {1 2 3 4 5 6} + +do_execsql_test 15.9 { + ANALYZE; + UPDATE sqlite_stat4 SET neq = NULL, nlt=NULL, ndlt=NULL; +} +db close +sqlite3 db test.db +do_execsql_test 15.10 { SELECT * FROM x1 } {1 2 3 4 5 6} + +# This is just for coverage.... +do_execsql_test 15.11 { + ANALYZE; + UPDATE sqlite_stat1 SET stat = stat || ' unordered'; +} +db close +sqlite3 db test.db +do_execsql_test 15.12 { SELECT * FROM x1 } {1 2 3 4 5 6} + +#------------------------------------------------------------------------- +# Test that allocations used for sqlite_stat4 samples are included in +# the quantity returned by SQLITE_DBSTATUS_SCHEMA_USED. +# +set one [string repeat x 1000] +set two [string repeat x 2000] +do_test 16.1 { + reset_db + execsql { + CREATE TABLE t1(a, UNIQUE(a)); + INSERT INTO t1 VALUES($one); + ANALYZE; + } + set nByte [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] + + reset_db + execsql { + CREATE TABLE t1(a, UNIQUE(a)); + INSERT INTO t1 VALUES($two); + ANALYZE; + } + set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] + + expr {$nByte2 > $nByte+950 && $nByte2 < $nByte+1050} +} {1} + +#------------------------------------------------------------------------- +# Test that stat4 data may be used with partial indexes. +# +do_test 17.1 { + reset_db + execsql { + CREATE TABLE t1(a, b, c, d); + CREATE INDEX i1 ON t1(a, b) WHERE d IS NOT NULL; + INSERT INTO t1 VALUES(-1, -1, -1, NULL); + INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; + INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; + INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; + INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; + INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; + INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; + } + + for {set i 0} {$i < 32} {incr i} { + if {$i<8} {set b 0} else { set b $i } + execsql { INSERT INTO t1 VALUES($i%2, $b, $i/2, 'abc') } + } + execsql {ANALYZE main.t1} +} {} + +do_catchsql_test 17.1.2 { + ANALYZE temp.t1; +} {1 {no such table: temp.t1}} + +do_eqp_test 17.2 { + SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=10 AND c=10; +} {/USING INDEX i1/} +do_eqp_test 17.3 { + SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=0 AND c=10; +} {/USING INDEX i1/} + +do_execsql_test 17.4 { + CREATE INDEX i2 ON t1(c); + ANALYZE main.i2; +} +do_eqp_test 17.5 { + SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=10 AND c=10; +} {/USING INDEX i1/} +do_eqp_test 17.6 { + SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=0 AND c=10; +} {/USING INDEX i2/} + +#------------------------------------------------------------------------- +# +do_test 18.1 { + reset_db + execsql { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + } + for {set i 0} {$i < 9} {incr i} { + execsql { + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + INSERT INTO t1 VALUES($i, 0); + } + } + execsql ANALYZE + execsql { SELECT count(*) FROM sqlite_stat4 } +} {9} + +#------------------------------------------------------------------------- +# For coverage. +# +ifcapable view { + do_test 19.1 { + reset_db + execsql { + CREATE TABLE t1(x, y); + CREATE INDEX i1 ON t1(x, y); + CREATE VIEW v1 AS SELECT * FROM t1; + ANALYZE; + } + } {} +} +ifcapable auth { + proc authproc {op args} { + if {$op == "SQLITE_ANALYZE"} { return "SQLITE_DENY" } + return "SQLITE_OK" + } + do_test 19.2 { + reset_db + db auth authproc + execsql { + CREATE TABLE t1(x, y); + CREATE VIEW v1 AS SELECT * FROM t1; + } + catchsql ANALYZE + } {1 {not authorized}} +} + finish_test diff --git a/test/fts4unicode.test b/test/fts4unicode.test index aee7f05b1b..8149bb172d 100644 --- a/test/fts4unicode.test +++ b/test/fts4unicode.test @@ -402,5 +402,40 @@ foreach {tn1 c} { } } +#------------------------------------------------------------------------- +# Cursory test of remove_diacritics=0. +# +# 00C4;LATIN CAPITAL LETTER A WITH DIAERESIS +# 00D6;LATIN CAPITAL LETTER O WITH DIAERESIS +# 00E4;LATIN SMALL LETTER A WITH DIAERESIS +# 00F6;LATIN SMALL LETTER O WITH DIAERESIS +# +do_execsql_test 8.1.1 " + CREATE VIRTUAL TABLE t3 USING fts4(tokenize=unicode61 'remove_diacritics=1'); + INSERT INTO t3 VALUES('o'); + INSERT INTO t3 VALUES('a'); + INSERT INTO t3 VALUES('O'); + INSERT INTO t3 VALUES('A'); + INSERT INTO t3 VALUES('\xD6'); + INSERT INTO t3 VALUES('\xC4'); + INSERT INTO t3 VALUES('\xF6'); + INSERT INTO t3 VALUES('\xE4'); +" +do_execsql_test 8.1.2 { + SELECT rowid FROM t3 WHERE t3 MATCH 'o'; +} {1 3 5 7} +do_execsql_test 8.1.3 { + SELECT rowid FROM t3 WHERE t3 MATCH 'a'; +} {2 4 6 8} +do_execsql_test 8.2.1 { + CREATE VIRTUAL TABLE t4 USING fts4(tokenize=unicode61 "remove_diacritics=0"); + INSERT INTO t4 SELECT * FROM t3; +} +do_execsql_test 8.2.2 { + SELECT rowid FROM t4 WHERE t4 MATCH 'o'; +} {1 3} +do_execsql_test 8.2.3 { + SELECT rowid FROM t4 WHERE t4 MATCH 'a'; +} {2 4} finish_test diff --git a/test/permutations.test b/test/permutations.test index e6491b7480..426a802a39 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -314,6 +314,14 @@ test_suite "coverage-pager" -description { walfault.test walbak.test journal2.test tkt-9d68c883.test } +test_suite "coverage-analyze" -description { + Coverage tests for file analyze.c. +} -files { + analyze3.test analyze4.test analyze5.test analyze6.test + analyze7.test analyze8.test analyze9.test analyzeA.test + analyze.test mallocA.test +} + lappend ::testsuitelist xxx #------------------------------------------------------------------------- diff --git a/test/where.test b/test/where.test index 42170ceac7..09ad0bce6d 100644 --- a/test/where.test +++ b/test/where.test @@ -1304,4 +1304,33 @@ do_test where-17.5 { } } {42 1 43 1} +# Ticket [be84e357c035d068135f20bcfe82761bbf95006b] 2013-09-03 +# Segfault during query involving LEFT JOIN column in the ORDER BY clause. +# +do_execsql_test where-18.1 { + CREATE TABLE t181(a); + CREATE TABLE t182(b,c); + INSERT INTO t181 VALUES(1); + SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c IS NULL; +} {1} +do_execsql_test where-18.2 { + SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +c; +} {1} +do_execsql_test where-18.3 { + SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c; +} {1} +do_execsql_test where-18.4 { + INSERT INTO t181 VALUES(1),(1),(1),(1); + SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +c; +} {1} +do_execsql_test where-18.5 { + INSERT INTO t181 VALUES(2); + SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c IS NULL, +a; +} {1 2} +do_execsql_test where-18.6 { + INSERT INTO t181 VALUES(2); + SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +a, +c IS NULL; +} {1 2} + + finish_test diff --git a/test/whereF.test b/test/whereF.test index c98312a262..b9580bb196 100644 --- a/test/whereF.test +++ b/test/whereF.test @@ -112,4 +112,11 @@ foreach {tn sql} { } {/.*SCAN TABLE t2\y.*SEARCH TABLE t1\y.*/} } +do_execsql_test 4.0 { + CREATE TABLE t4(a,b,c,d,e, PRIMARY KEY(a,b,c)); + CREATE INDEX t4adc ON t4(a,d,c); + CREATE UNIQUE INDEX t4aebc ON t4(a,e,b,c); + EXPLAIN QUERY PLAN SELECT rowid FROM t4 WHERE a=? AND b=?; +} {/a=. AND b=./} + finish_test diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index 9032d1ead7..dd57482007 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -139,7 +139,7 @@ proc section_comment {text} { # Read the source file named $filename and write it into the # sqlite3.c output file. If any #include statements are seen, -# process them approprately. +# process them appropriately. # proc copy_file {filename} { global seen_hdr available_hdr out addstatic linemacros @@ -171,8 +171,14 @@ proc copy_file {filename} { } elseif {![info exists seen_hdr($hdr)]} { set seen_hdr($hdr) 1 puts $out $line + } elseif {[regexp {/\*\s+amalgamator:\s+keep\s+\*/} $line]} { + # This include file must be kept because there was a "keep" + # directive inside of a line comment. + puts $out $line } else { - puts $out "/* $line */" + # Comment out the entire line, replacing any nested comment + # begin/end markers with the harmless substring "**". + puts $out "/* [string map [list /* ** */ **] $line] */" } } elseif {[regexp {^#ifdef __cplusplus} $line]} { puts $out "#if 0"