diff --git a/addopcodes.awk b/addopcodes.awk index c18c4f3599..dcd31eff84 100644 --- a/addopcodes.awk +++ b/addopcodes.awk @@ -30,4 +30,5 @@ END { printf "#define TK_%-29s %4d\n", "AGG_COLUMN", ++max printf "#define TK_%-29s %4d\n", "UMINUS", ++max printf "#define TK_%-29s %4d\n", "UPLUS", ++max + printf "#define TK_%-29s %4d\n", "REGISTER", ++max } diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 3b9efee54c..44b7f431df 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -1472,6 +1472,19 @@ static int fts3CreateMethod( return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); } +/* +** Set the pIdxInfo->estimatedRows variable to nRow. Unless this +** extension is currently being used by a version of SQLite too old to +** support estimatedRows. In that case this function is a no-op. +*/ +static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){ +#if SQLITE_VERSION_NUMBER>=3008002 + if( sqlite3_libversion_number()>=3008002 ){ + pIdxInfo->estimatedRows = nRow; + } +#endif +} + /* ** Implementation of the xBestIndex method for FTS3 tables. There ** are three possible strategies, in order of preference: @@ -1499,7 +1512,20 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ for(i=0; inConstraint; i++){ int bDocid; /* True if this constraint is on docid */ struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; - if( pCons->usable==0 ) continue; + if( pCons->usable==0 ){ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ + /* There exists an unusable MATCH constraint. This means that if + ** the planner does elect to use the results of this call as part + ** of the overall query plan the user will see an "unable to use + ** function MATCH in the requested context" error. To discourage + ** this, return a very high cost here. */ + pInfo->idxNum = FTS3_FULLSCAN_SEARCH; + pInfo->estimatedCost = 1e50; + fts3SetEstimatedRows(pInfo, ((sqlite3_int64)1) << 50); + return SQLITE_OK; + } + continue; + } bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1); diff --git a/ext/misc/spellfix.c b/ext/misc/spellfix.c index 768ea5753c..4be73bb39e 100644 --- a/ext/misc/spellfix.c +++ b/ext/misc/spellfix.c @@ -2051,6 +2051,7 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){ ** (D) scope = $scope ** (E) distance < $distance ** (F) distance <= $distance +** (G) rowid = $rowid ** ** The plan number is a bit mask formed with these bits: ** @@ -2060,8 +2061,9 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){ ** 0x08 (D) is found ** 0x10 (E) is found ** 0x20 (F) is found +** 0x40 (G) is found ** -** filter.argv[*] values contains $str, $langid, $top, and $scope, +** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid ** if specified and in that order. */ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ @@ -2070,6 +2072,7 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int iTopTerm = -1; int iScopeTerm = -1; int iDistTerm = -1; + int iRowidTerm = -1; int i; const struct sqlite3_index_constraint *pConstraint; pConstraint = pIdxInfo->aConstraint; @@ -2122,6 +2125,15 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ iPlan |= pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ? 16 : 32; iDistTerm = i; } + + /* Terms of the form: distance < $dist or distance <= $dist */ + if( (iPlan & 64)==0 + && pConstraint->iColumn<0 + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ + ){ + iPlan |= 64; + iRowidTerm = i; + } } if( iPlan&1 ){ int idx = 2; @@ -2149,6 +2161,11 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ pIdxInfo->aConstraintUsage[iDistTerm].omit = 1; } pIdxInfo->estimatedCost = 1e5; + }else if( (iPlan & 64) ){ + pIdxInfo->idxNum = 64; + pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1; + pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1; + pIdxInfo->estimatedCost = 5; }else{ pIdxInfo->idxNum = 0; pIdxInfo->estimatedCost = 1e50; @@ -2465,16 +2482,23 @@ static int spellfix1FilterForFullScan( int argc, sqlite3_value **argv ){ - int rc; + int rc = SQLITE_OK; char *zSql; spellfix1_vtab *pVTab = pCur->pVTab; spellfix1ResetCursor(pCur); + assert( idxNum==0 || idxNum==64 ); zSql = sqlite3_mprintf( - "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"", - pVTab->zDbName, pVTab->zTableName); + "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s", + pVTab->zDbName, pVTab->zTableName, + ((idxNum & 64) ? " WHERE rowid=?" : "") + ); if( zSql==0 ) return SQLITE_NOMEM; rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0); sqlite3_free(zSql); + if( rc==SQLITE_OK && (idxNum & 64) ){ + assert( argc==1 ); + rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]); + } pCur->nRow = pCur->iRow = 0; if( rc==SQLITE_OK ){ rc = sqlite3_step(pCur->pFullScan); diff --git a/manifest b/manifest index b54d8eeabe..bd3002071d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\schanges\sinto\sthe\ssessions\sbranch. -D 2013-12-24T12:09:42.648 +C Bring\sin\sall\sthe\slatest\strunk\schanges,\sincluding\sthe\sCommon\sTable\nExpressions\simplementation. +D 2014-01-24T14:05:18.988 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -8,7 +8,7 @@ F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 F VERSION 8ed548d87d0a27fd7d7620476f9e25f9fa742d73 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 -F addopcodes.awk 87ca612393d0f439550634bd2c156ea9ff6195ae +F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 @@ -78,7 +78,7 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 1e667eacb3fe4b4ad6f863920da4286f071f6e07 +F ext/fts3/fts3.c 3fe91e36a0304ad4b35020f0e22ff37e95873166 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe F ext/fts3/fts3Int.h eb5f8029589f3d8f1dc7fd50c773326a640388b1 F ext/fts3/fts3_aux.c 5c211e17a64885faeb16b9ba7772f9d5445c2365 @@ -114,7 +114,7 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a -F ext/misc/spellfix.c 76578f2c56ceaa23d22032338d90db79c68490fb +F ext/misc/spellfix.c adfc569fafef7a1eb8f21528e5277686b358c3ce F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e @@ -180,16 +180,16 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c 11e29ef8cf16a42925fde036bcffbeffd9cc82df +F src/btree.c 02e1a4e71d8fc37e9fd5216c15d989d148a77c87 F src/btree.h a61ddebc78c66795a2b93181321a116746302cc9 F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0 -F src/build.c 47ef8209e56d840d2b35b8a243c6ee567ad52bda +F src/build.c 7e6c275ab1731510d6f793d0f88373ab3e858e69 F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 77779efbe78dd678d84bfb4fc2e87b6b6ad8dccd F src/date.c 593c744b2623971e45affd0bde347631bdfa4625 -F src/delete.c e9806af75b7f4015f6410ad87a2a12c353339499 -F src/expr.c ffe4bc79c66f711f450a6113fbd1943b9b2380f7 +F src/delete.c 80a3947fc234baba91842b90558db67bcd8706ea +F src/expr.c 61f9105820d6702d7153dfb6ca3d58e751a5e95a F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 2ab0f5384b70594468ef3ac5c7ed8ca24bfd17d5 F src/func.c 6325ac2ec10833ccf4d5c36d323709221d37ea19 @@ -197,12 +197,12 @@ F src/global.c 1d7bb7ea8254ae6a68ed9bfaf65fcb3d1690b486 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c de6cd4bb09a38560d2dd8ae96ec8b4a7ac6d6d70 +F src/insert.c a8a987ba42e7172b144052db79e7246da6ae2ccf F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303 -F src/main.c 2c674289707ebd814b33b8594c930d60861013b9 +F src/main.c deaeda63657d005ad9833f2191b7ff65c83e0ded F src/malloc.c 0203ebce9152c6a0e5de520140b8ba65187350be F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b @@ -219,31 +219,31 @@ F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 F src/os.c 1b147e4cf7cc39e618115c14a086aed44bc91ace F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 -F src/os_unix.c 60a7b3b23e6fcf83a50d1e320b280b551724e11f -F src/os_win.c 16eac0961603182ffc10c02b39fe830126538e07 +F src/os_unix.c 3a4dcb554d3c915075766162f28c3fd4cdb75968 +F src/os_win.c 1b21af72c5fa6f9e519a5fcab33e80d182b1aedb F src/pager.c efa923693e958696eee69b205a20bfbc402c8480 F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428 -F src/parse.y acee1a9958539e21263362b194594c5255ad2fca +F src/parse.y bd51bc17cbfe7549adb4ca3747b1c3d384645065 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c 57fee9a9a617218f5037afbbe49b09da65bde56b -F src/pragma.c 5ab7279d132143feb77f773688a24ab05da75fd7 +F src/pragma.c ed409ce4104cf4d9de6ead40ace70974f124853b F src/prepare.c 677521ab7132615a8a26107a1d1c3132f44ae337 F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269 -F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68 +F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 819bb090c9a348d17f69f136cad2bfa9ee9cbb41 -F src/shell.c a3541193d5fce37e91dad8ef46a9505aa7c9b344 -F src/sqlite.h.in 9ccaa04411778b0b3a95df6a9fc9c396b779f0cb +F src/select.c 93764e0d81946c070e2c7f1127f35e21efabbcc3 +F src/shell.c 24722d24d4ea8ca93db35e44db7308de786767ca +F src/sqlite.h.in a92d7fcdcb1a8003a62e916ec49025f27ccb56b8 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h edf19afa8c416413008d9b402f2991e2fb0b3e51 +F src/sqliteInt.h e4d4b5db50e69f2ce130d40d324db5b5c5638aa8 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c fa8d54ebdb7d91e0f6e7472ee5d8b7b543e65adb -F src/test1.c 633e5e6a116acf4473b9289240bcceb5320a9d93 +F src/tclsqlite.c a870d43e3c19663fce878aa4c7cac9c624aab564 +F src/test1.c 2401eee14a4309a7cfe2aeb2f30ad517a1d9c299 F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df @@ -256,7 +256,7 @@ F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16 -F src/test_config.c cb3342a4d66e1121f094f3c2c165b72bbe289a2b +F src/test_config.c b131030783f4328beb7008dbfe7392c7f086abc7 F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f @@ -288,17 +288,17 @@ F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb F src/test_vfs.c e72f555ef7a59080f898fcf1a233deb9eb704ea9 F src/test_vfstrace.c 3a0ab304682fecbceb689e7d9b904211fde11d78 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 -F src/tokenize.c ec4c1a62b890bf1dbcdb966399e140b904c700a4 -F src/trigger.c d84e1f3669e9a217731a14a9d472b1c7b87c87ba +F src/tokenize.c 6da2de6e12218ccb0aea5184b56727d011f4bee7 +F src/trigger.c 5c1c0b899ac0ce284763dcb8fdbaa38ecf15ef98 F src/update.c 3143bbdfc8c2d78b74dba15133df04843221ce0d F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 F src/util.c e71f19b272f05c8695cf747b4bac1732685f9e5c F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c ad89fac32d84b5b40778a20a707206630e9cf655 -F src/vdbe.h 4c15d2c90b52fce24e1bd5eaa783f7451849e95b +F src/vdbe.c c41493ca68c23f421b21bdb0ff1f0e8df7aaa876 +F src/vdbe.h 06016671144c70373331e348fd7edf2b2535ac97 F src/vdbeInt.h 08d79db15519f98d6d2c2dedaebfbb7f3d69a6d8 F src/vdbeapi.c 647d65813a5595c7f667b9f43d119ecd8d70be08 -F src/vdbeaux.c 4dc5258ac337fd6600d1efb144b34316d2bbe05a +F src/vdbeaux.c a980f7817ab73afb5efc5fac97097421f0ee8f66 F src/vdbeblob.c 6e791541114d482074e031ef8dbc3d5e5c180e23 F src/vdbemem.c 0e69351b2c6ff7d8b638688c0ae336a26befa6b2 F src/vdbesort.c 9d83601f9d6243fe70dd0169a2820c5ddfd48147 @@ -306,8 +306,8 @@ F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767 F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 -F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74 -F src/where.c 60bc8c5b00e2292c24a42455d022eeeda33a16f1 +F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45 +F src/where.c 67ae3b5e97ecff36c70cb61ccc7d74cf228f1596 F src/whereInt.h 96a75c61f1d2b9d4a8e4bb17d89deb0cf7cba358 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -341,7 +341,7 @@ F test/attach2.test 0ec5defa340363de6cd50fd595046465e9aaba2d F test/attach3.test 359eb65d00102cdfcef6fa4e81dc1648f8f80b27 F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0 -F test/auth.test 9bea29041871807d9f289ee679d05d3ed103642f +F test/auth.test 5bdf154eb28c0e4bbc0473f335858c0d96171768 F test/auth2.test c3b415b76c033bedb81292118fb7c01f5f10cbcd F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7 @@ -378,9 +378,9 @@ F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3 F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0 F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738 -F test/capi3.test 56ab450125ead38846cbae7e5b6a216686c3cffa +F test/capi3.test 6cdd49656bd62a296924f4d2fcfd05cd2a298369 F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 -F test/capi3c.test 93d24621c9ff84da9da060f30431e0453db1cdb0 +F test/capi3c.test a21869e4d50d5dbb7e566e328fc0bc7c2efa6a32 F test/capi3d.test 6d0fc0a86d73f42dd19a7d8b7761ab9bc02277d0 F test/capi3e.test ad90088b18b0367125ff2d4b5400153fd2f99aab F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 @@ -420,6 +420,7 @@ F test/corruptD.test b3c205fac7952b1de645ce44bb02335cd9e3e040 F test/corruptE.test 193b4ca4e927e77c1d5f4f56203ddc998432a7ee F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4 F test/corruptG.test c150f156dace653c00a121ad0f5772a0568c41ba +F test/corruptH.test 0a247f3dc8a8f3578db5f639d86c6bb4d520207f F test/count.test 42a251178e32f617eda33f76236a7f79825a50b5 F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62 F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f @@ -452,7 +453,7 @@ F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412 F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306 F test/e_expr.test 5c71d183fbf519a4769fd2e2124afdc70b5b1f42 -F test/e_fkey.test d83a04478bb9c02d2c513518548a69f818869f41 +F test/e_fkey.test 630597377549af579d34faaf64c6959a5a68ef76 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_insert.test 1e44f84d2abe44d66e4fbf198be4b20e3cc724a0 F test/e_reindex.test 396b7b4f0a66863b4e95116a67d93b227193e589 @@ -561,6 +562,7 @@ F test/fts3expr3.test 9e91b8edbcb197bf2e92161aa7696446d96dce5f F test/fts3fault.test cb72dccb0a3b9f730f16c5240f3fcb9303eb1660 F test/fts3fault2.test 3198eef2804deea7cac8403e771d9cbcb752d887 F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641 +F test/fts3join.test 53e66a0c21eb568580674a43b21c059acb26f499 F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6 F test/fts3matchinfo.test ff423e73faab8fc6d7adeefedf74dd8e2b0b14e0 F test/fts3near.test 7e3354d46f155a822b59c0e957fd2a70c1d7e905 @@ -656,10 +658,10 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307 F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36 F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa -F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05 +F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 -F test/like.test 935fb4f608e3ea126891496a6e99b9468372bf5c +F test/like.test e191e536d0fcd722a6b965e7cd1ee0bfd12a5991 F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da F test/limit.test cc0ab63385239b63c72452b0e93700bf5e8f0b99 F test/loadext.test 92e6dfefd1229c3ef4aaabd87419efd8fa57a7a5 @@ -706,14 +708,14 @@ F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef -F test/misc1.test 9bed1bd334065a57dc841cff969d4fc1eeb6d49b +F test/misc1.test 441a0fafc7087f841db09fbfca54e7aea9f5a84c F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6 F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 -F test/misc7.test 1265eb98c2e22a446a13fdef754118b272716684 -F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054 +F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2 +F test/misuse.test 3c34719944ba045cc6c188a4852ba04680728912 F test/mmap1.test 93d167b328255cbe6679fe1e1a23be1b1197d07b F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022 F test/mmap3.test c92273e16eb8d23c1d55c9815b446bb72ef0512e @@ -795,7 +797,7 @@ F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054 F test/select4.test 00179be44e531fe04c1c3f15df216439dff2519d F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535 F test/select6.test e76bd10a56988f15726c097a5d5a7966fe82d3b2 -F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe +F test/select7.test 7fd2ef598cfabb6b9ff6ac13973b91d0527df49d F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95 F test/selectA.test 99cf21df033b93033ea4f34aba14a500f48f04fe @@ -838,7 +840,7 @@ F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b F test/speedtest1.c 7130d2cb6db45baa553a4ab2f715116c71c2d9f4 -F test/spellfix.test 8c40b169b104086d8795781f670ba3c786d6d8be +F test/spellfix.test 674db5da8b16d2b54939b68ccc0ac31ca53d9977 F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 @@ -1040,7 +1042,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 F test/view.test 4057630287bfa5955628fe90a13d4c225d1c7352 -F test/vtab1.test 45ddde57764659c0ec90874bcb6c4831f1004c06 +F test/vtab1.test b631d147b198cfd7903ab5fed028eb2a3d321dc6 F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1 F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 @@ -1105,6 +1107,9 @@ F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d +F test/with1.test 9d3537372c8cf6d5e0a5e9af037a52f3375fb704 +F test/with2.test 2fe78fcd8deef2a0f9cfc49bfc755911d0b3fd64 +F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991 F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8 F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99 F test/without_rowid3.test eac3d5c8a1924725b58503a368f2cbd24fd6c8a0 @@ -1123,11 +1128,11 @@ F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439 F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4 F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce -F tool/lemon.c 796930d5fc2036c7636f3f1ee12f9ae03719a2eb +F tool/lemon.c 07aba6270d5a5016ba8107b09e431eea4ecdc123 F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc F tool/logest.c 7ad625cac3d54012b27d468b7af6612f78b9ba75 F tool/mkautoconfamal.sh f8d8dbf7d62f409ebed5134998bf5b51d7266383 -F tool/mkkeywordhash.c 189d76644e373c7d0864c628deb8ce7b4f403591 +F tool/mkkeywordhash.c c9e05e4a7bcab8fab9f583d5b321fb72f565ad97 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkpragmatab.tcl 78a77b2c554d534c6f2dc903130186ed15715460 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 @@ -1162,7 +1167,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 32477642d79615fb85680bdac812ad9655cf6902 cc72c5aec7fe93d4a1368517aab949dc98d33003 -R 78ce3efefe8e1d1aa8c19dbbb86af6d1 +P cfd110bf5db2c1993a5e2ca718648bd9c17ee22c 83b0b2916589db0184435dbd4c304387f393ed60 +R 5819e16ba708c7c74e3c7382ce1686de U drh -Z 99f0c83c836f43da179f2c44da61e31f +Z ab25b0717f386dd1619d1172c5947b1e diff --git a/manifest.uuid b/manifest.uuid index b9fadc710a..7e8ba52694 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cfd110bf5db2c1993a5e2ca718648bd9c17ee22c \ No newline at end of file +9b43e559195680e558264c4c00d34dc9cf9d9146 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 20bed057e1..032f3d8c78 100644 --- a/src/btree.c +++ b/src/btree.c @@ -3754,7 +3754,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){ int iPage = pCur->iPage; memset(&info, 0, sizeof(info)); btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); - assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); + assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 ); } #else #define assertCellInfo(x) @@ -4390,26 +4390,24 @@ static int moveToRoot(BtCursor *pCur){ return rc; } pCur->iPage = 0; - - /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor - ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is - ** NULL, the caller expects a table b-tree. If this is not the case, - ** return an SQLITE_CORRUPT error. */ - assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 ); - if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } } - - /* Assert that the root page is of the correct type. This must be the - ** case as the call to this function that loaded the root-page (either - ** this call or a previous invocation) would have detected corruption - ** if the assumption were not true, and it is not possible for the flags - ** byte to have been modified while this cursor is holding a reference - ** to the page. */ pRoot = pCur->apPage[0]; assert( pRoot->pgno==pCur->pgnoRoot ); - assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey ); + + /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor + ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is + ** NULL, the caller expects a table b-tree. If this is not the case, + ** return an SQLITE_CORRUPT error. + ** + ** Earlier versions of SQLite assumed that this test could not fail + ** if the root page was already loaded when this function was called (i.e. + ** if pCur->iPage>=0). But this is not so if the database is corrupted + ** in such a way that page pRoot is linked into a second b-tree table + ** (or the freelist). */ + assert( pRoot->intKey==1 || pRoot->intKey==0 ); + if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){ + return SQLITE_CORRUPT_BKPT; + } pCur->aiIdx[0] = 0; pCur->info.nSize = 0; @@ -5251,6 +5249,7 @@ end_allocate_page: if( rc==SQLITE_OK ){ if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){ releasePage(*ppPage); + *ppPage = 0; return SQLITE_CORRUPT_BKPT; } (*ppPage)->isInit = 0; @@ -7350,6 +7349,7 @@ static int clearDatabasePage( int rc; unsigned char *pCell; int i; + int hdr; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ @@ -7358,6 +7358,7 @@ static int clearDatabasePage( rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; + hdr = pPage->hdrOffset; for(i=0; inCell; i++){ pCell = findCell(pPage, i); if( !pPage->leaf ){ @@ -7368,7 +7369,7 @@ static int clearDatabasePage( if( rc ) goto cleardatabasepage_out; } if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange); + rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); if( rc ) goto cleardatabasepage_out; }else if( pnChange ){ assert( pPage->intKey ); @@ -7377,7 +7378,7 @@ static int clearDatabasePage( if( freePageFlag ){ freePage(pPage, &rc); }else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){ - zeroPage(pPage, pPage->aData[0] | PTF_LEAF); + zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF); } cleardatabasepage_out: diff --git a/src/build.c b/src/build.c index c83ce57cfa..fa7364c3da 100644 --- a/src/build.c +++ b/src/build.c @@ -140,6 +140,7 @@ void sqlite3FinishCoding(Parse *pParse){ assert( !pParse->isMultiWrite || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); if( v ){ + while( sqlite3VdbeDeletePriorOpcode(v, OP_Close) ){} sqlite3VdbeAddOp0(v, OP_Halt); /* The cookie mask contains one bit for each database file open. @@ -1451,10 +1452,10 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){ for(j=0; zIdent[j]; j++){ if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break; } - needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID; - if( !needQuote ){ - needQuote = zIdent[j]; - } + needQuote = sqlite3Isdigit(zIdent[0]) + || sqlite3KeywordCode(zIdent, j)!=TK_ID + || zIdent[j]!=0 + || j==0; if( needQuote ) z[i++] = '"'; for(j=0; zIdent[j]; j++){ @@ -2677,7 +2678,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); regRecord = sqlite3GetTempReg(pParse); - sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 0, &iPartIdxLabel); + sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); sqlite3VdbeResolveLabel(v, iPartIdxLabel); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); @@ -4197,3 +4198,73 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ } return sqlite3KeyInfoRef(pIdx->pKeyInfo); } + +#ifndef SQLITE_OMIT_CTE +/* +** This routine is invoked once per CTE by the parser while parsing a +** WITH clause. +*/ +With *sqlite3WithAdd( + Parse *pParse, /* Parsing context */ + With *pWith, /* Existing WITH clause, or NULL */ + Token *pName, /* Name of the common-table */ + ExprList *pArglist, /* Optional column name list for the table */ + Select *pQuery /* Query used to initialize the table */ +){ + sqlite3 *db = pParse->db; + With *pNew; + char *zName; + + /* Check that the CTE name is unique within this WITH clause. If + ** not, store an error in the Parse structure. */ + zName = sqlite3NameFromToken(pParse->db, pName); + if( zName && pWith ){ + int i; + for(i=0; inCte; i++){ + if( sqlite3StrICmp(zName, pWith->a[i].zName)==0 ){ + sqlite3ErrorMsg(pParse, "duplicate WITH table name: %s", zName); + } + } + } + + if( pWith ){ + int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); + pNew = sqlite3DbRealloc(db, pWith, nByte); + }else{ + pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); + } + assert( zName!=0 || pNew==0 ); + assert( db->mallocFailed==0 || pNew==0 ); + + if( pNew==0 ){ + sqlite3ExprListDelete(db, pArglist); + sqlite3SelectDelete(db, pQuery); + sqlite3DbFree(db, zName); + pNew = pWith; + }else{ + pNew->a[pNew->nCte].pSelect = pQuery; + pNew->a[pNew->nCte].pCols = pArglist; + pNew->a[pNew->nCte].zName = zName; + pNew->a[pNew->nCte].zErr = 0; + pNew->nCte++; + } + + return pNew; +} + +/* +** Free the contents of the With object passed as the second argument. +*/ +void sqlite3WithDelete(sqlite3 *db, With *pWith){ + if( pWith ){ + int i; + for(i=0; inCte; i++){ + struct Cte *pCte = &pWith->a[i]; + sqlite3ExprListDelete(db, pCte->pCols); + sqlite3SelectDelete(db, pCte->pSelect); + sqlite3DbFree(db, pCte->zName); + } + sqlite3DbFree(db, pWith); + } +} +#endif /* !defined(SQLITE_OMIT_CTE) */ diff --git a/src/delete.c b/src/delete.c index 7d14c50df6..ce1d104672 100644 --- a/src/delete.c +++ b/src/delete.c @@ -728,9 +728,10 @@ void sqlite3GenerateRowIndexDelete( int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */ ){ int i; /* Index loop counter */ - int r1; /* Register holding an index key */ + int r1 = -1; /* Register holding an index key */ int iPartIdxLabel; /* Jump destination for skipping partial index entries */ Index *pIdx; /* Current index */ + Index *pPrior = 0; /* Prior index */ Vdbe *v; /* The prepared statement under construction */ Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */ @@ -741,10 +742,12 @@ void sqlite3GenerateRowIndexDelete( if( aRegIdx!=0 && aRegIdx[i]==0 ) continue; if( pIdx==pPk ) continue; VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); - r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, &iPartIdxLabel); + r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, + &iPartIdxLabel, pPrior, r1); sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); sqlite3VdbeResolveLabel(v, iPartIdxLabel); + pPrior = pIdx; } } @@ -766,6 +769,17 @@ void sqlite3GenerateRowIndexDelete( ** to false or null. If pIdx is not a partial index, *piPartIdxLabel ** will be set to zero which is an empty label that is ignored by ** sqlite3VdbeResolveLabel(). +** +** The pPrior and regPrior parameters are used to implement a cache to +** avoid unnecessary register loads. If pPrior is not NULL, then it is +** a pointer to a different index for which an index key has just been +** computed into register regPrior. If the current pIdx index is generating +** its key into the same sequence of registers and if pPrior and pIdx share +** a column in common, then the register corresponding to that column already +** holds the correct value and the loading of that register is skipped. +** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK +** on a table with multiple indices, and especially with the ROWID or +** PRIMARY KEY columns of the index. */ int sqlite3GenerateIndexKey( Parse *pParse, /* Parsing context */ @@ -773,7 +787,9 @@ int sqlite3GenerateIndexKey( int iDataCur, /* Cursor number from which to take column data */ int regOut, /* Put the new key into this register if not 0 */ int prefixOnly, /* Compute only a unique prefix of the key */ - int *piPartIdxLabel /* OUT: Jump to this label to skip partial index */ + int *piPartIdxLabel, /* OUT: Jump to this label to skip partial index */ + Index *pPrior, /* Previously generated index key */ + int regPrior /* Register holding previous generated key */ ){ Vdbe *v = pParse->pVdbe; int j; @@ -793,21 +809,21 @@ int sqlite3GenerateIndexKey( } nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; regBase = sqlite3GetTempRange(pParse, nCol); + if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0; for(j=0; jaiColumn[j]==pIdx->aiColumn[j] ) continue; sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j], regBase+j); + /* If the column affinity is REAL but the number is an integer, then it + ** might be stored in the table as an integer (using a compact + ** representation) then converted to REAL by an OP_RealAffinity opcode. + ** But we are getting ready to store this value back into an index, where + ** it should be converted by to INTEGER again. So omit the OP_RealAffinity + ** opcode if it is present */ + sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); } if( regOut ){ - const char *zAff; - if( pTab->pSelect - || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) - ){ - zAff = 0; - }else{ - zAff = sqlite3IndexAffinityStr(v, pIdx); - } sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); - sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); } sqlite3ReleaseTempRange(pParse, regBase, nCol); return regBase; diff --git a/src/expr.c b/src/expr.c index 8ee73acb78..2e8079eb8f 100644 --- a/src/expr.c +++ b/src/expr.c @@ -523,16 +523,25 @@ Expr *sqlite3PExpr( } /* -** Return 1 if an expression must be FALSE in all cases and 0 if the -** expression might be true. This is an optimization. If is OK to -** return 0 here even if the expression really is always false (a -** false negative). But it is a bug to return 1 if the expression -** might be true in some rare circumstances (a false positive.) +** If the expression is always either TRUE or FALSE (respectively), +** then return 1. If one cannot determine the truth value of the +** expression at compile-time return 0. +** +** This is an optimization. If is OK to return 0 here even if +** the expression really is always false or false (a false negative). +** But it is a bug to return 1 if the expression might have different +** boolean values in different circumstances (a false positive.) ** ** Note that if the expression is part of conditional for a ** LEFT JOIN, then we cannot determine at compile-time whether or not ** is it true or false, so always return 0. */ +static int exprAlwaysTrue(Expr *p){ + int v = 0; + if( ExprHasProperty(p, EP_FromJoin) ) return 0; + if( !sqlite3ExprIsInteger(p, &v) ) return 0; + return v!=0; +} static int exprAlwaysFalse(Expr *p){ int v = 0; if( ExprHasProperty(p, EP_FromJoin) ) return 0; @@ -886,6 +895,33 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){ return pNew; } +/* +** Create and return a deep copy of the object passed as the second +** argument. If an OOM condition is encountered, NULL is returned +** and the db->mallocFailed flag set. +*/ +#ifndef SQLITE_OMIT_CTE +static With *withDup(sqlite3 *db, With *p){ + With *pRet = 0; + if( p ){ + int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); + pRet = sqlite3DbMallocZero(db, nByte); + if( pRet ){ + int i; + pRet->nCte = p->nCte; + for(i=0; inCte; i++){ + pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0); + pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0); + pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName); + } + } + } + return pRet; +} +#else +# define withDup(x,y) 0 +#endif + /* ** The following group of routines make deep copies of expressions, ** expression lists, ID lists, and select statements. The copies can @@ -966,6 +1002,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ pNewItem->regReturn = pOldItem->regReturn; pNewItem->isCorrelated = pOldItem->isCorrelated; pNewItem->viaCoroutine = pOldItem->viaCoroutine; + pNewItem->isRecursive = pOldItem->isRecursive; pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex); pNewItem->notIndexed = pOldItem->notIndexed; pNewItem->pIndex = pOldItem->pIndex; @@ -1027,6 +1064,7 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->addrOpenEphm[2] = -1; + pNew->pWith = withDup(db, p->pWith); return pNew; } #else @@ -3529,8 +3567,8 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_AND: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); - sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL); + sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); sqlite3VdbeResolveLabel(v, d2); sqlite3ExprCachePop(pParse, 1); @@ -3539,7 +3577,9 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_OR: { testcase( jumpIfNull==0 ); sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); + sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); + sqlite3ExprCachePop(pParse, 1); break; } case TK_NOT: { @@ -3614,10 +3654,16 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ } #endif default: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); - sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); - testcase( regFree1==0 ); - testcase( jumpIfNull==0 ); + if( exprAlwaysTrue(pExpr) ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); + }else if( exprAlwaysFalse(pExpr) ){ + /* No-op */ + }else{ + r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); + sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); + testcase( regFree1==0 ); + testcase( jumpIfNull==0 ); + } break; } } @@ -3680,14 +3726,16 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_AND: { testcase( jumpIfNull==0 ); sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); + sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); + sqlite3ExprCachePop(pParse, 1); break; } case TK_OR: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); - sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL); + sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); sqlite3VdbeResolveLabel(v, d2); sqlite3ExprCachePop(pParse, 1); @@ -3759,10 +3807,16 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ } #endif default: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); - sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); - testcase( regFree1==0 ); - testcase( jumpIfNull==0 ); + if( exprAlwaysFalse(pExpr) ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); + }else if( exprAlwaysTrue(pExpr) ){ + /* no-op */ + }else{ + r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); + sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); + testcase( regFree1==0 ); + testcase( jumpIfNull==0 ); + } break; } } diff --git a/src/insert.c b/src/insert.c index c23d1b09f2..ef7921bddc 100644 --- a/src/insert.c +++ b/src/insert.c @@ -540,7 +540,6 @@ static int xferOptimization( void sqlite3Insert( Parse *pParse, /* Parser context */ SrcList *pTabList, /* Name of table into which we are inserting */ - ExprList *pList, /* List of values to be inserted */ Select *pSelect, /* A SELECT statement to use as the data source */ IdList *pColumn, /* Column names corresponding to IDLIST. */ int onError /* How to handle constraint errors */ @@ -568,6 +567,7 @@ void sqlite3Insert( Db *pDb; /* The database containing table being inserted into */ int appendFlag = 0; /* True if the insert is likely to be an append */ int withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */ + ExprList *pList = 0; /* List of VALUES() to be inserted */ /* Register allocations */ int regFromSelect = 0;/* Base register for data coming from SELECT */ @@ -591,6 +591,17 @@ void sqlite3Insert( goto insert_cleanup; } + /* If the Select object is really just a simple VALUES() list with a + ** single row values (the common case) then keep that one row of values + ** and go ahead and discard the Select object + */ + if( pSelect && (pSelect->selFlags & SF_Values)!=0 && pSelect->pPrior==0 ){ + pList = pSelect->pEList; + pSelect->pEList = 0; + sqlite3SelectDelete(db, pSelect); + pSelect = 0; + } + /* Locate the table into which we will be inserting new information. */ assert( pTabList->nSrc==1 ); @@ -1233,6 +1244,7 @@ void sqlite3GenerateConstraintChecks( int ipkTop = 0; /* Top of the rowid change constraint check */ int ipkBottom = 0; /* Bottom of the rowid change constraint check */ u8 isUpdate; /* True if this is an UPDATE operation */ + int regRowid = -1; /* Register holding ROWID value */ isUpdate = regOldData!=0; db = pParse->db; @@ -1475,7 +1487,9 @@ void sqlite3GenerateConstraintChecks( int iField = pIdx->aiColumn[i]; int x; if( iField<0 || iField==pTab->iPKey ){ + if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */ x = regNewData; + regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i; }else{ x = iField + regNewData + 1; } @@ -1856,6 +1870,12 @@ static int xferOptimization( if( pSelect==0 ){ return 0; /* Must be of the form INSERT INTO ... SELECT ... */ } + if( pParse->pWith || pSelect->pWith ){ + /* Do not attempt to process this query if there are an WITH clauses + ** attached to it. Proceeding may generate a false "no such table: xxx" + ** error if pSelect reads from a CTE named "xxx". */ + return 0; + } if( sqlite3TriggerList(pParse, pDest) ){ return 0; /* tab1 must not have triggers */ } diff --git a/src/main.c b/src/main.c index b7fe5072bd..875313500a 100644 --- a/src/main.c +++ b/src/main.c @@ -135,13 +135,6 @@ int sqlite3_initialize(void){ */ if( sqlite3GlobalConfig.isInit ) return SQLITE_OK; -#ifdef SQLITE_ENABLE_SQLLOG - { - extern void sqlite3_init_sqllog(void); - sqlite3_init_sqllog(); - } -#endif - /* Make sure the mutex subsystem is initialized. If unable to ** initialize the mutex subsystem, return early with the error. ** If the system is so sick that we are unable to allocate a mutex, @@ -3122,7 +3115,7 @@ int sqlite3_test_control(int op, ...){ ** to the xRandomness method of the default VFS. */ case SQLITE_TESTCTRL_PRNG_RESET: { - sqlite3PrngResetState(); + sqlite3_randomness(0,0); break; } diff --git a/src/os_unix.c b/src/os_unix.c index 485b32fd90..96cd5e6191 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -260,6 +260,12 @@ struct unixFile { #endif }; +/* This variable holds the process id (pid) from when the xRandomness() +** method was called. If xOpen() is called from a different process id, +** indicating that a fork() has occurred, the PRNG will be reset. +*/ +static int randomnessPid = 0; + /* ** Allowed values for the unixFile.ctrlFlags bitmask: */ @@ -4842,10 +4848,10 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ ** may now be invalid and should be unmapped. */ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ +#if SQLITE_MAX_MMAP_SIZE>0 unixFile *pFd = (unixFile *)fd; /* The underlying database file */ UNUSED_PARAMETER(iOff); -#if SQLITE_MAX_MMAP_SIZE>0 /* If p==0 (unmap the entire file) then there must be no outstanding ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), ** then there must be at least one outstanding. */ @@ -4861,6 +4867,10 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ } assert( pFd->nFetchOut>=0 ); +#else + UNUSED_PARAMETER(fd); + UNUSED_PARAMETER(p); + UNUSED_PARAMETER(iOff); #endif return SQLITE_OK; } @@ -5651,6 +5661,16 @@ static int unixOpen( || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL ); + /* Detect a pid change and reset the PRNG. There is a race condition + ** here such that two or more threads all trying to open databases at + ** the same instant might all reset the PRNG. But multiple resets + ** are harmless. + */ + if( randomnessPid!=getpid() ){ + randomnessPid = getpid(); + sqlite3_randomness(0,0); + } + memset(p, 0, sizeof(unixFile)); if( eType==SQLITE_OPEN_MAIN_DB ){ @@ -6038,18 +6058,18 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ ** tests repeatable. */ memset(zBuf, 0, nBuf); + randomnessPid = getpid(); #if !defined(SQLITE_TEST) { - int pid, fd, got; + int fd, got; fd = robust_open("/dev/urandom", O_RDONLY, 0); if( fd<0 ){ time_t t; time(&t); memcpy(zBuf, &t, sizeof(t)); - pid = getpid(); - memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); - assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); - nBuf = sizeof(t) + sizeof(pid); + memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid)); + assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf ); + nBuf = sizeof(t) + sizeof(randomnessPid); }else{ do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR ); robust_close(0, fd, __LINE__); diff --git a/src/os_win.c b/src/os_win.c index faa60569c9..4fb4f02703 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -3224,7 +3224,7 @@ static void winShmEnterMutex(void){ static void winShmLeaveMutex(void){ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } -#ifdef SQLITE_DEBUG +#ifndef NDEBUG static int winShmMutexHeld(void) { return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } diff --git a/src/parse.y b/src/parse.y index ba9feb10fc..d0ec821d7a 100644 --- a/src/parse.y +++ b/src/parse.y @@ -94,14 +94,6 @@ struct TrigEvent { int a; IdList * b; }; */ struct AttachKey { int type; Token key; }; -/* -** One or more VALUES claues -*/ -struct ValueList { - ExprList *pList; - Select *pSelect; -}; - } // end %include // Input is a single SQL command @@ -202,9 +194,7 @@ columnid(A) ::= nm(X). { // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. // -%type id {Token} -id(A) ::= ID(X). {A = X;} -id(A) ::= INDEXED(X). {A = X;} +%token_class id ID|INDEXED. // The following directive causes tokens ABORT, AFTER, ASC, etc. to // fallback to ID if they will not parse as their original value. @@ -214,8 +204,8 @@ id(A) ::= INDEXED(X). {A = X;} ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN - QUERY KEY OF OFFSET PRAGMA RAISE RELEASE REPLACE RESTRICT ROW ROLLBACK - SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITHOUT + QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW + ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif SQLITE_OMIT_COMPOUND_SELECT @@ -249,8 +239,7 @@ id(A) ::= INDEXED(X). {A = X;} // And "ids" is an identifer-or-string. // -%type ids {Token} -ids(A) ::= ID|STRING(X). {A = X;} +%token_class ids ID|STRING. // The name of a column or table can be any of the following: // @@ -408,7 +397,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). { //////////////////////// The SELECT statement ///////////////////////////////// // cmd ::= select(X). { - SelectDest dest = {SRT_Output, 0, 0, 0, 0}; + SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0}; sqlite3Select(pParse, X, &dest); sqlite3ExplainBegin(pParse->pVdbe); sqlite3ExplainSelect(pParse->pVdbe, X); @@ -418,12 +407,23 @@ cmd ::= select(X). { %type select {Select*} %destructor select {sqlite3SelectDelete(pParse->db, $$);} +%type selectnowith {Select*} +%destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);} %type oneselect {Select*} %destructor oneselect {sqlite3SelectDelete(pParse->db, $$);} -select(A) ::= oneselect(X). {A = X;} +select(A) ::= with(W) selectnowith(X). { + if( X ){ + X->pWith = W; + }else{ + sqlite3WithDelete(pParse->db, W); + } + A = X; +} + +selectnowith(A) ::= oneselect(X). {A = X;} %ifndef SQLITE_OMIT_COMPOUND_SELECT -select(A) ::= select(X) multiselect_op(Y) oneselect(Z). { +selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). { if( Z ){ Z->op = (u8)Y; Z->pPrior = X; @@ -442,6 +442,23 @@ oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). { A = sqlite3SelectNew(pParse,W,X,Y,P,Q,Z,D,L.pLimit,L.pOffset); } +oneselect(A) ::= values(X). {A = X;} + +%type values {Select*} +%destructor values {sqlite3SelectDelete(pParse->db, $$);} +values(A) ::= VALUES LP nexprlist(X) RP. { + A = sqlite3SelectNew(pParse,X,0,0,0,0,0,SF_Values,0,0); +} +values(A) ::= values(X) COMMA LP exprlist(Y) RP. { + Select *pRight = sqlite3SelectNew(pParse,Y,0,0,0,0,0,SF_Values,0,0); + if( pRight ){ + pRight->op = TK_ALL; + pRight->pPrior = X; + A = pRight; + }else{ + A = X; + } +} // The "distinct" nonterminal is true (1) if the DISTINCT keyword is // present and false (0) if it is not. @@ -642,15 +659,17 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y). /////////////////////////// The DELETE statement ///////////////////////////// // %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W) +cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W) orderby_opt(O) limit_opt(L). { + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE"); sqlite3DeleteFrom(pParse,X,W); } %endif %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W). { +cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). { + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3DeleteFrom(pParse,X,W); } @@ -665,8 +684,9 @@ where_opt(A) ::= WHERE expr(X). {A = X.pExpr;} ////////////////////////// The UPDATE command //////////////////////////////// // %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) - orderby_opt(O) limit_opt(L). { +cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) + where_opt(W) orderby_opt(O) limit_opt(L). { + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE"); @@ -674,8 +694,9 @@ cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) } %endif %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) +cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W). { + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); sqlite3Update(pParse,X,Y,W,R); @@ -696,58 +717,20 @@ setlist(A) ::= nm(X) EQ expr(Y). { ////////////////////////// The INSERT command ///////////////////////////////// // -cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) valuelist(Y). - {sqlite3Insert(pParse, X, Y.pList, Y.pSelect, F, R);} -cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). - {sqlite3Insert(pParse, X, 0, S, F, R);} -cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. - {sqlite3Insert(pParse, X, 0, 0, F, R);} +cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). { + sqlite3WithPush(pParse, W, 1); + sqlite3Insert(pParse, X, S, F, R); +} +cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. +{ + sqlite3WithPush(pParse, W, 1); + sqlite3Insert(pParse, X, 0, F, R); +} %type insert_cmd {u8} insert_cmd(A) ::= INSERT orconf(R). {A = R;} insert_cmd(A) ::= REPLACE. {A = OE_Replace;} -// A ValueList is either a single VALUES clause or a comma-separated list -// of VALUES clauses. If it is a single VALUES clause then the -// ValueList.pList field points to the expression list of that clause. -// If it is a list of VALUES clauses, then those clauses are transformed -// into a set of SELECT statements without FROM clauses and connected by -// UNION ALL and the ValueList.pSelect points to the right-most SELECT in -// that compound. -%type valuelist {struct ValueList} -%destructor valuelist { - sqlite3ExprListDelete(pParse->db, $$.pList); - sqlite3SelectDelete(pParse->db, $$.pSelect); -} -valuelist(A) ::= VALUES LP nexprlist(X) RP. { - A.pList = X; - A.pSelect = 0; -} - -// Since a list of VALUEs is inplemented as a compound SELECT, we have -// to disable the value list option if compound SELECTs are disabled. -%ifndef SQLITE_OMIT_COMPOUND_SELECT -valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. { - Select *pRight = sqlite3SelectNew(pParse, Y, 0, 0, 0, 0, 0, 0, 0, 0); - if( X.pList ){ - X.pSelect = sqlite3SelectNew(pParse, X.pList, 0, 0, 0, 0, 0, 0, 0, 0); - X.pList = 0; - } - A.pList = 0; - if( X.pSelect==0 || pRight==0 ){ - sqlite3SelectDelete(pParse->db, pRight); - sqlite3SelectDelete(pParse->db, X.pSelect); - A.pSelect = 0; - }else{ - pRight->op = TK_ALL; - pRight->pPrior = X.pSelect; - pRight->selFlags |= SF_Values; - pRight->pPrior->selFlags |= SF_Values; - A.pSelect = pRight; - } -} -%endif SQLITE_OMIT_COMPOUND_SELECT - %type inscollist_opt {IdList*} %destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);} %type idlist {IdList*} @@ -810,22 +793,22 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { } term(A) ::= INTEGER|FLOAT|BLOB(X). {spanExpr(&A, pParse, @X, &X);} term(A) ::= STRING(X). {spanExpr(&A, pParse, @X, &X);} -expr(A) ::= REGISTER(X). { - /* When doing a nested parse, one can include terms in an expression - ** that look like this: #1 #2 ... These terms refer to registers - ** in the virtual machine. #N is the N-th register. */ - if( pParse->nested==0 ){ - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &X); - A.pExpr = 0; - }else{ - A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &X); - if( A.pExpr ) sqlite3GetInt32(&X.z[1], &A.pExpr->iTable); - } - spanSet(&A, &X, &X); -} expr(A) ::= VARIABLE(X). { - spanExpr(&A, pParse, TK_VARIABLE, &X); - sqlite3ExprAssignVarNumber(pParse, A.pExpr); + if( X.n>=2 && X.z[0]=='#' && sqlite3Isdigit(X.z[1]) ){ + /* When doing a nested parse, one can include terms in an expression + ** that look like this: #1 #2 ... These terms refer to registers + ** in the virtual machine. #N is the N-th register. */ + if( pParse->nested==0 ){ + sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &X); + A.pExpr = 0; + }else{ + A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &X); + if( A.pExpr ) sqlite3GetInt32(&X.z[1], &A.pExpr->iTable); + } + }else{ + spanExpr(&A, pParse, TK_VARIABLE, &X); + sqlite3ExprAssignVarNumber(pParse, A.pExpr); + } spanSet(&A, &X, &X); } expr(A) ::= expr(E) COLLATE ids(C). { @@ -839,7 +822,7 @@ expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). { spanSet(&A,&X,&Y); } %endif SQLITE_OMIT_CAST -expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). { +expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). { if( Y && Y->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X); } @@ -849,7 +832,7 @@ expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). { A.pExpr->flags |= EP_Distinct; } } -expr(A) ::= ID(X) LP STAR RP(E). { +expr(A) ::= id(X) LP STAR RP(E). { A.pExpr = sqlite3ExprFunction(pParse, 0, &X); spanSet(&A,&X,&E); } @@ -888,10 +871,8 @@ expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} %type likeop {struct LikeOp} -likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.bNot = 0;} -likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.bNot = 1;} -likeop(A) ::= MATCH(X). {A.eOperator = X; A.bNot = 0;} -likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.bNot = 1;} +likeop(A) ::= LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 0;} +likeop(A) ::= NOT LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 1;} expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] { ExprList *pList; pList = sqlite3ExprListAppend(pParse,0, Y.pExpr); @@ -1199,11 +1180,10 @@ nmnum(A) ::= ON(X). {A = X;} nmnum(A) ::= DELETE(X). {A = X;} nmnum(A) ::= DEFAULT(X). {A = X;} %endif SQLITE_OMIT_PRAGMA +%token_class number INTEGER|FLOAT. plus_num(A) ::= PLUS number(X). {A = X;} plus_num(A) ::= number(X). {A = X;} minus_num(A) ::= MINUS number(X). {A = X;} -number(A) ::= INTEGER|FLOAT(X). {A = X;} - //////////////////////////// The CREATE TRIGGER command ///////////////////// %ifndef SQLITE_OMIT_TRIGGER @@ -1295,12 +1275,8 @@ trigger_cmd(A) ::= { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } // INSERT -trigger_cmd(A) ::= - insert_cmd(R) INTO trnm(X) inscollist_opt(F) valuelist(Y). - {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y.pList, Y.pSelect, R);} - trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S). - {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);} + {A = sqlite3TriggerInsertStep(pParse->db, &X, F, S, R);} // DELETE trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). @@ -1406,3 +1382,23 @@ anylist ::= . anylist ::= anylist LP anylist RP. anylist ::= anylist ANY. %endif SQLITE_OMIT_VIRTUALTABLE + + +//////////////////////// COMMON TABLE EXPRESSIONS //////////////////////////// +%type with {With*} +%type wqlist {With*} +%destructor with {sqlite3WithDelete(pParse->db, $$);} +%destructor wqlist {sqlite3WithDelete(pParse->db, $$);} + +with(A) ::= . {A = 0;} +%ifndef SQLITE_OMIT_CTE +with(A) ::= WITH wqlist(W). { A = W; } +with(A) ::= WITH RECURSIVE wqlist(W). { A = W; } + +wqlist(A) ::= nm(X) idxlist_opt(Y) AS LP select(Z) RP. { + A = sqlite3WithAdd(pParse, 0, &X, Y, Z); +} +wqlist(A) ::= wqlist(W) COMMA nm(X) idxlist_opt(Y) AS LP select(Z) RP. { + A = sqlite3WithAdd(pParse, W, &X, Y, Z); +} +%endif SQLITE_OMIT_CTE diff --git a/src/pragma.c b/src/pragma.c index bbd27b8c18..7383bce96f 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1881,8 +1881,10 @@ void sqlite3Pragma( for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx, *pPk; + Index *pPrior = 0; int loopTop; int iDataCur, iIdxCur; + int r1 = -1; if( pTab->pIndex==0 ) continue; pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); @@ -1901,9 +1903,10 @@ void sqlite3Pragma( loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int jmp2, jmp3, jmp4; - int r1; if( pPk==pIdx ) continue; - r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3); + r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, + pPrior, r1); + pPrior = pIdx; sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */ jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, 0, r1, pIdx->nColumn); diff --git a/src/random.c b/src/random.c index 7afff50885..b82566524c 100644 --- a/src/random.c +++ b/src/random.c @@ -52,6 +52,12 @@ void sqlite3_randomness(int N, void *pBuf){ sqlite3_mutex_enter(mutex); #endif + if( N<=0 ){ + wsdPrng.isInit = 0; + sqlite3_mutex_leave(mutex); + return; + } + /* Initialize the state of the random number generator once, ** the first time this routine is called. The seed value does ** not need to contain a lot of randomness since we are not @@ -79,7 +85,8 @@ void sqlite3_randomness(int N, void *pBuf){ wsdPrng.isInit = 1; } - while( N-- ){ + assert( N>0 ); + do{ wsdPrng.i++; t = wsdPrng.s[wsdPrng.i]; wsdPrng.j += t; @@ -87,7 +94,7 @@ void sqlite3_randomness(int N, void *pBuf){ wsdPrng.s[wsdPrng.j] = t; t += wsdPrng.s[wsdPrng.i]; *(zBuf++) = wsdPrng.s[t]; - } + }while( --N ); sqlite3_mutex_leave(mutex); } @@ -116,7 +123,4 @@ void sqlite3PrngRestoreState(void){ sizeof(sqlite3Prng) ); } -void sqlite3PrngResetState(void){ - GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0; -} #endif /* SQLITE_OMIT_BUILTIN_TEST */ diff --git a/src/select.c b/src/select.c index d075116749..98ed526e08 100644 --- a/src/select.c +++ b/src/select.c @@ -29,6 +29,7 @@ static void clearSelect(sqlite3 *db, Select *p){ sqlite3SelectDelete(db, p->pPrior); sqlite3ExprDelete(db, p->pLimit); sqlite3ExprDelete(db, p->pOffset); + sqlite3WithDelete(db, p->pWith); } /* @@ -461,13 +462,13 @@ static void pushOntoSorter( */ static void codeOffset( Vdbe *v, /* Generate code into this VM */ - Select *p, /* The SELECT statement being coded */ + int iOffset, /* Register holding the offset counter */ int iContinue /* Jump here to skip the current record */ ){ - if( p->iOffset && iContinue!=0 ){ + if( iOffset>0 && iContinue!=0 ){ int addr; - sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1); - addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset); + sqlite3VdbeAddOp2(v, OP_AddImm, iOffset, -1); + addr = sqlite3VdbeAddOp1(v, OP_IfNeg, iOffset); sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue); VdbeComment((v, "skip OFFSET records")); sqlite3VdbeJumpHere(v, addr); @@ -542,17 +543,16 @@ struct DistinctCtx { ** This routine generates the code for the inside of the inner loop ** of a SELECT. ** -** If srcTab and nColumn are both zero, then the pEList expressions -** are evaluated in order to get the data for this row. If nColumn>0 -** then data is pulled from srcTab and pEList is used only to get the -** datatypes for each column. +** If srcTab is negative, then the pEList expressions +** are evaluated in order to get the data for this row. If srcTab is +** zero or more, then data is pulled from srcTab and pEList is used only +** to get number columns and the datatype for each column. */ static void selectInnerLoop( Parse *pParse, /* The parser context */ Select *p, /* The complete select statement being coded */ ExprList *pEList, /* List of values being extracted */ int srcTab, /* Pull data from this table */ - int nColumn, /* Number of columns in the source table */ ExprList *pOrderBy, /* If not NULL, sort results using this key */ DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */ SelectDest *pDest, /* How to dispose of the results */ @@ -568,20 +568,15 @@ static void selectInnerLoop( int nResultCol; /* Number of result columns */ assert( v ); - if( NEVER(v==0) ) return; assert( pEList!=0 ); hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP; if( pOrderBy==0 && !hasDistinct ){ - codeOffset(v, p, iContinue); + codeOffset(v, p->iOffset, iContinue); } /* Pull the requested columns. */ - if( nColumn>0 ){ - nResultCol = nColumn; - }else{ - nResultCol = pEList->nExpr; - } + nResultCol = pEList->nExpr; if( pDest->iSdst==0 ){ pDest->iSdst = pParse->nMem+1; pDest->nSdst = nResultCol; @@ -590,9 +585,10 @@ static void selectInnerLoop( assert( pDest->nSdst==nResultCol ); } regResult = pDest->iSdst; - if( nColumn>0 ){ - for(i=0; i=0 ){ + for(i=0; ia[i].zName)); } }else if( eDest!=SRT_Exists ){ /* If the destination is an EXISTS(...) expression, the actual @@ -601,15 +597,12 @@ static void selectInnerLoop( sqlite3ExprCodeExprList(pParse, pEList, regResult, (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); } - nColumn = nResultCol; /* If the DISTINCT keyword was present on the SELECT statement ** and this row has been seen before, then do not make this row ** part of the result. */ if( hasDistinct ){ - assert( pEList!=0 ); - assert( pEList->nExpr==nColumn ); switch( pDistinct->eTnctType ){ case WHERE_DISTINCT_ORDERED: { VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ @@ -618,7 +611,7 @@ static void selectInnerLoop( /* Allocate space for the previous row */ regPrev = pParse->nMem+1; - pParse->nMem += nColumn; + pParse->nMem += nResultCol; /* Change the OP_OpenEphemeral coded earlier to an OP_Null ** sets the MEM_Cleared bit on the first register of the @@ -632,10 +625,10 @@ static void selectInnerLoop( pOp->p1 = 1; pOp->p2 = regPrev; - iJump = sqlite3VdbeCurrentAddr(v) + nColumn; - for(i=0; ia[i].pExpr); - if( ieTnctType==WHERE_DISTINCT_UNORDERED ); - codeDistinct(pParse, pDistinct->tabTnct, iContinue, nColumn, regResult); + codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, regResult); break; } } if( pOrderBy==0 ){ - codeOffset(v, p, iContinue); + codeOffset(v, p->iOffset, iContinue); } } @@ -672,7 +665,7 @@ static void selectInnerLoop( case SRT_Union: { int r1; r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); sqlite3ReleaseTempReg(pParse, r1); break; @@ -683,19 +676,33 @@ static void selectInnerLoop( ** the temporary table iParm. */ case SRT_Except: { - sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nColumn); + sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol); break; } -#endif +#endif /* SQLITE_OMIT_COMPOUND_SELECT */ /* Store the result as data using a unique key. */ + case SRT_DistTable: case SRT_Table: case SRT_EphemTab: { int r1 = sqlite3GetTempReg(pParse); testcase( eDest==SRT_Table ); testcase( eDest==SRT_EphemTab ); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); +#ifndef SQLITE_OMIT_CTE + if( eDest==SRT_DistTable ){ + /* If the destination is DistTable, then cursor (iParm+1) is open + ** on an ephemeral index. If the current row is already present + ** in the index, do not write it to the output. If not, add the + ** current row to the index and proceed with writing it to the + ** output table as well. */ + int addr = sqlite3VdbeCurrentAddr(v) + 4; + sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1); + assert( pOrderBy==0 ); + } +#endif if( pOrderBy ){ pushOntoSorter(pParse, pOrderBy, p, r1); }else{ @@ -715,7 +722,7 @@ static void selectInnerLoop( ** item into the set table with bogus data. */ case SRT_Set: { - assert( nColumn==1 ); + assert( nResultCol==1 ); pDest->affSdst = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst); if( pOrderBy ){ @@ -747,7 +754,7 @@ static void selectInnerLoop( ** of the scan loop. */ case SRT_Mem: { - assert( nColumn==1 ); + assert( nResultCol==1 ); if( pOrderBy ){ pushOntoSorter(pParse, pOrderBy, p, regResult); }else{ @@ -768,18 +775,64 @@ static void selectInnerLoop( testcase( eDest==SRT_Output ); if( pOrderBy ){ int r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); pushOntoSorter(pParse, pOrderBy, p, r1); sqlite3ReleaseTempReg(pParse, r1); }else if( eDest==SRT_Coroutine ){ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); }else{ - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn); - sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn); + sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol); + sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol); } break; } +#ifndef SQLITE_OMIT_CTE + /* Write the results into a priority queue that is order according to + ** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an + ** index with pSO->nExpr+2 columns. Build a key using pSO for the first + ** pSO->nExpr columns, then make sure all keys are unique by adding a + ** final OP_Sequence column. The last column is the record as a blob. + */ + case SRT_DistQueue: + case SRT_Queue: { + int nKey; + int r1, r2, r3; + int addrTest = 0; + ExprList *pSO; + pSO = pDest->pOrderBy; + assert( pSO ); + nKey = pSO->nExpr; + r1 = sqlite3GetTempReg(pParse); + r2 = sqlite3GetTempRange(pParse, nKey+2); + r3 = r2+nKey+1; + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r3); + if( eDest==SRT_DistQueue ){ + /* If the destination is DistQueue, then cursor (iParm+1) is open + ** on a second ephemeral index that holds all values every previously + ** added to the queue. Only add this new value if it has never before + ** been added */ + addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0, r3, 0); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3); + sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + } + for(i=0; ia[i].u.x.iOrderByCol - 1, + r2+i); + } + sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey); + sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); + if( addrTest ) sqlite3VdbeJumpHere(v, addrTest); + sqlite3ReleaseTempReg(pParse, r1); + sqlite3ReleaseTempRange(pParse, r2, nKey+2); + break; + } +#endif /* SQLITE_OMIT_CTE */ + + + #if !defined(SQLITE_OMIT_TRIGGER) /* Discard the results. This is used for SELECT statements inside ** the body of a TRIGGER. The purpose of such selects is to call @@ -868,7 +921,7 @@ int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; } ** function is responsible for seeing that this structure is eventually ** freed. */ -static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){ +static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList, int nExtra){ int nExpr; KeyInfo *pInfo; struct ExprList_item *pItem; @@ -876,7 +929,7 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){ int i; nExpr = pList->nExpr; - pInfo = sqlite3KeyInfoAlloc(db, nExpr, 1); + pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra, 1); if( pInfo ){ assert( sqlite3KeyInfoIsWriteable(pInfo) ); for(i=0, pItem=pList->a; inTab++; sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2); addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); - codeOffset(v, p, addrContinue); + codeOffset(v, p->iOffset, addrContinue); sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow); sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); }else{ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); - codeOffset(v, p, addrContinue); + codeOffset(v, p->iOffset, addrContinue); sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow); } switch( eDest ){ @@ -1202,7 +1255,7 @@ static const char *columnTypeImpl( sNC.pParse = pNC->pParse; zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol, &estWidth); } - }else if( ALWAYS(pTab->pSchema) ){ + }else if( pTab->pSchema ){ /* A real table */ assert( !pS ); if( iCol<0 ) iCol = pTab->iPKey; @@ -1363,8 +1416,9 @@ static void generateColumnNames( sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); } }else{ - sqlite3VdbeSetColName(v, i, COLNAME_NAME, - sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); + const char *z = pEList->a[i].zSpan; + z = z==0 ? sqlite3MPrintf(db, "column%d", i+1) : sqlite3DbStrDup(db, z); + sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, SQLITE_DYNAMIC); } } generateColumnTypes(pParse, pTabList, pEList); @@ -1586,8 +1640,13 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){ ** ** This routine changes the values of iLimit and iOffset only if ** a limit or offset is defined by pLimit and pOffset. iLimit and -** iOffset should have been preset to appropriate default values -** (usually but not always -1) prior to calling this routine. +** iOffset should have been preset to appropriate default values (zero) +** prior to calling this routine. +** +** The iOffset register (if it exists) is initialized to the value +** of the OFFSET. The iLimit register is initialized to LIMIT. Register +** iOffset+1 is initialized to LIMIT+OFFSET. +** ** Only if pLimit!=0 or pOffset!=0 do the limit registers get ** redefined. The UNION ALL operator uses this property to force ** the reuse of the same limit and offset registers across multiple @@ -1611,7 +1670,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ if( p->pLimit ){ p->iLimit = iLimit = ++pParse->nMem; v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ + assert( v!=0 ); if( sqlite3ExprIsInteger(p->pLimit, &n) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); @@ -1668,7 +1727,165 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ -/* Forward reference */ +#ifndef SQLITE_OMIT_CTE +/* +** This routine generates VDBE code to compute the content of a WITH RECURSIVE +** query of the form: +** +** AS ( UNION [ALL] ) +** \___________/ \_______________/ +** p->pPrior p +** +** +** There is exactly one reference to the recursive-table in the FROM clause +** of recursive-query, marked with the SrcList->a[].isRecursive flag. +** +** The setup-query runs once to generate an initial set of rows that go +** into a Queue table. Rows are extracted from the Queue table one by +** one. Each row extracted from Queue is output to pDest. Then the single +** extracted row (now in the iCurrent table) becomes the content of the +** recursive-table for a recursive-query run. The output of the recursive-query +** is added back into the Queue table. Then another row is extracted from Queue +** and the iteration continues until the Queue table is empty. +** +** If the compound query operator is UNION then no duplicate rows are ever +** inserted into the Queue table. The iDistinct table keeps a copy of all rows +** that have ever been inserted into Queue and causes duplicates to be +** discarded. If the operator is UNION ALL, then duplicates are allowed. +** +** If the query has an ORDER BY, then entries in the Queue table are kept in +** ORDER BY order and the first entry is extracted for each cycle. Without +** an ORDER BY, the Queue table is just a FIFO. +** +** If a LIMIT clause is provided, then the iteration stops after LIMIT rows +** have been output to pDest. A LIMIT of zero means to output no rows and a +** negative LIMIT means to output all rows. If there is also an OFFSET clause +** with a positive value, then the first OFFSET outputs are discarded rather +** than being sent to pDest. The LIMIT count does not begin until after OFFSET +** rows have been skipped. +*/ +static void generateWithRecursiveQuery( + Parse *pParse, /* Parsing context */ + Select *p, /* The recursive SELECT to be coded */ + SelectDest *pDest /* What to do with query results */ +){ + SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */ + int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */ + Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ + Select *pSetup = p->pPrior; /* The setup query */ + int addrTop; /* Top of the loop */ + int addrCont, addrBreak; /* CONTINUE and BREAK addresses */ + int iCurrent = 0; /* The Current table */ + int regCurrent; /* Register holding Current table */ + int iQueue; /* The Queue table */ + int iDistinct = 0; /* To ensure unique results if UNION */ + int eDest = SRT_Table; /* How to write to Queue */ + SelectDest destQueue; /* SelectDest targetting the Queue table */ + int i; /* Loop counter */ + int rc; /* Result code */ + ExprList *pOrderBy; /* The ORDER BY clause */ + Expr *pLimit, *pOffset; /* Saved LIMIT and OFFSET */ + int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */ + + /* Obtain authorization to do a recursive query */ + if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; + + /* Process the LIMIT and OFFSET clauses, if they exist */ + addrBreak = sqlite3VdbeMakeLabel(v); + computeLimitRegisters(pParse, p, addrBreak); + pLimit = p->pLimit; + pOffset = p->pOffset; + regLimit = p->iLimit; + regOffset = p->iOffset; + p->pLimit = p->pOffset = 0; + p->iLimit = p->iOffset = 0; + + /* Locate the cursor number of the Current table */ + for(i=0; ALWAYS(inSrc); i++){ + if( pSrc->a[i].isRecursive ){ + iCurrent = pSrc->a[i].iCursor; + break; + } + } + + /* Detach the ORDER BY clause from the compound SELECT */ + pOrderBy = p->pOrderBy; + p->pOrderBy = 0; + + /* Allocate cursors numbers for Queue and Distinct. The cursor number for + ** the Distinct table must be exactly one greater than Queue in order + ** for the SRT_DistTable and SRT_DistQueue destinations to work. */ + iQueue = pParse->nTab++; + if( p->op==TK_UNION ){ + eDest = pOrderBy ? SRT_DistQueue : SRT_DistTable; + iDistinct = pParse->nTab++; + }else{ + eDest = pOrderBy ? SRT_Queue : SRT_Table; + } + sqlite3SelectDestInit(&destQueue, eDest, iQueue); + + /* Allocate cursors for Current, Queue, and Distinct. */ + regCurrent = ++pParse->nMem; + sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol); + if( pOrderBy ){ + KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 1); + sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0, + (char*)pKeyInfo, P4_KEYINFO); + destQueue.pOrderBy = pOrderBy; + }else{ + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol); + } + VdbeComment((v, "Queue table")); + if( iDistinct ){ + p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0); + p->selFlags |= SF_UsesEphemeral; + } + + /* Store the results of the setup-query in Queue. */ + rc = sqlite3Select(pParse, pSetup, &destQueue); + if( rc ) goto end_of_recursive_query; + + /* Find the next row in the Queue and output that row */ + addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak); + + /* Transfer the next row in Queue over to Current */ + sqlite3VdbeAddOp1(v, OP_NullRow, iCurrent); /* To reset column cache */ + if( pOrderBy ){ + sqlite3VdbeAddOp3(v, OP_Column, iQueue, pOrderBy->nExpr+1, regCurrent); + }else{ + sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent); + } + sqlite3VdbeAddOp1(v, OP_Delete, iQueue); + + /* Output the single row in Current */ + addrCont = sqlite3VdbeMakeLabel(v); + codeOffset(v, regOffset, addrCont); + selectInnerLoop(pParse, p, p->pEList, iCurrent, + 0, 0, pDest, addrCont, addrBreak); + if( regLimit ) sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1); + sqlite3VdbeResolveLabel(v, addrCont); + + /* Execute the recursive SELECT taking the single row in Current as + ** the value for the recursive-table. Store the results in the Queue. + */ + p->pPrior = 0; + sqlite3Select(pParse, p, &destQueue); + assert( p->pPrior==0 ); + p->pPrior = pSetup; + + /* Keep running the loop until the Queue is empty */ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); + sqlite3VdbeResolveLabel(v, addrBreak); + +end_of_recursive_query: + p->pOrderBy = pOrderBy; + p->pLimit = pLimit; + p->pOffset = pOffset; + return; +} +#endif + +/* Forward references */ static int multiSelectOrderBy( Parse *pParse, /* Parsing context */ Select *p, /* The right-most of SELECTs to be coded */ @@ -1720,14 +1937,15 @@ static int multiSelect( Select *pDelete = 0; /* Chain of simple selects to delete */ sqlite3 *db; /* Database connection */ #ifndef SQLITE_OMIT_EXPLAIN - int iSub1; /* EQP id of left-hand query */ - int iSub2; /* EQP id of right-hand query */ + int iSub1 = 0; /* EQP id of left-hand query */ + int iSub2 = 0; /* EQP id of right-hand query */ #endif /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. */ assert( p && p->pPrior ); /* Calling function guarantees this much */ + assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION ); db = pParse->db; pPrior = p->pPrior; assert( pPrior->pRightmost!=pPrior ); @@ -1773,11 +1991,17 @@ static int multiSelect( goto multi_select_end; } +#ifndef SQLITE_OMIT_CTE + if( p->selFlags & SF_Recursive ){ + generateWithRecursiveQuery(pParse, p, &dest); + }else +#endif + /* Compound SELECTs that have an ORDER BY clause are handled separately. */ if( p->pOrderBy ){ return multiSelectOrderBy(pParse, p, pDest); - } + }else /* Generate code for the left and right SELECT statements. */ @@ -1912,7 +2136,7 @@ static int multiSelect( computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); iStart = sqlite3VdbeCurrentAddr(v); - selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr, + selectInnerLoop(pParse, p, p->pEList, unionTab, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); @@ -1990,7 +2214,7 @@ static int multiSelect( iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1); sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); sqlite3ReleaseTempReg(pParse, r1); - selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr, + selectInnerLoop(pParse, p, p->pEList, tab1, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); @@ -2112,7 +2336,7 @@ static int generateOutputSubroutine( /* Suppress the first OFFSET entries if there is an OFFSET clause */ - codeOffset(v, p, iContinue); + codeOffset(v, p->iOffset, iContinue); switch( pDest->eDest ){ /* Store the result as data using a unique key. @@ -2841,6 +3065,14 @@ static void substSelect( ** (21) The subquery does not use LIMIT or the outer query is not ** DISTINCT. (See ticket [752e1646fc]). ** +** (22) The subquery is not a recursive CTE. +** +** (23) The parent is not a recursive CTE, or the sub-query is not a +** compound query. This restriction is because transforming the +** parent to a compound query confuses the code that handles +** recursive queries in multiSelect(). +** +** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. @@ -2912,6 +3144,8 @@ static int flattenSubquery( if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){ return 0; /* Restriction (21) */ } + if( pSub->selFlags & SF_Recursive ) return 0; /* Restriction (22) */ + if( (p->selFlags & SF_Recursive) && pSub->pPrior ) return 0; /* (23) */ /* OBSOLETE COMMENT 1: ** Restriction 3: If the subquery is a join, make sure the subquery is @@ -3393,6 +3627,197 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ return WRC_Continue; } +#ifndef SQLITE_OMIT_CTE +/* +** Argument pWith (which may be NULL) points to a linked list of nested +** WITH contexts, from inner to outermost. If the table identified by +** FROM clause element pItem is really a common-table-expression (CTE) +** then return a pointer to the CTE definition for that table. Otherwise +** return NULL. +** +** If a non-NULL value is returned, set *ppContext to point to the With +** object that the returned CTE belongs to. +*/ +static struct Cte *searchWith( + With *pWith, /* Current outermost WITH clause */ + struct SrcList_item *pItem, /* FROM clause element to resolve */ + With **ppContext /* OUT: WITH clause return value belongs to */ +){ + const char *zName; + if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){ + With *p; + for(p=pWith; p; p=p->pOuter){ + int i; + for(i=0; inCte; i++){ + if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){ + *ppContext = p; + return &p->a[i]; + } + } + } + } + return 0; +} + +/* The code generator maintains a stack of active WITH clauses +** with the inner-most WITH clause being at the top of the stack. +** +** This routine pushes the WITH clause passed as the second argument +** onto the top of the stack. If argument bFree is true, then this +** WITH clause will never be popped from the stack. In this case it +** should be freed along with the Parse object. In other cases, when +** bFree==0, the With object will be freed along with the SELECT +** statement with which it is associated. +*/ +void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ + assert( bFree==0 || pParse->pWith==0 ); + if( pWith ){ + pWith->pOuter = pParse->pWith; + pParse->pWith = pWith; + pParse->bFreeWith = bFree; + } +} + +/* +** This function checks if argument pFrom refers to a CTE declared by +** a WITH clause on the stack currently maintained by the parser. And, +** if currently processing a CTE expression, if it is a recursive +** reference to the current CTE. +** +** If pFrom falls into either of the two categories above, pFrom->pTab +** and other fields are populated accordingly. The caller should check +** (pFrom->pTab!=0) to determine whether or not a successful match +** was found. +** +** Whether or not a match is found, SQLITE_OK is returned if no error +** occurs. If an error does occur, an error message is stored in the +** parser and some error code other than SQLITE_OK returned. +*/ +static int withExpand( + Walker *pWalker, + struct SrcList_item *pFrom +){ + Parse *pParse = pWalker->pParse; + sqlite3 *db = pParse->db; + struct Cte *pCte; /* Matched CTE (or NULL if no match) */ + With *pWith; /* WITH clause that pCte belongs to */ + + assert( pFrom->pTab==0 ); + + pCte = searchWith(pParse->pWith, pFrom, &pWith); + if( pCte ){ + Table *pTab; + ExprList *pEList; + Select *pSel; + Select *pLeft; /* Left-most SELECT statement */ + int bMayRecursive; /* True if compound joined by UNION [ALL] */ + With *pSavedWith; /* Initial value of pParse->pWith */ + + /* If pCte->zErr is non-NULL at this point, then this is an illegal + ** recursive reference to CTE pCte. Leave an error in pParse and return + ** early. If pCte->zErr is NULL, then this is not a recursive reference. + ** In this case, proceed. */ + if( pCte->zErr ){ + sqlite3ErrorMsg(pParse, pCte->zErr, pCte->zName); + return SQLITE_ERROR; + } + + assert( pFrom->pTab==0 ); + pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); + if( pTab==0 ) return WRC_Abort; + pTab->nRef = 1; + pTab->zName = sqlite3DbStrDup(db, pCte->zName); + pTab->iPKey = -1; + pTab->nRowEst = 1048576; + pTab->tabFlags |= TF_Ephemeral; + pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); + if( db->mallocFailed ) return SQLITE_NOMEM; + assert( pFrom->pSelect ); + + /* Check if this is a recursive CTE. */ + pSel = pFrom->pSelect; + bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION ); + if( bMayRecursive ){ + int i; + SrcList *pSrc = pFrom->pSelect->pSrc; + for(i=0; inSrc; i++){ + struct SrcList_item *pItem = &pSrc->a[i]; + if( pItem->zDatabase==0 + && pItem->zName!=0 + && 0==sqlite3StrICmp(pItem->zName, pCte->zName) + ){ + pItem->pTab = pTab; + pItem->isRecursive = 1; + pTab->nRef++; + pSel->selFlags |= SF_Recursive; + } + } + } + + /* Only one recursive reference is permitted. */ + if( pTab->nRef>2 ){ + sqlite3ErrorMsg( + pParse, "multiple references to recursive table: %s", pCte->zName + ); + return SQLITE_ERROR; + } + assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 )); + + pCte->zErr = "circular reference: %s"; + pSavedWith = pParse->pWith; + pParse->pWith = pWith; + sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel); + + for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); + pEList = pLeft->pEList; + if( pCte->pCols ){ + if( pEList->nExpr!=pCte->pCols->nExpr ){ + sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns", + pCte->zName, pEList->nExpr, pCte->pCols->nExpr + ); + pParse->pWith = pSavedWith; + return SQLITE_ERROR; + } + pEList = pCte->pCols; + } + + selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol); + if( bMayRecursive ){ + if( pSel->selFlags & SF_Recursive ){ + pCte->zErr = "multiple recursive references: %s"; + }else{ + pCte->zErr = "recursive reference in a subquery: %s"; + } + sqlite3WalkSelect(pWalker, pSel); + } + pCte->zErr = 0; + pParse->pWith = pSavedWith; + } + + return SQLITE_OK; +} +#endif + +#ifndef SQLITE_OMIT_CTE +/* +** If the SELECT passed as the second argument has an associated WITH +** clause, pop it from the stack stored as part of the Parse object. +** +** This function is used as the xSelectCallback2() callback by +** sqlite3SelectExpand() when walking a SELECT tree to resolve table +** names and other FROM clause elements. +*/ +static void selectPopWith(Walker *pWalker, Select *p){ + Parse *pParse = pWalker->pParse; + if( p->pWith ){ + assert( pParse->pWith==p->pWith ); + pParse->pWith = p->pWith->pOuter; + } +} +#else +#define selectPopWith 0 +#endif + /* ** This routine is a Walker callback for "expanding" a SELECT statement. ** "Expanding" means to do the following: @@ -3436,6 +3861,7 @@ static int selectExpander(Walker *pWalker, Select *p){ } pTabList = p->pSrc; pEList = p->pEList; + sqlite3WithPush(pParse, p->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. @@ -3448,12 +3874,21 @@ static int selectExpander(Walker *pWalker, Select *p){ */ for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab; + assert( pFrom->isRecursive==0 || pFrom->pTab ); + if( pFrom->isRecursive ) continue; if( pFrom->pTab!=0 ){ /* This statement has already been prepared. There is no need ** to go further. */ assert( i==0 ); +#ifndef SQLITE_OMIT_CTE + selectPopWith(pWalker, p); +#endif return WRC_Prune; } +#ifndef SQLITE_OMIT_CTE + if( withExpand(pWalker, pFrom) ) return WRC_Abort; + if( pFrom->pTab ) {} else +#endif if( pFrom->zName==0 ){ #ifndef SQLITE_OMIT_SUBQUERY Select *pSel = pFrom->pSelect; @@ -3716,6 +4151,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; + w.xSelectCallback2 = selectPopWith; sqlite3WalkSelect(&w, pSelect); } @@ -3734,7 +4170,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ ** at that point because identifiers had not yet been resolved. This ** routine is called after identifier resolution. */ -static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ +static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ Parse *pParse; int i; SrcList *pTabList; @@ -3750,13 +4186,13 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){ /* A sub-query in the FROM clause of a SELECT */ Select *pSel = pFrom->pSelect; - assert( pSel ); - while( pSel->pPrior ) pSel = pSel->pPrior; - selectAddColumnTypeAndCollation(pParse, pTab, pSel); + if( pSel ){ + while( pSel->pPrior ) pSel = pSel->pPrior; + selectAddColumnTypeAndCollation(pParse, pTab, pSel); + } } } } - return WRC_Continue; } #endif @@ -3772,10 +4208,9 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ #ifndef SQLITE_OMIT_SUBQUERY Walker w; memset(&w, 0, sizeof(w)); - w.xSelectCallback = selectAddSubqueryTypeInfo; + w.xSelectCallback2 = selectAddSubqueryTypeInfo; w.xExprCallback = exprWalkNoop; w.pParse = pParse; - w.bSelectDepthFirst = 1; sqlite3WalkSelect(&w, pSelect); #endif } @@ -3847,7 +4282,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ "argument"); pFunc->iDistinct = -1; }else{ - KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList); + KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO); } @@ -3980,50 +4415,8 @@ static void explainSimpleCount( /* ** Generate code for the SELECT statement given in the p argument. ** -** The results are distributed in various ways depending on the -** contents of the SelectDest structure pointed to by argument pDest -** as follows: -** -** pDest->eDest Result -** ------------ ------------------------------------------- -** SRT_Output Generate a row of output (using the OP_ResultRow -** opcode) for each row in the result set. -** -** SRT_Mem Only valid if the result is a single column. -** Store the first column of the first result row -** in register pDest->iSDParm then abandon the rest -** of the query. This destination implies "LIMIT 1". -** -** SRT_Set The result must be a single column. Store each -** row of result as the key in table pDest->iSDParm. -** Apply the affinity pDest->affSdst before storing -** results. Used to implement "IN (SELECT ...)". -** -** SRT_Union Store results as a key in a temporary table -** identified by pDest->iSDParm. -** -** SRT_Except Remove results from the temporary table pDest->iSDParm. -** -** SRT_Table Store results in temporary table pDest->iSDParm. -** This is like SRT_EphemTab except that the table -** is assumed to already be open. -** -** SRT_EphemTab Create an temporary table pDest->iSDParm and store -** the result there. The cursor is left open after -** returning. This is like SRT_Table except that -** this destination uses OP_OpenEphemeral to create -** the table first. -** -** SRT_Coroutine Generate a co-routine that returns a new row of -** results each time it is invoked. The entry point -** of the co-routine is stored in register pDest->iSDParm. -** -** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result -** set is not empty. -** -** SRT_Discard Throw the results away. This is used by SELECT -** statements within triggers whose only purpose is -** the side-effects of functions. +** The results are returned according to the SelectDest structure. +** See comments in sqliteInt.h for further information. ** ** This routine returns the number of errors. If any errors are ** encountered, then an appropriate error message is left in @@ -4298,7 +4691,7 @@ int sqlite3Select( */ if( pOrderBy ){ KeyInfo *pKeyInfo; - pKeyInfo = keyInfoFromExprList(pParse, pOrderBy); + pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 0); pOrderBy->iECursor = pParse->nTab++; p->addrOpenEphm[2] = addrSortIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, @@ -4330,7 +4723,7 @@ int sqlite3Select( sDistinct.tabTnct = pParse->nTab++; sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, sDistinct.tabTnct, 0, 0, - (char*)keyInfoFromExprList(pParse, p->pEList), + (char*)keyInfoFromExprList(pParse, p->pEList, 0), P4_KEYINFO); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED; @@ -4364,7 +4757,7 @@ int sqlite3Select( } /* Use the standard inner loop. */ - selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, &sDistinct, pDest, + selectInnerLoop(pParse, p, pEList, -1, pOrderBy, &sDistinct, pDest, sqlite3WhereContinueLabel(pWInfo), sqlite3WhereBreakLabel(pWInfo)); @@ -4454,7 +4847,7 @@ int sqlite3Select( ** will be converted into a Noop. */ sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy); + pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0); addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 0, (char*)pKeyInfo, P4_KEYINFO); @@ -4636,7 +5029,7 @@ int sqlite3Select( sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); finalizeAggFunctions(pParse, &sAggInfo); sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL); - selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy, + selectInnerLoop(pParse, p, p->pEList, -1, pOrderBy, &sDistinct, pDest, addrOutputRow+1, addrSetAbort); sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); @@ -4779,7 +5172,7 @@ int sqlite3Select( pOrderBy = 0; sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL); - selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, 0, + selectInnerLoop(pParse, p, p->pEList, -1, 0, 0, pDest, addrEnd, addrEnd); sqlite3ExprListDelete(db, pDel); } diff --git a/src/shell.c b/src/shell.c index 00cff6a8e5..1c4c4ad3e3 100644 --- a/src/shell.c +++ b/src/shell.c @@ -597,6 +597,7 @@ static void output_c_string(FILE *out, const char *z){ */ static void output_html_string(FILE *out, const char *z){ int i; + if( z==0 ) z = ""; while( *z ){ for(i=0; z[i] && z[i]!='<' @@ -1176,7 +1177,7 @@ static int str_in_array(const char *zStr, const char **azArray){ ** ** * For each "Goto", if the jump destination is earlier in the program ** and ends on one of: -** Yield SeekGt SeekLt RowSetRead +** Yield SeekGt SeekLt RowSetRead Rewind ** then indent all opcodes between the earlier instruction ** and "Goto" by 2 spaces. */ @@ -1188,7 +1189,7 @@ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){ int iOp; /* Index of operation in p->aiIndent[] */ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 }; - const char *azYield[] = { "Yield", "SeekLt", "SeekGt", "RowSetRead", 0 }; + const char *azYield[] = { "Yield", "SeekLt", "SeekGt", "RowSetRead", "Rewind", 0 }; const char *azGoto[] = { "Goto", 0 }; /* Try to figure out if this is really an EXPLAIN statement. If this @@ -1225,7 +1226,7 @@ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){ for(i=p2op; iaiIndent[i] += 2; } if( str_in_array(zOp, azGoto) && p2opnIndent && abYield[p2op] ){ - for(i=p2op; iaiIndent[i] += 2; + for(i=p2op+1; iaiIndent[i] += 2; } } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index abb6904196..f618aa3a12 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -2395,11 +2395,13 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** applications to access the same PRNG for other purposes. ** ** ^A call to this routine stores N bytes of randomness into buffer P. +** ^If N is less than one, then P can be a NULL pointer. ** -** ^The first time this routine is invoked (either internally or by -** the application) the PRNG is seeded using randomness obtained -** from the xRandomness method of the default [sqlite3_vfs] object. -** ^On all subsequent invocations, the pseudo-randomness is generated +** ^If this routine has not been previously called or if the previous +** call had N less than one, then the PRNG is seeded using randomness +** obtained from the xRandomness method of the default [sqlite3_vfs] object. +** ^If the previous call to this routine had an N of 1 or more then +** the pseudo-randomness is generated ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ @@ -2559,6 +2561,7 @@ int sqlite3_set_authorizer( #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ +#define SQLITE_RECURSIVE 33 /* NULL NULL */ /* ** CAPI3REF: Tracing And Profiling Functions diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 876911a468..0a607a2010 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -761,6 +761,7 @@ typedef struct VTable VTable; typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WhereInfo WhereInfo; +typedef struct With With; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and @@ -1064,7 +1065,7 @@ struct sqlite3 { #define SQLITE_ColumnCache 0x0002 /* Column cache */ #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ -#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ +/* not used 0x0010 // Was: SQLITE_IdxRealAsInt */ #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ @@ -1436,7 +1437,7 @@ struct Table { }; /* -** Allowed values for Tabe.tabFlags. +** Allowed values for Table.tabFlags. */ #define TF_Readonly 0x01 /* Read-only system table */ #define TF_Ephemeral 0x02 /* An ephemeral table */ @@ -2025,6 +2026,7 @@ struct SrcList { unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ + unsigned isRecursive :1; /* True for recursive reference in WITH */ #ifndef SQLITE_OMIT_EXPLAIN u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ #endif @@ -2151,6 +2153,7 @@ struct Select { Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ + With *pWith; /* WITH clause attached to this select. Or NULL. */ }; /* @@ -2168,11 +2171,70 @@ struct Select { #define SF_Materialize 0x0100 /* Force materialization of views */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ +#define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ /* -** The results of a select can be distributed in several ways. The -** "SRT" prefix means "SELECT Result Type". +** The results of a SELECT can be distributed in several ways, as defined +** by one of the following macros. The "SRT" prefix means "SELECT Result +** Type". +** +** SRT_Union Store results as a key in a temporary index +** identified by pDest->iSDParm. +** +** SRT_Except Remove results from the temporary index pDest->iSDParm. +** +** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result +** set is not empty. +** +** SRT_Discard Throw the results away. This is used by SELECT +** statements within triggers whose only purpose is +** the side-effects of functions. +** +** All of the above are free to ignore their ORDER BY clause. Those that +** follow must honor the ORDER BY clause. +** +** SRT_Output Generate a row of output (using the OP_ResultRow +** opcode) for each row in the result set. +** +** SRT_Mem Only valid if the result is a single column. +** Store the first column of the first result row +** in register pDest->iSDParm then abandon the rest +** of the query. This destination implies "LIMIT 1". +** +** SRT_Set The result must be a single column. Store each +** row of result as the key in table pDest->iSDParm. +** Apply the affinity pDest->affSdst before storing +** results. Used to implement "IN (SELECT ...)". +** +** SRT_EphemTab Create an temporary table pDest->iSDParm and store +** the result there. The cursor is left open after +** returning. This is like SRT_Table except that +** this destination uses OP_OpenEphemeral to create +** the table first. +** +** SRT_Coroutine Generate a co-routine that returns a new row of +** results each time it is invoked. The entry point +** of the co-routine is stored in register pDest->iSDParm +** and the result row is stored in pDest->nDest registers +** starting with pDest->iSdst. +** +** SRT_Table Store results in temporary table pDest->iSDParm. +** This is like SRT_EphemTab except that the table +** is assumed to already be open. +** +** SRT_DistTable Store results in a temporary table pDest->iSDParm. +** But also use temporary table pDest->iSDParm+1 as +** a record of all prior results and ignore any duplicate +** rows. Name means: "Distinct Table". +** +** SRT_Queue Store results in priority queue pDest->iSDParm (really +** an index). Append a sequence number so that all entries +** are distinct. +** +** SRT_DistQueue Store results in priority queue pDest->iSDParm only if +** the same record has never been stored before. The +** index at pDest->iSDParm+1 hold all prior stores. */ #define SRT_Union 1 /* Store result as keys in an index */ #define SRT_Except 2 /* Remove result from a UNION index */ @@ -2185,20 +2247,24 @@ struct Select { #define SRT_Output 5 /* Output each row of result */ #define SRT_Mem 6 /* Store result in a memory cell */ #define SRT_Set 7 /* Store results as keys in an index */ -#define SRT_Table 8 /* Store result as data with an automatic rowid */ -#define SRT_EphemTab 9 /* Create transient tab and store like SRT_Table */ -#define SRT_Coroutine 10 /* Generate a single row of result */ +#define SRT_EphemTab 8 /* Create transient tab and store like SRT_Table */ +#define SRT_Coroutine 9 /* Generate a single row of result */ +#define SRT_Table 10 /* Store result as data with an automatic rowid */ +#define SRT_DistTable 11 /* Like SRT_Table, but unique results only */ +#define SRT_Queue 12 /* Store result in an queue */ +#define SRT_DistQueue 13 /* Like SRT_Queue, but unique results only */ /* ** An instance of this object describes where to put of the results of ** a SELECT statement. */ struct SelectDest { - u8 eDest; /* How to dispose of the results. On of SRT_* above. */ - char affSdst; /* Affinity used when eDest==SRT_Set */ - int iSDParm; /* A parameter used by the eDest disposal method */ - int iSdst; /* Base register where results are written */ - int nSdst; /* Number of registers allocated */ + u8 eDest; /* How to dispose of the results. On of SRT_* above. */ + char affSdst; /* Affinity used when eDest==SRT_Set */ + int iSDParm; /* A parameter used by the eDest disposal method */ + int iSdst; /* Base register where results are written */ + int nSdst; /* Number of registers allocated */ + ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */ }; /* @@ -2301,6 +2367,7 @@ struct Parse { int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */ int nLabel; /* Number of labels used */ int *aLabel; /* Space to hold the labels */ + int iFixedOp; /* Never back out opcodes iFixedOp-1 or earlier */ int ckBase; /* Base register of data during check constraints */ int iPartIdxTab; /* Table corresponding to a partial index */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ @@ -2371,6 +2438,8 @@ struct Parse { #endif Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ + With *pWith; /* Current WITH clause, or NULL */ + u8 bFreeWith; /* True if pWith should be freed with parser */ }; /* @@ -2494,7 +2563,7 @@ struct TriggerStep { Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ Token target; /* Target table for DELETE, UPDATE, INSERT */ Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ - ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */ + ExprList *pExprList; /* SET clause for UPDATE. */ IdList *pIdList; /* Column names for INSERT */ TriggerStep *pNext; /* Next in the link-list */ TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */ @@ -2616,9 +2685,9 @@ struct Sqlite3Config { struct Walker { int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ + void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ Parse *pParse; /* Parser context. */ int walkerDepth; /* Number of subqueries */ - u8 bSelectDepthFirst; /* Do subqueries first */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ @@ -2642,6 +2711,21 @@ int sqlite3WalkSelectFrom(Walker*, Select*); #define WRC_Prune 1 /* Omit children but continue walking siblings */ #define WRC_Abort 2 /* Abandon the tree walk */ +/* +** An instance of this structure represents a set of one or more CTEs +** (common table expressions) created by a single WITH clause. +*/ +struct With { + int nCte; /* Number of CTEs in the WITH clause */ + With *pOuter; /* Containing WITH clause, or NULL */ + struct Cte { /* For each CTE in the WITH clause.... */ + char *zName; /* Name of this CTE */ + ExprList *pCols; /* List of explicit column names, or NULL */ + Select *pSelect; /* The definition of this CTE */ + const char *zErr; /* Error message for circular references */ + } a[1]; +}; + /* ** Assuming zIn points to the first byte of a UTF-8 character, ** advance zIn to point to the first byte of the next UTF-8 character. @@ -2909,7 +2993,7 @@ void sqlite3DeleteTable(sqlite3*, Table*); # define sqlite3AutoincrementEnd(X) #endif int sqlite3CodeCoroutine(Parse*, Select*, SelectDest*); -void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); +void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int); void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); int sqlite3IdListIndex(IdList*,const char*); @@ -2984,7 +3068,6 @@ int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); Vdbe *sqlite3GetVdbe(Parse*); void sqlite3PrngSaveState(void); void sqlite3PrngRestoreState(void); -void sqlite3PrngResetState(void); void sqlite3RollbackAll(sqlite3*,int); void sqlite3CodeVerifySchema(Parse*, int); void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); @@ -3004,7 +3087,7 @@ int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); int sqlite3IsRowid(const char*); void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8); void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*); -int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*); +int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, u8,u8,int,int*); void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int); @@ -3048,7 +3131,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*, - ExprList*,Select*,u8); + Select*,u8); TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8); TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*); void sqlite3DeleteTrigger(sqlite3*, Trigger*); @@ -3341,6 +3424,14 @@ const char *sqlite3JournalModename(int); int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); #endif +#ifndef SQLITE_OMIT_CTE + With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*); + void sqlite3WithDelete(sqlite3*,With*); + void sqlite3WithPush(Parse*, With*, u8); +#else +#define sqlite3WithPush(x,y,z) +#define sqlite3WithDelete(x,y) +#endif /* Declarations for functions in fkey.c. All of these are replaced by ** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 809d6b59ee..3a0895e715 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -958,6 +958,7 @@ static int auth_callback( case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break; case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break; case SQLITE_SAVEPOINT : zCode="SQLITE_SAVEPOINT"; break; + case SQLITE_RECURSIVE : zCode="SQLITE_RECURSIVE"; break; default : zCode="????"; break; } Tcl_DStringInit(&str); diff --git a/src/test1.c b/src/test1.c index a96b298661..3000288c7d 100644 --- a/src/test1.c +++ b/src/test1.c @@ -242,7 +242,28 @@ static int test_io_trace( return TCL_OK; } - +/* +** Usage: clang_sanitize_address +** +** Returns true if the program was compiled using clang with the +** -fsanitize=address switch on the command line. False otherwise. +*/ +static int clang_sanitize_address( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + char **argv /* Text of each argument */ +){ + int res = 0; +#if defined(__has_feature) +# if __has_feature(address_sanitizer) + res = 1; +# endif +#endif + Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); + return TCL_OK; +} + /* ** Usage: sqlite3_exec_printf DB FORMAT STRING ** @@ -6160,7 +6181,6 @@ static int optimization_control( { "column-cache", SQLITE_ColumnCache }, { "groupby-order", SQLITE_GroupByOrder }, { "factor-constants", SQLITE_FactorOutConst }, - { "real-as-int", SQLITE_IdxRealAsInt }, { "distinct-opt", SQLITE_DistinctOpt }, { "cover-idx-scan", SQLITE_CoverIdxScan }, { "order-by-idx-join", SQLITE_OrderByIdxJoin }, @@ -6324,6 +6344,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_busy_timeout", (Tcl_CmdProc*)test_busy_timeout }, { "printf", (Tcl_CmdProc*)test_printf }, { "sqlite3IoTrace", (Tcl_CmdProc*)test_io_trace }, + { "clang_sanitize_address", (Tcl_CmdProc*)clang_sanitize_address }, }; static struct { char *zName; diff --git a/src/test_config.c b/src/test_config.c index 5fa43515e8..dd7eac9543 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -231,6 +231,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "check", "1", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_OMIT_CTE + Tcl_SetVar2(interp, "sqlite_options", "cte", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "cte", "1", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_ENABLE_COLUMN_METADATA Tcl_SetVar2(interp, "sqlite_options", "columnmetadata", "1", TCL_GLOBAL_ONLY); #else diff --git a/src/tokenize.c b/src/tokenize.c index f27929ea5b..87553e25b0 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -303,24 +303,15 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ for(i=1; sqlite3Isdigit(z[i]); i++){} return i; } - case '#': { - for(i=1; sqlite3Isdigit(z[i]); i++){} - if( i>1 ){ - /* Parameters of the form #NNN (where NNN is a number) are used - ** internally by sqlite3NestedParse. */ - *tokenType = TK_REGISTER; - return i; - } - /* Fall through into the next case if the '#' is not followed by - ** a digit. Try to match #AAAA where AAAA is a parameter name. */ - } #ifndef SQLITE_OMIT_TCL_VARIABLE case '$': #endif case '@': /* For compatibility with MS SQL Server */ + case '#': case ':': { int n = 0; - testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' ); + testcase( z[0]=='$' ); testcase( z[0]=='@' ); + testcase( z[0]==':' ); testcase( z[0]=='#' ); *tokenType = TK_VARIABLE; for(i=1; (c=z[i])!=0; i++){ if( IdChar(c) ){ @@ -503,6 +494,7 @@ abort_parse: sqlite3DeleteTable(db, pParse->pNewTable); } + if( pParse->bFreeWith ) sqlite3WithDelete(db, pParse->pWith); sqlite3DeleteTrigger(db, pParse->pNewTrigger); for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]); sqlite3DbFree(db, pParse->azVar); diff --git a/src/trigger.c b/src/trigger.c index 1c68a708dd..fdc2a0388c 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -397,25 +397,21 @@ TriggerStep *sqlite3TriggerInsertStep( sqlite3 *db, /* The database connection */ Token *pTableName, /* Name of the table into which we insert */ IdList *pColumn, /* List of columns in pTableName to insert into */ - ExprList *pEList, /* The VALUE clause: a list of values to be inserted */ Select *pSelect, /* A SELECT statement that supplies values */ u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ ){ TriggerStep *pTriggerStep; - assert(pEList == 0 || pSelect == 0); - assert(pEList != 0 || pSelect != 0 || db->mallocFailed); + assert(pSelect != 0 || db->mallocFailed); pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName); if( pTriggerStep ){ pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); pTriggerStep->pIdList = pColumn; - pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); pTriggerStep->orconf = orconf; }else{ sqlite3IdListDelete(db, pColumn); } - sqlite3ExprListDelete(db, pEList); sqlite3SelectDelete(db, pSelect); return pTriggerStep; @@ -753,7 +749,6 @@ static int codeTriggerProgram( case TK_INSERT: { sqlite3Insert(pParse, targetSrcList(pParse, pStep), - sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3SelectDup(db, pStep->pSelect, 0), sqlite3IdListDup(db, pStep->pIdList), pParse->eOrconf diff --git a/src/vdbe.c b/src/vdbe.c index 674c252ebe..c6cc0de130 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -4427,7 +4427,6 @@ case OP_NullRow: { pC->nullRow = 1; pC->rowidIsValid = 0; pC->cacheStatus = CACHE_STALE; - assert( pC->pCursor || pC->pVtabCursor ); if( pC->pCursor ){ sqlite3BtreeClearCursor(pC->pCursor); } diff --git a/src/vdbe.h b/src/vdbe.h index 80f4faf904..76f3c2b09f 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -177,6 +177,7 @@ void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); void sqlite3VdbeChangeP5(Vdbe*, u8 P5); void sqlite3VdbeJumpHere(Vdbe*, int addr); void sqlite3VdbeChangeToNoop(Vdbe*, int addr); +int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op); void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); void sqlite3VdbeUsesBtree(Vdbe*, int); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 24ebbba13f..dc1be611cf 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -277,6 +277,7 @@ void sqlite3VdbeResolveLabel(Vdbe *v, int x){ if( j>=0 && p->aLabel ){ p->aLabel[j] = v->nOp; } + p->iFixedOp = v->nOp - 1; } /* @@ -625,7 +626,8 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){ ** the address of the next instruction to be coded. */ void sqlite3VdbeJumpHere(Vdbe *p, int addr){ - if( ALWAYS(addr>=0) ) sqlite3VdbeChangeP2(p, addr, p->nOp); + sqlite3VdbeChangeP2(p, addr, p->nOp); + p->pParse->iFixedOp = p->nOp - 1; } @@ -727,6 +729,18 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){ } } +/* +** Remove the last opcode inserted +*/ +int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){ + if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){ + sqlite3VdbeChangeToNoop(p, p->nOp-1); + return 1; + }else{ + return 0; + } +} + /* ** Change the value of the P4 operand for a specific instruction. ** This routine is useful when a large program is loaded from a diff --git a/src/walker.c b/src/walker.c index cde34ad780..016ae77a92 100644 --- a/src/walker.c +++ b/src/walker.c @@ -113,9 +113,12 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ /* ** Call sqlite3WalkExpr() for every expression in Select statement p. ** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and -** on the compound select chain, p->pPrior. Invoke the xSelectCallback() -** either before or after the walk of expressions and FROM clause, depending -** on whether pWalker->bSelectDepthFirst is false or true, respectively. +** on the compound select chain, p->pPrior. +** +** If it is not NULL, the xSelectCallback() callback is invoked before +** the walk of the expressions and FROM clause. The xSelectCallback2() +** method, if it is not NULL, is invoked following the walk of the +** expressions and FROM clause. ** ** Return WRC_Continue under normal conditions. Return WRC_Abort if ** there is an abort request. @@ -125,11 +128,13 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ */ int sqlite3WalkSelect(Walker *pWalker, Select *p){ int rc; - if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue; + if( p==0 || (pWalker->xSelectCallback==0 && pWalker->xSelectCallback2==0) ){ + return WRC_Continue; + } rc = WRC_Continue; pWalker->walkerDepth++; while( p ){ - if( !pWalker->bSelectDepthFirst ){ + if( pWalker->xSelectCallback ){ rc = pWalker->xSelectCallback(pWalker, p); if( rc ) break; } @@ -139,12 +144,8 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){ pWalker->walkerDepth--; return WRC_Abort; } - if( pWalker->bSelectDepthFirst ){ - rc = pWalker->xSelectCallback(pWalker, p); - /* Depth-first search is currently only used for - ** selectAddSubqueryTypeInfo() and that routine always returns - ** WRC_Continue (0). So the following branch is never taken. */ - if( NEVER(rc) ) break; + if( pWalker->xSelectCallback2 ){ + pWalker->xSelectCallback2(pWalker, p); } p = p->pPrior; } diff --git a/src/where.c b/src/where.c index 59028b9921..6fac5e5ed1 100644 --- a/src/where.c +++ b/src/where.c @@ -667,7 +667,7 @@ static int isLikeOrGlob( } assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */ - pRight = pList->a[0].pExpr; + pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr); op = pRight->op; if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; @@ -1710,7 +1710,7 @@ static void constructAutomaticIndex( /* Fill the automatic index with content */ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); regRecord = sqlite3GetTempReg(pParse); - sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0); + sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); @@ -1751,7 +1751,8 @@ static sqlite3_index_info *allocateIndexInfo( assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); - if( pTerm->eOperator & (WO_ISNULL) ) continue; + testcase( pTerm->eOperator & WO_ALL ); + if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; nTerm++; } @@ -1803,7 +1804,8 @@ static sqlite3_index_info *allocateIndexInfo( assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); - if( pTerm->eOperator & (WO_ISNULL) ) continue; + testcase( pTerm->eOperator & WO_ALL ); + if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; @@ -3408,10 +3410,16 @@ static Bitmask codeOneLoopStart( static const u8 aStep[] = { OP_Next, OP_Prev }; static const u8 aStart[] = { OP_Rewind, OP_Last }; assert( bRev==0 || bRev==1 ); - pLevel->op = aStep[bRev]; - pLevel->p1 = iCur; - pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + if( pTabItem->isRecursive ){ + /* Tables marked isRecursive have only a single row that is stored in + ** a pseudo-cursor. No need to Rewind or Next such cursors. */ + pLevel->op = OP_Noop; + }else{ + pLevel->op = aStep[bRev]; + pLevel->p1 = iCur; + pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); + pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + } } /* Insert code to test every subexpression that can be completely @@ -4196,6 +4204,7 @@ static int whereLoopAddBtree( && !pSrc->notIndexed && HasRowid(pTab) && !pSrc->isCorrelated + && !pSrc->isRecursive ){ /* Generate auto-index WhereLoops */ WhereTerm *pTerm; @@ -5430,9 +5439,12 @@ WhereInfo *sqlite3WhereBegin( /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. */ - if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){ - sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL); - pWhere = 0; + for(ii=0; iinTerm; ii++){ + if( nTabList==0 || sqlite3ExprIsConstantNotJoin(sWLB.pWC->a[ii].pExpr) ){ + sqlite3ExprIfFalse(pParse, sWLB.pWC->a[ii].pExpr, pWInfo->iBreak, + SQLITE_JUMPIFNULL); + sWLB.pWC->a[ii].wtFlags |= TERM_CODED; + } } /* Special case: No FROM clause diff --git a/test/auth.test b/test/auth.test index 5e91b33eaa..43e53ef2e3 100644 --- a/test/auth.test +++ b/test/auth.test @@ -2080,6 +2080,42 @@ ifcapable {altertable} { execsql {DROP TABLE t5} } ;# ifcapable altertable +ifcapable {cte} { + do_test auth-1.310 { + proc auth {code arg1 arg2 arg3 arg4} { + if {$code=="SQLITE_RECURSIVE"} { + return SQLITE_DENY + } + return SQLITE_OK + } + db eval { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,2),(3,4),(5,6); + } + } {} + do_catchsql_test auth-1.311 { + WITH + auth1311(x,y) AS (SELECT a+b, b-a FROM t1) + SELECT * FROM auth1311 ORDER BY x; + } {0 {3 1 7 1 11 1}} + do_catchsql_test auth-1.312 { + WITH RECURSIVE + auth1312(x,y) AS (SELECT a+b, b-a FROM t1) + SELECT x, y FROM auth1312 ORDER BY x; + } {0 {3 1 7 1 11 1}} + do_catchsql_test auth-1.313 { + WITH RECURSIVE + auth1313(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM auth1313 WHERE x<5) + SELECT * FROM t1; + } {0 {1 2 3 4 5 6}} + do_catchsql_test auth-1.314 { + WITH RECURSIVE + auth1314(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM auth1314 WHERE x<5) + SELECT * FROM t1 LEFT JOIN auth1314; + } {1 {not authorized}} +} ;# ifcapable cte + do_test auth-2.1 { proc auth {code arg1 arg2 arg3 arg4} { if {$code=="SQLITE_READ" && $arg1=="t3" && $arg2=="x"} { diff --git a/test/capi3.test b/test/capi3.test index 9d7434d25d..f0234e8897 100644 --- a/test/capi3.test +++ b/test/capi3.test @@ -179,16 +179,18 @@ do_test capi3-3.4 { do_test capi3-3.5 { sqlite3_close $db2 } {SQLITE_OK} -do_test capi3-3.6.1-misuse { - sqlite3_close $db2 -} {SQLITE_MISUSE} -do_test capi3-3.6.2-misuse { - sqlite3_errmsg $db2 -} {library routine called out of sequence} -ifcapable {utf16} { - do_test capi3-3.6.3-misuse { - utf8 [sqlite3_errmsg16 $db2] +if {[clang_sanitize_address]==0} { + do_test capi3-3.6.1-misuse { + sqlite3_close $db2 + } {SQLITE_MISUSE} + do_test capi3-3.6.2-misuse { + sqlite3_errmsg $db2 } {library routine called out of sequence} + ifcapable {utf16} { + do_test capi3-3.6.3-misuse { + utf8 [sqlite3_errmsg16 $db2] + } {library routine called out of sequence} + } } do_test capi3-3.7 { @@ -661,10 +663,12 @@ do_test capi3-6.3 { sqlite3_finalize $STMT } {SQLITE_OK} -do_test capi3-6.4-misuse { - db cache flush - sqlite3_close $DB -} {SQLITE_OK} +if {[clang_sanitize_address]==0} { + do_test capi3-6.4-misuse { + db cache flush + sqlite3_close $DB + } {SQLITE_OK} +} db close # This procedure sets the value of the file-format in file 'test.db' @@ -1060,11 +1064,13 @@ if {[llength [info commands sqlite3_sleep]]>0} { } # Ticket #1219: Make sure binding APIs can handle a NULL pointer. -# -do_test capi3-14.1-misuse { - set rc [catch {sqlite3_bind_text 0 1 hello 5} msg] - lappend rc $msg -} {1 SQLITE_MISUSE} +# +if {[clang_sanitize_address]==0} { + do_test capi3-14.1-misuse { + set rc [catch {sqlite3_bind_text 0 1 hello 5} msg] + lappend rc $msg + } {1 SQLITE_MISUSE} +} # Ticket #1650: Honor the nBytes parameter to sqlite3_prepare. # diff --git a/test/capi3c.test b/test/capi3c.test index 14545c0a68..6388717e00 100644 --- a/test/capi3c.test +++ b/test/capi3c.test @@ -169,16 +169,18 @@ do_test capi3c-3.4 { do_test capi3c-3.5 { sqlite3_close $db2 } {SQLITE_OK} -do_test capi3c-3.6.1-misuse { - sqlite3_close $db2 -} {SQLITE_MISUSE} -do_test capi3c-3.6.2-misuse { - sqlite3_errmsg $db2 -} {library routine called out of sequence} -ifcapable {utf16} { - do_test capi3c-3.6.3-misuse { - utf8 [sqlite3_errmsg16 $db2] +if {[clang_sanitize_address]==0} { + do_test capi3c-3.6.1-misuse { + sqlite3_close $db2 + } {SQLITE_MISUSE} + do_test capi3c-3.6.2-misuse { + sqlite3_errmsg $db2 } {library routine called out of sequence} + ifcapable {utf16} { + do_test capi3c-3.6.3-misuse { + utf8 [sqlite3_errmsg16 $db2] + } {library routine called out of sequence} + } } # rename sqlite3_open "" @@ -627,13 +629,17 @@ check_data $STMT capi3c-6.3 {INTEGER} {1} {1.0} {1} do_test capi3c-6.3 { sqlite3_finalize $STMT } {SQLITE_OK} -do_test capi3c-6.4 { - db cache flush - sqlite3_close $DB -} {SQLITE_OK} -do_test capi3c-6.99-misuse { +if {[clang_sanitize_address]==0} { + do_test capi3c-6.4 { + db cache flush + sqlite3_close $DB + } {SQLITE_OK} + do_test capi3c-6.99-misuse { + db close + } {} +} else { db close -} {} +} # This procedure sets the value of the file-format in file 'test.db' # to $newval. Also, the schema cookie is incremented. diff --git a/test/corruptH.test b/test/corruptH.test new file mode 100644 index 0000000000..23f80632b1 --- /dev/null +++ b/test/corruptH.test @@ -0,0 +1,150 @@ +# 2014-01-20 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix corruptH + +# Do not use a codec for tests in this file, as the database file is +# manipulated directly using tcl scripts (using the [hexio_write] command). +# +do_not_use_codec +database_may_be_corrupt + +# Initialize the database. +# +do_execsql_test 1.1 { + PRAGMA page_size=1024; + + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + + CREATE TABLE t2(x); + INSERT INTO t2 VALUES(randomblob(200)); + INSERT INTO t2 SELECT randomblob(200) FROM t2; + INSERT INTO t2 SELECT randomblob(200) FROM t2; + INSERT INTO t2 SELECT randomblob(200) FROM t2; + INSERT INTO t2 SELECT randomblob(200) FROM t2; + INSERT INTO t2 SELECT randomblob(200) FROM t2; + INSERT INTO t2 SELECT randomblob(200) FROM t2; +} {} + +# Corrupt the file so that the root page of t1 is also linked into t2 as +# a leaf page. +# +do_test 1.2 { + db eval { SELECT name, rootpage FROM sqlite_master } { + set r($name) $rootpage + } + db close + hexio_write test.db [expr {($r(t2)-1)*1024 + 11}] [format %.2X $r(t1)] + sqlite3 db test.db +} {} + +do_test 1.3 { + db eval { PRAGMA secure_delete=1 } + list [catch { + db eval { SELECT * FROM t1 WHERE a IN (1, 2) } { + db eval { DELETE FROM t2 } + } + } msg] $msg +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +reset_db + +# Initialize the database. +# +do_execsql_test 2.1 { + PRAGMA page_size=1024; + + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + + CREATE TABLE t3(x); + + CREATE TABLE t2(x PRIMARY KEY) WITHOUT ROWID; + INSERT INTO t2 VALUES(randomblob(100)); + + DROP TABLE t3; +} {} + +do_test 2.2 { + db eval { SELECT name, rootpage FROM sqlite_master } { + set r($name) $rootpage + } + db close + set fl [hexio_get_int [hexio_read test.db 32 4]] + + hexio_write test.db [expr {($fl-1) * 1024 + 0}] 00000000 + hexio_write test.db [expr {($fl-1) * 1024 + 4}] 00000001 + hexio_write test.db [expr {($fl-1) * 1024 + 8}] [format %.8X $r(t1)] + hexio_write test.db 36 00000002 + + sqlite3 db test.db +} {} + +do_test 2.3 { + list [catch { + db eval { SELECT * FROM t1 WHERE a IN (1, 2) } { + db eval { + INSERT INTO t2 SELECT randomblob(100) FROM t2; + INSERT INTO t2 SELECT randomblob(100) FROM t2; + INSERT INTO t2 SELECT randomblob(100) FROM t2; + INSERT INTO t2 SELECT randomblob(100) FROM t2; + INSERT INTO t2 SELECT randomblob(100) FROM t2; + } + } + } msg] $msg +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +reset_db + +# Initialize the database. +# +do_execsql_test 3.1 { + PRAGMA page_size=1024; + + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + + CREATE TABLE t2(c INTEGER PRAGMA KEY, d); + INSERT INTO t2 VALUES(1, randomblob(1100)); +} {} + +do_test 3.2 { + db eval { SELECT name, rootpage FROM sqlite_master } { + set r($name) $rootpage + } + db close + + hexio_write test.db [expr {($r(t2)-1) * 1024 + 1020}] 00000002 + + sqlite3 db test.db +} {} + +do_test 3.3 { + list [catch { + db eval { SELECT * FROM t1 WHERE a IN (1, 2) } { + db eval { + DELETE FROM t2 WHERE c=1; + } + } + } msg] $msg +} {1 {database disk image is malformed}} + +finish_test + diff --git a/test/e_fkey.test b/test/e_fkey.test index 022611695d..921e967176 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -2946,38 +2946,45 @@ proc test_on_update_recursion {limit} { " } -do_test e_fkey-63.1.1 { - test_on_delete_recursion $SQLITE_MAX_TRIGGER_DEPTH -} {0 0} -do_test e_fkey-63.1.2 { - test_on_delete_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1] -} {1 {too many levels of trigger recursion}} -do_test e_fkey-63.1.3 { - sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5 - test_on_delete_recursion 5 -} {0 0} -do_test e_fkey-63.1.4 { - test_on_delete_recursion 6 -} {1 {too many levels of trigger recursion}} -do_test e_fkey-63.1.5 { - sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000 -} {5} -do_test e_fkey-63.2.1 { - test_on_update_recursion $SQLITE_MAX_TRIGGER_DEPTH -} {0 0} -do_test e_fkey-63.2.2 { - test_on_update_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1] -} {1 {too many levels of trigger recursion}} -do_test e_fkey-63.2.3 { - sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5 - test_on_update_recursion 5 -} {0 0} -do_test e_fkey-63.2.4 { - test_on_update_recursion 6 -} {1 {too many levels of trigger recursion}} -do_test e_fkey-63.2.5 { - sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000 -} {5} +# If the current build was created using clang with the -fsanitize=address +# switch, then the library uses considerably more stack space than usual. +# So much more, that some of the following tests cause stack overflows +# if they are run under this configuration. +# +if {[clang_sanitize_address]==0} { + do_test e_fkey-63.1.1 { + test_on_delete_recursion $SQLITE_MAX_TRIGGER_DEPTH + } {0 0} + do_test e_fkey-63.1.2 { + test_on_delete_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1] + } {1 {too many levels of trigger recursion}} + do_test e_fkey-63.1.3 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5 + test_on_delete_recursion 5 + } {0 0} + do_test e_fkey-63.1.4 { + test_on_delete_recursion 6 + } {1 {too many levels of trigger recursion}} + do_test e_fkey-63.1.5 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000 + } {5} + do_test e_fkey-63.2.1 { + test_on_update_recursion $SQLITE_MAX_TRIGGER_DEPTH + } {0 0} + do_test e_fkey-63.2.2 { + test_on_update_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1] + } {1 {too many levels of trigger recursion}} + do_test e_fkey-63.2.3 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5 + test_on_update_recursion 5 + } {0 0} + do_test e_fkey-63.2.4 { + test_on_update_recursion 6 + } {1 {too many levels of trigger recursion}} + do_test e_fkey-63.2.5 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000 + } {5} +} #------------------------------------------------------------------------- # The setting of the recursive_triggers pragma does not affect foreign diff --git a/test/fts3join.test b/test/fts3join.test new file mode 100644 index 0000000000..64363639db --- /dev/null +++ b/test/fts3join.test @@ -0,0 +1,66 @@ +# 2014 January 4 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# This file implements regression tests for SQLite library. The +# focus of this script is testing the FTS3 module. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix fts3join + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft1 USING fts4(x); + INSERT INTO ft1 VALUES('aaa aaa'); + INSERT INTO ft1 VALUES('aaa bbb'); + INSERT INTO ft1 VALUES('bbb aaa'); + INSERT INTO ft1 VALUES('bbb bbb'); + + CREATE TABLE t1(id, y); + INSERT INTO t1 VALUES(1, 'aaa'); + INSERT INTO t1 VALUES(2, 'bbb'); +} + +do_execsql_test 1.1 { + SELECT docid FROM ft1, t1 WHERE ft1 MATCH y AND id=1; +} {1 2 3} + +do_execsql_test 1.2 { + SELECT docid FROM ft1, t1 WHERE ft1 MATCH y AND id=1 ORDER BY docid; +} {1 2 3} + +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE ft2 USING fts4(x); + CREATE VIRTUAL TABLE ft3 USING fts4(y); + + INSERT INTO ft2 VALUES('abc'); + INSERT INTO ft2 VALUES('def'); + INSERT INTO ft3 VALUES('ghi'); + INSERT INTO ft3 VALUES('abc'); +} + +do_execsql_test 2.1 { SELECT * FROM ft2, ft3 WHERE x MATCH y; } {abc abc} +do_execsql_test 2.2 { SELECT * FROM ft2, ft3 WHERE y MATCH x; } {abc abc} +do_execsql_test 2.3 { SELECT * FROM ft3, ft2 WHERE x MATCH y; } {abc abc} +do_execsql_test 2.4 { SELECT * FROM ft3, ft2 WHERE y MATCH x; } {abc abc} + +do_catchsql_test 2.5 { + SELECT * FROM ft3, ft2 WHERE y MATCH x AND x MATCH y; +} {1 {unable to use function MATCH in the requested context}} + +finish_test + + diff --git a/test/keyword1.test b/test/keyword1.test index 94831201ce..2bc1ff5494 100644 --- a/test/keyword1.test +++ b/test/keyword1.test @@ -65,6 +65,7 @@ set kwlist { pragma query raise + recursive regexp reindex release @@ -80,6 +81,8 @@ set kwlist { vacuum view virtual + with + without }; set exprkw { cast diff --git a/test/like.test b/test/like.test index 230dc74fed..923272cfb2 100644 --- a/test/like.test +++ b/test/like.test @@ -893,5 +893,60 @@ do_test like-11.10 { } } {abc abcd sort {} t11cb} +# A COLLATE clause on the pattern does not change the result of a +# LIKE operator. +# +do_execsql_test like-12.1 { + CREATE TABLE t12nc(id INTEGER, x TEXT UNIQUE COLLATE nocase); + INSERT INTO t12nc VALUES(1,'abcde'),(2,'uvwxy'),(3,'ABCDEF'); + CREATE TABLE t12b(id INTEGER, x TEXT UNIQUE COLLATE binary); + INSERT INTO t12b VALUES(1,'abcde'),(2,'uvwxy'),(3,'ABCDEF'); + SELECT id FROM t12nc WHERE x LIKE 'abc%' ORDER BY +id; +} {1 3} +do_execsql_test like-12.2 { + SELECT id FROM t12b WHERE x LIKE 'abc%' ORDER BY +id; +} {1 3} +do_execsql_test like-12.3 { + SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id; +} {1 3} +do_execsql_test like-12.4 { + SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id; +} {1 3} +do_execsql_test like-12.5 { + SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id; +} {1 3} +do_execsql_test like-12.6 { + SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id; +} {1 3} + +# Adding a COLLATE clause to the pattern of a LIKE operator does nothing +# to change the suitability of using an index to satisfy that LIKE +# operator. +# +do_execsql_test like-12.11 { + EXPLAIN QUERY PLAN + SELECT id FROM t12nc WHERE x LIKE 'abc%' ORDER BY +id; +} {/SEARCH/} +do_execsql_test like-12.12 { + EXPLAIN QUERY PLAN + SELECT id FROM t12b WHERE x LIKE 'abc%' ORDER BY +id; +} {/SCAN/} +do_execsql_test like-12.13 { + EXPLAIN QUERY PLAN + SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id; +} {/SEARCH/} +do_execsql_test like-12.14 { + EXPLAIN QUERY PLAN + SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id; +} {/SCAN/} +do_execsql_test like-12.15 { + EXPLAIN QUERY PLAN + SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id; +} {/SEARCH/} +do_execsql_test like-12.16 { + EXPLAIN QUERY PLAN + SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id; +} {/SCAN/} + finish_test diff --git a/test/misc1.test b/test/misc1.test index 8573d349b3..f886f896e2 100644 --- a/test/misc1.test +++ b/test/misc1.test @@ -592,4 +592,17 @@ do_test misc1-18.1 { expr {$n>=100} } {1} +# 2014-01-10: In a CREATE TABLE AS, if one or more of the column names +# are an empty string, that is still OK. +# +do_execsql_test misc1-19.1 { + CREATE TABLE t19 AS SELECT 1, 2 AS '', 3; + SELECT * FROM t19; +} {1 2 3} +do_execsql_test misc1-19.2 { + CREATE TABLE t19b AS SELECT 4 AS '', 5 AS '', 6 AS ''; + SELECT * FROM t19b; +} {4 5 6} + + finish_test diff --git a/test/misc7.test b/test/misc7.test index ec042ff0ee..8fd5fe7546 100644 --- a/test/misc7.test +++ b/test/misc7.test @@ -15,9 +15,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -do_test misc7-1-misuse { - c_misuse_test -} {} +if {[clang_sanitize_address]==0} { + do_test misc7-1-misuse { + c_misuse_test + } {} +} do_test misc7-2 { c_realloc_test diff --git a/test/misuse.test b/test/misuse.test index 71ee0118c8..d5d836cbfb 100644 --- a/test/misuse.test +++ b/test/misuse.test @@ -171,37 +171,40 @@ do_test misuse-4.3 { } msg] lappend v $msg $r } {0 {} SQLITE_BUSY} -do_test misuse-4.4 { + +if {[clang_sanitize_address]==0} { + do_test misuse-4.4 { # Flush the TCL statement cache here, otherwise the sqlite3_close() will # fail because there are still un-finalized() VDBEs. - db cache flush - sqlite3_close $::DB - catchsql2 {SELECT * FROM t1} -} {1 {library routine called out of sequence}} -do_test misuse-4.5 { - catchsql { - SELECT * FROM t1 - } -} {1 {library routine called out of sequence}} + db cache flush + sqlite3_close $::DB + catchsql2 {SELECT * FROM t1} + } {1 {library routine called out of sequence}} + do_test misuse-4.5 { + catchsql { + SELECT * FROM t1 + } + } {1 {library routine called out of sequence}} -# Attempt to use a database after it has been closed. -# -do_test misuse-5.1 { - db close - sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db] - execsql { - SELECT * FROM t1 - } -} {1 2} -do_test misuse-5.2 { - catchsql2 {SELECT * FROM t1} -} {0 {a b 1 2}} -do_test misuse-5.3 { - db close - set r [catch { - sqlite3_prepare $::DB {SELECT * FROM t1} -1 TAIL - } msg] - lappend r $msg -} {1 {(21) library routine called out of sequence}} + # Attempt to use a database after it has been closed. + # + do_test misuse-5.1 { + db close + sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db] + execsql { + SELECT * FROM t1 + } + } {1 2} + do_test misuse-5.2 { + catchsql2 {SELECT * FROM t1} + } {0 {a b 1 2}} + do_test misuse-5.3 { + db close + set r [catch { + sqlite3_prepare $::DB {SELECT * FROM t1} -1 TAIL + } msg] + lappend r $msg + } {1 {(21) library routine called out of sequence}} +} finish_test diff --git a/test/select7.test b/test/select7.test index e8fc440006..6816b9fcb9 100644 --- a/test/select7.test +++ b/test/select7.test @@ -138,21 +138,23 @@ ifcapable {subquery && compound} { # Verify that an error occurs if you have too many terms on a # compound select statement. # -ifcapable compound { - if {$SQLITE_MAX_COMPOUND_SELECT>0} { - set sql {SELECT 0} - set result 0 - for {set i 1} {$i<$SQLITE_MAX_COMPOUND_SELECT} {incr i} { - append sql " UNION ALL SELECT $i" - lappend result $i +if {[clang_sanitize_address]==0} { + ifcapable compound { + if {$SQLITE_MAX_COMPOUND_SELECT>0} { + set sql {SELECT 0} + set result 0 + for {set i 1} {$i<$SQLITE_MAX_COMPOUND_SELECT} {incr i} { + append sql " UNION ALL SELECT $i" + lappend result $i + } + do_test select7-6.1 { + catchsql $sql + } [list 0 $result] + append sql { UNION ALL SELECT 99999999} + do_test select7-6.2 { + catchsql $sql + } {1 {too many terms in compound SELECT}} } - do_test select7-6.1 { - catchsql $sql - } [list 0 $result] - append sql { UNION ALL SELECT 99999999} - do_test select7-6.2 { - catchsql $sql - } {1 {too many terms in compound SELECT}} } } diff --git a/test/spellfix.test b/test/spellfix.test index 9eb7a45f5d..3e6fdf7eec 100644 --- a/test/spellfix.test +++ b/test/spellfix.test @@ -221,6 +221,51 @@ foreach {tn word res} { } $res } +#------------------------------------------------------------------------- +# Try some queries by rowid. +# +do_execsql_test 6.1.1 { + SELECT word FROM t3 WHERE rowid = 10; +} {keener} +do_execsql_test 6.1.2 { + SELECT word, distance FROM t3 WHERE rowid = 10; +} {keener {}} +do_execsql_test 6.1.3 { + SELECT word, distance FROM t3 WHERE rowid = 10 AND word MATCH 'kiiner'; +} {keener 300} + +proc trace_callback {sql} { + if {[string range $sql 0 2] == "-- "} { + lappend ::trace [string range $sql 3 end] + } +} + +proc do_tracesql_test {tn sql {res {}}} { + set ::trace [list] + uplevel [list do_test $tn [subst -nocommands { + set vals [execsql {$sql}] + concat [set vals] [set ::trace] + }] [list {*}$res]] +} + +db trace trace_callback +do_tracesql_test 6.2.1 { + SELECT word FROM t3 WHERE rowid = 10; +} {keener + {SELECT word, rank, NULL, langid, id FROM "main"."t3_vocab" WHERE rowid=?} +} +do_tracesql_test 6.2.2 { + SELECT word, distance FROM t3 WHERE rowid = 10; +} {keener {} + {SELECT word, rank, NULL, langid, id FROM "main"."t3_vocab" WHERE rowid=?} +} +do_tracesql_test 6.2.3 { + SELECT word, distance FROM t3 WHERE rowid = 10 AND word MATCH 'kiiner'; +} {keener 300 + {SELECT id, word, rank, k1 FROM "main"."t3_vocab" WHERE langid=0 AND k2>=?1 AND k20 + AND NOT EXISTS ( + SELECT 1 + FROM digits AS lp + WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1) + OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1) + OR z.z = substr(s, (((ind-1)/3) % 3) * 3 + + ((ind-1)/27) * 27 + lp + + ((lp-1) / 3) * 6, 1) + ) + ) + SELECT s FROM x WHERE ind=0; +} {534678912672195348198342567859761423426853791713924856961537284287419635345286179} + + +# Test cases to illustrate on the ORDER BY clause on a recursive query can be +# used to control depth-first versus breath-first search in a tree. +# +do_execsql_test 9.1 { + CREATE TABLE org( + name TEXT PRIMARY KEY, + boss TEXT REFERENCES org + ) WITHOUT ROWID; + INSERT INTO org VALUES('Alice',NULL); + INSERT INTO org VALUES('Bob','Alice'); + INSERT INTO org VALUES('Cindy','Alice'); + INSERT INTO org VALUES('Dave','Bob'); + INSERT INTO org VALUES('Emma','Bob'); + INSERT INTO org VALUES('Fred','Cindy'); + INSERT INTO org VALUES('Gail','Cindy'); + INSERT INTO org VALUES('Harry','Dave'); + INSERT INTO org VALUES('Ingrid','Dave'); + INSERT INTO org VALUES('Jim','Emma'); + INSERT INTO org VALUES('Kate','Emma'); + INSERT INTO org VALUES('Lanny','Fred'); + INSERT INTO org VALUES('Mary','Fred'); + INSERT INTO org VALUES('Noland','Gail'); + INSERT INTO org VALUES('Olivia','Gail'); + -- The above are all under Alice. Add a few more records for people + -- not in Alice's group, just to prove that they won't be selected. + INSERT INTO org VALUES('Xaviar',NULL); + INSERT INTO org VALUES('Xia','Xaviar'); + INSERT INTO org VALUES('Xerxes','Xaviar'); + INSERT INTO org VALUES('Xena','Xia'); + -- Find all members of Alice's group, breath-first order + WITH RECURSIVE + under_alice(name,level) AS ( + VALUES('Alice','0') + UNION ALL + SELECT org.name, under_alice.level+1 + FROM org, under_alice + WHERE org.boss=under_alice.name + ORDER BY 2 + ) + SELECT group_concat(substr('...............',1,level*3) || name,x'0a') + FROM under_alice; +} {{Alice +...Bob +...Cindy +......Dave +......Emma +......Fred +......Gail +.........Harry +.........Ingrid +.........Jim +.........Kate +.........Lanny +.........Mary +.........Noland +.........Olivia}} + +# The previous query used "ORDER BY level" to yield a breath-first search. +# Change that to "ORDER BY level DESC" for a depth-first search. +# +do_execsql_test 9.2 { + WITH RECURSIVE + under_alice(name,level) AS ( + VALUES('Alice','0') + UNION ALL + SELECT org.name, under_alice.level+1 + FROM org, under_alice + WHERE org.boss=under_alice.name + ORDER BY 2 DESC + ) + SELECT group_concat(substr('...............',1,level*3) || name,x'0a') + FROM under_alice; +} {{Alice +...Bob +......Dave +.........Harry +.........Ingrid +......Emma +.........Jim +.........Kate +...Cindy +......Fred +.........Lanny +.........Mary +......Gail +.........Noland +.........Olivia}} + +# Without an ORDER BY clause, the recursive query should use a FIFO, +# resulting in a breath-first search. +# +do_execsql_test 9.3 { + WITH RECURSIVE + under_alice(name,level) AS ( + VALUES('Alice','0') + UNION ALL + SELECT org.name, under_alice.level+1 + FROM org, under_alice + WHERE org.boss=under_alice.name + ) + SELECT group_concat(substr('...............',1,level*3) || name,x'0a') + FROM under_alice; +} {{Alice +...Bob +...Cindy +......Dave +......Emma +......Fred +......Gail +.........Harry +.........Ingrid +.........Jim +.........Kate +.........Lanny +.........Mary +.........Noland +.........Olivia}} + +finish_test diff --git a/test/with2.test b/test/with2.test new file mode 100644 index 0000000000..d702f8c962 --- /dev/null +++ b/test/with2.test @@ -0,0 +1,391 @@ +# 2014 January 11 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the WITH clause. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix with2 + +ifcapable {!cte} { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); +} + +do_execsql_test 1.1 { + WITH x1 AS (SELECT * FROM t1) + SELECT sum(a) FROM x1; +} {3} + +do_execsql_test 1.2 { + WITH x1 AS (SELECT * FROM t1) + SELECT (SELECT sum(a) FROM x1); +} {3} + +do_execsql_test 1.3 { + WITH x1 AS (SELECT * FROM t1) + SELECT (SELECT sum(a) FROM x1); +} {3} + +do_execsql_test 1.4 { + CREATE TABLE t2(i); + INSERT INTO t2 VALUES(2); + INSERT INTO t2 VALUES(3); + INSERT INTO t2 VALUES(5); + + WITH x1 AS (SELECT i FROM t2), + i(a) AS ( + SELECT min(i)-1 FROM x1 UNION SELECT a+1 FROM i WHERE a<10 + ) + SELECT a FROM i WHERE a NOT IN x1 +} {1 4 6 7 8 9 10} + +do_execsql_test 1.5 { + WITH x1 AS (SELECT a FROM t1), + x2 AS (SELECT i FROM t2), + x3 AS (SELECT * FROM x1, x2 WHERE x1.a IN x2 AND x2.i IN x1) + SELECT * FROM x3 +} {2 2} + +do_execsql_test 1.6 { + CREATE TABLE t3 AS SELECT 3 AS x; + CREATE TABLE t4 AS SELECT 4 AS x; + + WITH x1 AS (SELECT * FROM t3), + x2 AS ( + WITH t3 AS (SELECT * FROM t4) + SELECT * FROM x1 + ) + SELECT * FROM x2; +} {3} + +do_execsql_test 1.7 { + WITH x2 AS ( + WITH t3 AS (SELECT * FROM t4) + SELECT * FROM t3 + ) + SELECT * FROM x2; +} {4} + +do_execsql_test 1.8 { + WITH x2 AS ( + WITH t3 AS (SELECT * FROM t4) + SELECT * FROM main.t3 + ) + SELECT * FROM x2; +} {3} + +do_execsql_test 1.9 { + WITH x1 AS (SELECT * FROM t1) + SELECT (SELECT sum(a) FROM x1), (SELECT max(a) FROM x1); +} {3 2} + +do_execsql_test 1.10 { + WITH x1 AS (SELECT * FROM t1) + SELECT (SELECT sum(a) FROM x1), (SELECT max(a) FROM x1), a FROM x1; +} {3 2 1 3 2 2} + +do_execsql_test 1.11 { + WITH + i(x) AS ( + WITH + j(x) AS ( SELECT * FROM i ), + i(x) AS ( SELECT * FROM t1 ) + SELECT * FROM j + ) + SELECT * FROM i; +} {1 2} + +do_execsql_test 1.12 { + WITH r(i) AS ( + VALUES('.') + UNION ALL + SELECT i || '.' FROM r, ( + SELECT x FROM x INTERSECT SELECT y FROM y + ) WHERE length(i) < 10 + ), + x(x) AS ( VALUES(1) UNION ALL VALUES(2) UNION ALL VALUES(3) ), + y(y) AS ( VALUES(2) UNION ALL VALUES(4) UNION ALL VALUES(6) ) + + SELECT * FROM r; +} {. .. ... .... ..... ...... ....... ........ ......... ..........} + +do_execsql_test 1.13 { + WITH r(i) AS ( + VALUES('.') + UNION ALL + SELECT i || '.' FROM r, ( SELECT x FROM x WHERE x=2 ) WHERE length(i) < 10 + ), + x(x) AS ( VALUES(1) UNION ALL VALUES(2) UNION ALL VALUES(3) ) + + SELECT * FROM r ORDER BY length(i) DESC; +} {.......... ......... ........ ....... ...... ..... .... ... .. .} + +do_execsql_test 1.14 { + WITH + t4(x) AS ( + VALUES(4) + UNION ALL + SELECT x+1 FROM t4 WHERE x<10 + ) + SELECT * FROM t4; +} {4 5 6 7 8 9 10} + +do_execsql_test 1.15 { + WITH + t4(x) AS ( + VALUES(4) + UNION ALL + SELECT x+1 FROM main.t4 WHERE x<10 + ) + SELECT * FROM t4; +} {4 5} + +do_catchsql_test 1.16 { + WITH + t4(x) AS ( + VALUES(4) + UNION ALL + SELECT x+1 FROM t4, main.t4, t4 WHERE x<10 + ) + SELECT * FROM t4; +} {1 {multiple references to recursive table: t4}} + + +#--------------------------------------------------------------------------- +# Check that variables can be used in CTEs. +# +set ::min [expr 3] +set ::max [expr 9] +do_execsql_test 2.1 { + WITH i(x) AS ( + VALUES($min) UNION ALL SELECT x+1 FROM i WHERE x < $max + ) + SELECT * FROM i; +} {3 4 5 6 7 8 9} + +do_execsql_test 2.2 { + WITH i(x) AS ( + VALUES($min) UNION ALL SELECT x+1 FROM i WHERE x < $max + ) + SELECT x FROM i JOIN i AS j USING (x); +} {3 4 5 6 7 8 9} + +#--------------------------------------------------------------------------- +# Check that circular references are rejected. +# +do_catchsql_test 3.1 { + WITH i(x, y) AS ( VALUES(1, (SELECT x FROM i)) ) + SELECT * FROM i; +} {1 {circular reference: i}} + +do_catchsql_test 3.2 { + WITH + i(x) AS ( SELECT * FROM j ), + j(x) AS ( SELECT * FROM k ), + k(x) AS ( SELECT * FROM i ) + SELECT * FROM i; +} {1 {circular reference: i}} + +do_catchsql_test 3.3 { + WITH + i(x) AS ( SELECT * FROM (SELECT * FROM j) ), + j(x) AS ( SELECT * FROM (SELECT * FROM i) ) + SELECT * FROM i; +} {1 {circular reference: i}} + +do_catchsql_test 3.4 { + WITH + i(x) AS ( SELECT * FROM (SELECT * FROM j) ), + j(x) AS ( SELECT * FROM (SELECT * FROM i) ) + SELECT * FROM j; +} {1 {circular reference: j}} + +do_catchsql_test 3.5 { + WITH + i(x) AS ( + WITH j(x) AS ( SELECT * FROM i ) + SELECT * FROM j + ) + SELECT * FROM i; +} {1 {circular reference: i}} + +#--------------------------------------------------------------------------- +# Try empty and very long column lists. +# +do_catchsql_test 4.1 { + WITH x() AS ( SELECT 1,2,3 ) + SELECT * FROM x; +} {1 {near ")": syntax error}} + +proc genstmt {n} { + for {set i 1} {$i<=$n} {incr i} { + lappend cols "c$i" + lappend vals $i + } + return " + WITH x([join $cols ,]) AS (SELECT [join $vals ,]) + SELECT (c$n == $n) FROM x + " +} + +do_execsql_test 4.2 [genstmt 10] 1 +do_execsql_test 4.3 [genstmt 100] 1 +do_execsql_test 4.4 [genstmt 255] 1 +set nLimit [sqlite3_limit db SQLITE_LIMIT_COLUMN -1] +do_execsql_test 4.5 [genstmt [expr $nLimit-1]] 1 +do_execsql_test 4.6 [genstmt $nLimit] 1 +do_catchsql_test 4.7 [genstmt [expr $nLimit+1]] {1 {too many columns in index}} + +#--------------------------------------------------------------------------- +# Check that adding a WITH clause to an INSERT disables the xfer +# optimization. +# +proc do_xfer_test {tn bXfer sql {res {}}} { + set ::sqlite3_xferopt_count 0 + uplevel [list do_test $tn [subst -nocommands { + set dres [db eval {$sql}] + list [set ::sqlite3_xferopt_count] [set dres] + }] [list $bXfer $res]] +} + +do_execsql_test 5.1 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); +} + +do_xfer_test 5.2 1 { INSERT INTO t1 SELECT * FROM t2 } +do_xfer_test 5.3 0 { INSERT INTO t1 SELECT a, b FROM t2 } +do_xfer_test 5.4 0 { INSERT INTO t1 SELECT b, a FROM t2 } +do_xfer_test 5.5 0 { + WITH x AS (SELECT a, b FROM t2) INSERT INTO t1 SELECT * FROM x +} +do_xfer_test 5.6 0 { + WITH x AS (SELECT a, b FROM t2) INSERT INTO t1 SELECT * FROM t2 +} +do_xfer_test 5.7 0 { + INSERT INTO t1 WITH x AS ( SELECT * FROM t2 ) SELECT * FROM x +} +do_xfer_test 5.8 0 { + INSERT INTO t1 WITH x(a,b) AS ( SELECT * FROM t2 ) SELECT * FROM x +} + +#--------------------------------------------------------------------------- +# Check that syntax (and other) errors in statements with WITH clauses +# attached to them do not cause problems (e.g. memory leaks). +# +do_execsql_test 6.1 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); +} + +do_catchsql_test 6.2 { + WITH x AS (SELECT * FROM t1) + INSERT INTO t2 VALUES(1, 2,); +} {1 {near ")": syntax error}} + +do_catchsql_test 6.3 { + WITH x AS (SELECT * FROM t1) + INSERT INTO t2 SELECT a, b, FROM t1; +} {1 {near "FROM": syntax error}} + +do_catchsql_test 6.3 { + WITH x AS (SELECT * FROM t1) + INSERT INTO t2 SELECT a, b FROM abc; +} {1 {no such table: abc}} + +do_catchsql_test 6.4 { + WITH x AS (SELECT * FROM t1) + INSERT INTO t2 SELECT a, b, FROM t1 a a a; +} {1 {near "FROM": syntax error}} + +do_catchsql_test 6.5 { + WITH x AS (SELECT * FROM t1) + DELETE FROM t2 WHERE; +} {1 {near ";": syntax error}} + +do_catchsql_test 6.6 { + WITH x AS (SELECT * FROM t1) DELETE FROM t2 WHERE +} {/1 {near .* syntax error}/} + +do_catchsql_test 6.7 { + WITH x AS (SELECT * FROM t1) DELETE FROM t2 WHRE 1; +} {/1 {near .* syntax error}/} + +do_catchsql_test 6.8 { + WITH x AS (SELECT * FROM t1) UPDATE t2 SET a = 10, b = ; +} {/1 {near .* syntax error}/} + +do_catchsql_test 6.9 { + WITH x AS (SELECT * FROM t1) UPDATE t2 SET a = 10, b = 1 WHERE a===b; +} {/1 {near .* syntax error}/} + +do_catchsql_test 6.10 { + WITH x(a,b) AS ( + SELECT 1, 1 + UNION ALL + SELECT a*b,a+b FROM x WHERE c=2 + ) + SELECT * FROM x +} {1 {no such column: c}} + +#------------------------------------------------------------------------- +# Recursive queries in IN(...) expressions. +# +do_execsql_test 7.1 { + CREATE TABLE t5(x INTEGER); + CREATE TABLE t6(y INTEGER); + + WITH s(x) AS ( VALUES(7) UNION ALL SELECT x+7 FROM s WHERE x<49 ) + INSERT INTO t5 + SELECT * FROM s; + + INSERT INTO t6 + WITH s(x) AS ( VALUES(2) UNION ALL SELECT x+2 FROM s WHERE x<49 ) + SELECT * FROM s; +} + +do_execsql_test 7.2 { + SELECT * FROM t6 WHERE y IN (SELECT x FROM t5) +} {14 28 42} + +do_execsql_test 7.3 { + WITH ss AS (SELECT x FROM t5) + SELECT * FROM t6 WHERE y IN (SELECT x FROM ss) +} {14 28 42} + +do_execsql_test 7.4 { + WITH ss(x) AS ( VALUES(7) UNION ALL SELECT x+7 FROM ss WHERE x<49 ) + SELECT * FROM t6 WHERE y IN (SELECT x FROM ss) +} {14 28 42} + +do_execsql_test 7.5 { + SELECT * FROM t6 WHERE y IN ( + WITH ss(x) AS ( VALUES(7) UNION ALL SELECT x+7 FROM ss WHERE x<49 ) + SELECT x FROM ss + ) +} {14 28 42} + + + +finish_test + diff --git a/test/withM.test b/test/withM.test new file mode 100644 index 0000000000..c1650d9576 --- /dev/null +++ b/test/withM.test @@ -0,0 +1,77 @@ +# 2014 January 11 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the WITH clause. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set ::testprefix withM + +ifcapable {!cte} { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE TABLE t1(x INTEGER, y INTEGER); + INSERT INTO t1 VALUES(123, 456); +} + +do_faultsim_test withM-1.1 -prep { + sqlite3 db test.db +} -body { + execsql { + WITH tmp AS ( SELECT * FROM t1 ) + SELECT * FROM tmp; + } +} -test { + faultsim_test_result {0 {123 456}} + db close +} + +do_faultsim_test withM-1.2 -prep { + sqlite3 db test.db +} -body { + execsql { + WITH w1 AS ( SELECT * FROM t1 ), + w2 AS ( + WITH w3 AS ( SELECT * FROM w1 ) + SELECT * FROM w3 + ) + SELECT * FROM w2; + } +} -test { + faultsim_test_result {0 {123 456}} + db close +} + +do_faultsim_test withM-1.3 -prep { + sqlite3 db test.db +} -body { + execsql { + WITH w1(a,b) AS ( + SELECT 1, 1 + UNION ALL + SELECT a+1, b + 2*a + 1 FROM w1 + ) + SELECT * FROM w1 LIMIT 5; + } +} -test { + faultsim_test_result {0 {1 1 2 4 3 9 4 16 5 25}} + db close +} + +finish_test + + + diff --git a/tool/lemon.c b/tool/lemon.c index 58f13880f0..d7179ad423 100644 --- a/tool/lemon.c +++ b/tool/lemon.c @@ -50,6 +50,107 @@ static char *msort(char*,char**,int(*)(const char*,const char*)); */ #define lemonStrlen(X) ((int)strlen(X)) +/* +** Compilers are starting to complain about the use of sprintf() and strcpy(), +** saying they are unsafe. So we define our own versions of those routines too. +** +** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and +** lemon_addtext(). The first two are replacements for sprintf() and vsprintf(). +** The third is a helper routine for vsnprintf() that adds texts to the end of a +** buffer, making sure the buffer is always zero-terminated. +** +** The string formatter is a minimal subset of stdlib sprintf() supporting only +** a few simply conversions: +** +** %d +** %s +** %.*s +** +*/ +static void lemon_addtext( + char *zBuf, /* The buffer to which text is added */ + int *pnUsed, /* Slots of the buffer used so far */ + const char *zIn, /* Text to add */ + int nIn, /* Bytes of text to add. -1 to use strlen() */ + int iWidth /* Field width. Negative to left justify */ +){ + if( nIn<0 ) for(nIn=0; zIn[nIn]; nIn++){} + while( iWidth>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth--; } + if( nIn==0 ) return; + memcpy(&zBuf[*pnUsed], zIn, nIn); + *pnUsed += nIn; + while( (-iWidth)>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth++; } + zBuf[*pnUsed] = 0; +} +static int lemon_vsprintf(char *str, const char *zFormat, va_list ap){ + int i, j, k, c; + int nUsed = 0; + const char *z; + char zTemp[50]; + str[0] = 0; + for(i=j=0; (c = zFormat[i])!=0; i++){ + if( c=='%' ){ + int iWidth = 0; + lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0); + c = zFormat[++i]; + if( isdigit(c) || (c=='-' && isdigit(zFormat[i+1])) ){ + if( c=='-' ) i++; + while( isdigit(zFormat[i]) ) iWidth = iWidth*10 + zFormat[i++] - '0'; + if( c=='-' ) iWidth = -iWidth; + c = zFormat[i]; + } + if( c=='d' ){ + int v = va_arg(ap, int); + if( v<0 ){ + lemon_addtext(str, &nUsed, "-", 1, iWidth); + v = -v; + }else if( v==0 ){ + lemon_addtext(str, &nUsed, "0", 1, iWidth); + } + k = 0; + while( v>0 ){ + k++; + zTemp[sizeof(zTemp)-k] = (v%10) + '0'; + v /= 10; + } + lemon_addtext(str, &nUsed, &zTemp[sizeof(zTemp)-k], k, iWidth); + }else if( c=='s' ){ + z = va_arg(ap, const char*); + lemon_addtext(str, &nUsed, z, -1, iWidth); + }else if( c=='.' && memcmp(&zFormat[i], ".*s", 3)==0 ){ + i += 2; + k = va_arg(ap, int); + z = va_arg(ap, const char*); + lemon_addtext(str, &nUsed, z, k, iWidth); + }else if( c=='%' ){ + lemon_addtext(str, &nUsed, "%", 1, 0); + }else{ + fprintf(stderr, "illegal format\n"); + exit(1); + } + j = i+1; + } + } + lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0); + return nUsed; +} +static int lemon_sprintf(char *str, const char *format, ...){ + va_list ap; + int rc; + va_start(ap, format); + rc = lemon_vsprintf(str, format, ap); + va_end(ap); + return rc; +} +static void lemon_strcpy(char *dest, const char *src){ + while( (*(dest++) = *(src++))!=0 ){} +} +static void lemon_strcat(char *dest, const char *src){ + while( *dest ) dest++; + lemon_strcpy(dest, src); +} + + /* a few forward declarations... */ struct rule; struct lemon; @@ -1367,7 +1468,7 @@ static void handle_D_option(char *z){ fprintf(stderr,"out of memory\n"); exit(1); } - strcpy(*paz, z); + lemon_strcpy(*paz, z); for(z=*paz; *z && *z!='='; z++){} *z = 0; } @@ -1378,7 +1479,7 @@ static void handle_T_option(char *z){ if( user_templatename==0 ){ memory_error(); } - strcpy(user_templatename, z); + lemon_strcpy(user_templatename, z); } /* The main program. Parse the command line and do it... */ @@ -1447,12 +1548,15 @@ int main(int argc, char **argv) } /* Count and index the symbols of the grammar */ - lem.nsymbol = Symbol_count(); Symbol_new("{default}"); + lem.nsymbol = Symbol_count(); lem.symbols = Symbol_arrayof(); - for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; - qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), Symbolcmpp); - for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=0; iindex = i; + qsort(lem.symbols,lem.nsymbol,sizeof(struct symbol*), Symbolcmpp); + for(i=0; iindex = i; + while( lem.symbols[i-1]->type==MULTITERMINAL ){ i--; } + assert( strcmp(lem.symbols[i-1]->name,"{default}")==0 ); + lem.nsymbol = i - 1; for(i=1; isupper(lem.symbols[i]->name[0]); i++); lem.nterminal = i; @@ -1940,7 +2044,9 @@ enum e_state { WAITING_FOR_DESTRUCTOR_SYMBOL, WAITING_FOR_DATATYPE_SYMBOL, WAITING_FOR_FALLBACK_ID, - WAITING_FOR_WILDCARD_ID + WAITING_FOR_WILDCARD_ID, + WAITING_FOR_CLASS_ID, + WAITING_FOR_CLASS_TOKEN }; struct pstate { char *filename; /* Name of the input file */ @@ -1950,6 +2056,7 @@ struct pstate { struct lemon *gp; /* Global state vector */ enum e_state state; /* The state of the parser */ struct symbol *fallback; /* The fallback token */ + struct symbol *tkclass; /* Token class symbol */ struct symbol *lhs; /* Left-hand side of current rule */ const char *lhsalias; /* Alias for the LHS */ int nrhs; /* Number of right-hand side symbols seen */ @@ -2254,6 +2361,8 @@ to follow the previous rule."); psp->state = WAITING_FOR_FALLBACK_ID; }else if( strcmp(x,"wildcard")==0 ){ psp->state = WAITING_FOR_WILDCARD_ID; + }else if( strcmp(x,"token_class")==0 ){ + psp->state = WAITING_FOR_CLASS_ID; }else{ ErrorMsg(psp->filename,psp->tokenlineno, "Unknown declaration keyword: \"%%%s\".",x); @@ -2347,7 +2456,7 @@ to follow the previous rule."); for(z=psp->filename, nBack=0; *z; z++){ if( *z=='\\' ) nBack++; } - sprintf(zLine, "#line %d ", psp->tokenlineno); + lemon_sprintf(zLine, "#line %d ", psp->tokenlineno); nLine = lemonStrlen(zLine); n += nLine + lemonStrlen(psp->filename) + nBack; } @@ -2422,6 +2531,40 @@ to follow the previous rule."); } } break; + case WAITING_FOR_CLASS_ID: + if( !islower(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%token_class must be followed by an identifier: ", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else if( Symbol_find(x) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "Symbol \"%s\" already used", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + psp->tkclass = Symbol_new(x); + psp->tkclass->type = MULTITERMINAL; + psp->state = WAITING_FOR_CLASS_TOKEN; + } + break; + case WAITING_FOR_CLASS_TOKEN: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) || ((x[0]=='|' || x[0]=='/') && isupper(x[1])) ){ + struct symbol *msp = psp->tkclass; + msp->nsubsym++; + msp->subsym = (struct symbol **) realloc(msp->subsym, + sizeof(struct symbol*)*msp->nsubsym); + if( !isupper(x[0]) ) x++; + msp->subsym[msp->nsubsym-1] = Symbol_new(x); + }else{ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%token_class argument \"%s\" should be a token", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; case RESYNC_AFTER_RULE_ERROR: /* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; ** break; */ @@ -2516,9 +2659,8 @@ void Parse(struct lemon *gp) filesize = ftell(fp); rewind(fp); filebuf = (char *)malloc( filesize+1 ); - if( filebuf==0 ){ - ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", - filesize+1); + if( filesize>100000000 || filebuf==0 ){ + ErrorMsg(ps.filename,0,"Input file too large."); gp->errorcnt++; fclose(fp); return; @@ -2716,10 +2858,10 @@ PRIVATE char *file_makename(struct lemon *lemp, const char *suffix) fprintf(stderr,"Can't allocate space for a filename.\n"); exit(1); } - strcpy(name,lemp->filename); + lemon_strcpy(name,lemp->filename); cp = strrchr(name,'.'); if( cp ) *cp = 0; - strcat(name,suffix); + lemon_strcat(name,suffix); return name; } @@ -2776,11 +2918,13 @@ void Reprint(struct lemon *lemp) printf(" ::="); for(i=0; inrhs; i++){ sp = rp->rhs[i]; - printf(" %s", sp->name); if( sp->type==MULTITERMINAL ){ + printf(" %s", sp->subsym[0]->name); for(j=1; jnsubsym; j++){ printf("|%s", sp->subsym[j]->name); } + }else{ + printf(" %s", sp->name); } /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ } @@ -2802,11 +2946,13 @@ void ConfigPrint(FILE *fp, struct config *cfp) if( i==cfp->dot ) fprintf(fp," *"); if( i==rp->nrhs ) break; sp = rp->rhs[i]; - fprintf(fp," %s", sp->name); if( sp->type==MULTITERMINAL ){ + fprintf(fp," %s", sp->subsym[0]->name); for(j=1; jnsubsym; j++){ fprintf(fp,"|%s",sp->subsym[j]->name); } + }else{ + fprintf(fp," %s", sp->name); } } } @@ -2916,7 +3062,7 @@ void ReportOutput(struct lemon *lemp) while( cfp ){ char buf[20]; if( cfp->dot==cfp->rp->nrhs ){ - sprintf(buf,"(%d)",cfp->rp->index); + lemon_sprintf(buf,"(%d)",cfp->rp->index); fprintf(fp," %5s ",buf); }else{ fprintf(fp," "); @@ -2981,7 +3127,7 @@ PRIVATE char *pathsearch(char *argv0, char *name, int modemask) c = *cp; *cp = 0; path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 ); - if( path ) sprintf(path,"%s/%s",argv0,name); + if( path ) lemon_sprintf(path,"%s/%s",argv0,name); *cp = c; }else{ pathlist = getenv("PATH"); @@ -2990,13 +3136,13 @@ PRIVATE char *pathsearch(char *argv0, char *name, int modemask) path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 ); if( (pathbuf != 0) && (path!=0) ){ pathbufptr = pathbuf; - strcpy(pathbuf, pathlist); + lemon_strcpy(pathbuf, pathlist); while( *pathbuf ){ cp = strchr(pathbuf,':'); if( cp==0 ) cp = &pathbuf[lemonStrlen(pathbuf)]; c = *cp; *cp = 0; - sprintf(path,"%s/%s",pathbuf,name); + lemon_sprintf(path,"%s/%s",pathbuf,name); *cp = c; if( c==0 ) pathbuf[0] = 0; else pathbuf = &cp[1]; @@ -3087,9 +3233,9 @@ PRIVATE FILE *tplt_open(struct lemon *lemp) cp = strrchr(lemp->filename,'.'); if( cp ){ - sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + lemon_sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); }else{ - sprintf(buf,"%s.lt",lemp->filename); + lemon_sprintf(buf,"%s.lt",lemp->filename); } if( access(buf,004)==0 ){ tpltname = buf; @@ -3240,9 +3386,9 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ while( n-- > 0 ){ c = *(zText++); if( c=='%' && n>0 && zText[0]=='d' ){ - sprintf(zInt, "%d", p1); + lemon_sprintf(zInt, "%d", p1); p1 = p2; - strcpy(&z[used], zInt); + lemon_strcpy(&z[used], zInt); used += lemonStrlen(&z[used]); zText++; n--; @@ -3467,7 +3613,7 @@ void print_stack_union( fprintf(stderr,"Out of memory.\n"); exit(1); } - strcpy(types[hash],stddt); + lemon_strcpy(types[hash],stddt); } } @@ -3553,9 +3699,11 @@ static void writeRuleText(FILE *out, struct rule *rp){ fprintf(out,"%s ::=", rp->lhs->name); for(j=0; jnrhs; j++){ struct symbol *sp = rp->rhs[j]; - fprintf(out," %s", sp->name); - if( sp->type==MULTITERMINAL ){ + if( sp->type!=MULTITERMINAL ){ + fprintf(out," %s", sp->name); + }else{ int k; + fprintf(out," %s", sp->subsym[0]->name); for(k=1; knsubsym; k++){ fprintf(out,"|%s",sp->subsym[k]->name); } @@ -3856,7 +4004,7 @@ void ReportTable( /* Generate a table containing the symbolic name of every symbol */ for(i=0; insymbol; i++){ - sprintf(line,"\"%s\",",lemp->symbols[i]->name); + lemon_sprintf(line,"\"%s\",",lemp->symbols[i]->name); fprintf(out," %-15s",line); if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } } @@ -4023,7 +4171,8 @@ void ReportHeader(struct lemon *lemp) if( in ){ int nextChar; for(i=1; interminal && fgets(line,LINESIZE,in); i++){ - sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lemon_sprintf(pattern,"#define %s%-30s %3d\n", + prefix,lemp->symbols[i]->name,i); if( strcmp(line,pattern) ) break; } nextChar = fgetc(in); @@ -4036,7 +4185,7 @@ void ReportHeader(struct lemon *lemp) out = file_open(lemp,".h","wb"); if( out ){ for(i=1; interminal; i++){ - fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + fprintf(out,"#define %s%-30s %3d\n",prefix,lemp->symbols[i]->name,i); } fclose(out); } @@ -4253,7 +4402,7 @@ const char *Strsafe(const char *y) if( y==0 ) return 0; z = Strsafe_find(y); if( z==0 && (cpy=(char *)malloc( lemonStrlen(y)+1 ))!=0 ){ - strcpy(cpy,y); + lemon_strcpy(cpy,y); z = cpy; Strsafe_insert(z); } @@ -4292,8 +4441,7 @@ void Strsafe_init(){ if( x1a ){ x1a->size = 1024; x1a->count = 0; - x1a->tbl = (x1node*)malloc( - (sizeof(x1node) + sizeof(x1node*))*1024 ); + x1a->tbl = (x1node*)calloc(1024, sizeof(x1node) + sizeof(x1node*)); if( x1a->tbl==0 ){ free(x1a); x1a = 0; @@ -4330,8 +4478,7 @@ int Strsafe_insert(const char *data) struct s_x1 array; array.size = size = x1a->size*2; array.count = x1a->count; - array.tbl = (x1node*)malloc( - (sizeof(x1node) + sizeof(x1node*))*size ); + array.tbl = (x1node*)calloc(size, sizeof(x1node) + sizeof(x1node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x1node**)&(array.tbl[size]); for(i=0; i'Z'); - int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); - assert( i1!=i2 || strcmp((**a).name,(**b).name)==0 ); - return i1-i2; + const struct symbol *a = *(const struct symbol **) _a; + const struct symbol *b = *(const struct symbol **) _b; + int i1 = a->type==MULTITERMINAL ? 3 : a->name[0]>'Z' ? 2 : 1; + int i2 = b->type==MULTITERMINAL ? 3 : b->name[0]>'Z' ? 2 : 1; + return i1==i2 ? a->index - b->index : i1 - i2; } /* There is one instance of the following structure for each @@ -4458,8 +4608,7 @@ void Symbol_init(){ if( x2a ){ x2a->size = 128; x2a->count = 0; - x2a->tbl = (x2node*)malloc( - (sizeof(x2node) + sizeof(x2node*))*128 ); + x2a->tbl = (x2node*)calloc(128, sizeof(x2node) + sizeof(x2node*)); if( x2a->tbl==0 ){ free(x2a); x2a = 0; @@ -4496,8 +4645,7 @@ int Symbol_insert(struct symbol *data, const char *key) struct s_x2 array; array.size = size = x2a->size*2; array.count = x2a->count; - array.tbl = (x2node*)malloc( - (sizeof(x2node) + sizeof(x2node*))*size ); + array.tbl = (x2node*)calloc(size, sizeof(x2node) + sizeof(x2node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x2node**)&(array.tbl[size]); for(i=0; isize = 128; x3a->count = 0; - x3a->tbl = (x3node*)malloc( - (sizeof(x3node) + sizeof(x3node*))*128 ); + x3a->tbl = (x3node*)calloc(128, sizeof(x3node) + sizeof(x3node*)); if( x3a->tbl==0 ){ free(x3a); x3a = 0; @@ -4695,8 +4842,7 @@ int State_insert(struct state *data, struct config *key) struct s_x3 array; array.size = size = x3a->size*2; array.count = x3a->count; - array.tbl = (x3node*)malloc( - (sizeof(x3node) + sizeof(x3node*))*size ); + array.tbl = (x3node*)calloc(size, sizeof(x3node) + sizeof(x3node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x3node**)&(array.tbl[size]); for(i=0; icount; - array = (struct state **)malloc( sizeof(struct state *)*size ); + array = (struct state **)calloc(size, sizeof(struct state *)); if( array ){ for(i=0; itbl[i].data; } @@ -4799,8 +4945,7 @@ void Configtable_init(){ if( x4a ){ x4a->size = 64; x4a->count = 0; - x4a->tbl = (x4node*)malloc( - (sizeof(x4node) + sizeof(x4node*))*64 ); + x4a->tbl = (x4node*)calloc(64, sizeof(x4node) + sizeof(x4node*)); if( x4a->tbl==0 ){ free(x4a); x4a = 0; @@ -4837,8 +4982,7 @@ int Configtable_insert(struct config *data) struct s_x4 array; array.size = size = x4a->size*2; array.count = x4a->count; - array.tbl = (x4node*)malloc( - (sizeof(x4node) + sizeof(x4node*))*size ); + array.tbl = (x4node*)calloc(size, sizeof(x4node) + sizeof(x4node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x4node**)&(array.tbl[size]); for(i=0; i