mirror of https://github.com/sqlite/sqlite
Bring in all the latest trunk changes, including the Common Table
Expressions implementation. FossilOrigin-Name: 9b43e559195680e558264c4c00d34dc9cf9d9146
This commit is contained in:
commit
c187698614
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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; i<pInfo->nConstraint; 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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
99
manifest
99
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
|
||||
|
|
|
@ -1 +1 @@
|
|||
cfd110bf5db2c1993a5e2ca718648bd9c17ee22c
|
||||
9b43e559195680e558264c4c00d34dc9cf9d9146
|
41
src/btree.c
41
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; i<pPage->nCell; 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:
|
||||
|
|
81
src/build.c
81
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; i<pWith->nCte; 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; i<pWith->nCte; 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) */
|
||||
|
|
40
src/delete.c
40
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; j<nCol; j++){
|
||||
if( pPrior && pPrior->aiColumn[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;
|
||||
|
|
84
src/expr.c
84
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; i<p->nCte; 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;
|
||||
}
|
||||
}
|
||||
|
|
22
src/insert.c
22
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 */
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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__);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
192
src/parse.y
192
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
|
||||
|
|
|
@ -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);
|
||||
|
|
14
src/random.c
14
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 */
|
||||
|
|
617
src/select.c
617
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<nColumn; i++){
|
||||
if( srcTab>=0 ){
|
||||
for(i=0; i<nResultCol; i++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
|
||||
VdbeComment((v, "%s", pEList->a[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; i<nColumn; i++){
|
||||
iJump = sqlite3VdbeCurrentAddr(v) + nResultCol;
|
||||
for(i=0; i<nResultCol; i++){
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[i].pExpr);
|
||||
if( i<nColumn-1 ){
|
||||
if( i<nResultCol-1 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Ne, regResult+i, iJump, regPrev+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, regResult+i, iContinue, regPrev+i);
|
||||
|
@ -644,7 +637,7 @@ static void selectInnerLoop(
|
|||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
}
|
||||
assert( sqlite3VdbeCurrentAddr(v)==iJump );
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nColumn-1);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -655,12 +648,12 @@ static void selectInnerLoop(
|
|||
|
||||
default: {
|
||||
assert( pDistinct->eTnctType==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; i<nKey; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy,
|
||||
regResult + pSO->a[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; i<nExpr; i++, pItem++){
|
||||
|
@ -1017,13 +1070,13 @@ static void generateSortTail(
|
|||
int ptab2 = pParse->nTab++;
|
||||
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:
|
||||
**
|
||||
** <recursive-table> AS (<setup-query> UNION [ALL] <recursive-query>)
|
||||
** \___________/ \_______________/
|
||||
** 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(i<pSrc->nSrc); 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; i<p->nCte; 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; i<pSrc->nSrc; 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; i<pTabList->nSrc; 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);
|
||||
}
|
||||
|
|
|
@ -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; i<iOp; i++) p->aiIndent[i] += 2;
|
||||
}
|
||||
if( str_in_array(zOp, azGoto) && p2op<p->nIndent && abYield[p2op] ){
|
||||
for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2;
|
||||
for(i=p2op+1; i<iOp; i++) p->aiIndent[i] += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
127
src/sqliteInt.h
127
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
|
||||
|
|
|
@ -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);
|
||||
|
|
23
src/test1.c
23
src/test1.c
|
@ -242,6 +242,27 @@ 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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
23
src/walker.c
23
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;
|
||||
}
|
||||
|
|
34
src/where.c
34
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; ii<sWLB.pWC->nTerm; 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
|
||||
|
|
|
@ -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"} {
|
||||
|
|
|
@ -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'
|
||||
|
@ -1061,10 +1065,12 @@ 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.
|
||||
#
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 k2<?2}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -1376,4 +1376,23 @@ do_execsql_test 20.4 {
|
|||
ORDER BY 1, 2;
|
||||
} {5 5 6 6 11 11 12 12}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 21.1 {
|
||||
CREATE TABLE t9(a,b,c);
|
||||
CREATE VIRTUAL TABLE t9v USING echo(t9);
|
||||
|
||||
INSERT INTO t9 VALUES(1,2,3);
|
||||
INSERT INTO t9 VALUES(3,2,1);
|
||||
INSERT INTO t9 VALUES(2,2,2);
|
||||
}
|
||||
|
||||
do_execsql_test 21.2 {
|
||||
SELECT * FROM t9v WHERE a<b;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 21.3 {
|
||||
SELECT * FROM t9v WHERE a=b;
|
||||
} {2 2 2}
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -0,0 +1,574 @@
|
|||
# 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 with1
|
||||
|
||||
ifcapable {!cte} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x INTEGER, y INTEGER);
|
||||
WITH x(a) AS ( SELECT * FROM t1) SELECT 10
|
||||
} {10}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM ( WITH x AS ( SELECT * FROM t1) SELECT 10 );
|
||||
} {10}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
WITH x(a) AS ( SELECT * FROM t1) INSERT INTO t1 VALUES(1,2);
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
WITH x(a) AS ( SELECT * FROM t1) DELETE FROM t1;
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
WITH x(a) AS ( SELECT * FROM t1) UPDATE t1 SET x = y;
|
||||
} {}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
WITH tmp AS ( SELECT * FROM t1 ) SELECT x FROM tmp;
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
WITH tmp(a) AS ( SELECT * FROM t1 ) SELECT a FROM tmp;
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT * FROM (
|
||||
WITH tmp(a) AS ( SELECT * FROM t1 ) SELECT a FROM tmp
|
||||
);
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
WITH tmp1(a) AS ( SELECT * FROM t1 ),
|
||||
tmp2(x) AS ( SELECT * FROM tmp1)
|
||||
SELECT * FROM tmp2;
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
WITH tmp2(x) AS ( SELECT * FROM tmp1),
|
||||
tmp1(a) AS ( SELECT * FROM t1 )
|
||||
SELECT * FROM tmp2;
|
||||
} {1 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_catchsql_test 3.1 {
|
||||
WITH tmp2(x) AS ( SELECT * FROM tmp1 ),
|
||||
tmp1(a) AS ( SELECT * FROM tmp2 )
|
||||
SELECT * FROM tmp1;
|
||||
} {1 {circular reference: tmp1}}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
CREATE TABLE t2(x INTEGER);
|
||||
WITH tmp(a) AS (SELECT * FROM t1),
|
||||
tmp(a) AS (SELECT * FROM t1)
|
||||
SELECT * FROM tmp;
|
||||
} {1 {duplicate WITH table name: tmp}}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
CREATE TABLE t3(x);
|
||||
CREATE TABLE t4(x);
|
||||
|
||||
INSERT INTO t3 VALUES('T3');
|
||||
INSERT INTO t4 VALUES('T4');
|
||||
|
||||
WITH t3(a) AS (SELECT * FROM t4)
|
||||
SELECT * FROM t3;
|
||||
} {T4}
|
||||
|
||||
do_execsql_test 3.4 {
|
||||
WITH tmp AS ( SELECT * FROM t3 ),
|
||||
tmp2 AS ( WITH tmp AS ( SELECT * FROM t4 ) SELECT * FROM tmp )
|
||||
SELECT * FROM tmp2;
|
||||
} {T4}
|
||||
|
||||
do_execsql_test 3.5 {
|
||||
WITH tmp AS ( SELECT * FROM t3 ),
|
||||
tmp2 AS ( WITH xxxx AS ( SELECT * FROM t4 ) SELECT * FROM tmp )
|
||||
SELECT * FROM tmp2;
|
||||
} {T3}
|
||||
|
||||
do_catchsql_test 3.6 {
|
||||
WITH tmp AS ( SELECT * FROM t3 ),
|
||||
SELECT * FROM tmp;
|
||||
} {1 {near "SELECT": syntax error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 4.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
|
||||
WITH dset AS ( SELECT 2 UNION ALL SELECT 4 )
|
||||
DELETE FROM t1 WHERE x IN dset;
|
||||
SELECT * FROM t1;
|
||||
} {1 3}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
WITH iset AS ( SELECT 2 UNION ALL SELECT 4 )
|
||||
INSERT INTO t1 SELECT * FROM iset;
|
||||
SELECT * FROM t1;
|
||||
} {1 3 2 4}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
WITH uset(a, b) AS ( SELECT 2, 8 UNION ALL SELECT 4, 9 )
|
||||
UPDATE t1 SET x = COALESCE( (SELECT b FROM uset WHERE a=x), x );
|
||||
SELECT * FROM t1;
|
||||
} {1 3 8 9}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 5.1 {
|
||||
WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i)
|
||||
SELECT x FROM i LIMIT 10;
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
|
||||
do_catchsql_test 5.2 {
|
||||
WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i ORDER BY 1)
|
||||
SELECT x FROM i LIMIT 10;
|
||||
} {0 {1 2 3 4 5 6 7 8 9 10}}
|
||||
|
||||
do_execsql_test 5.2.1 {
|
||||
CREATE TABLE edge(xfrom, xto, seq, PRIMARY KEY(xfrom, xto)) WITHOUT ROWID;
|
||||
INSERT INTO edge VALUES(0, 1, 10);
|
||||
INSERT INTO edge VALUES(1, 2, 20);
|
||||
INSERT INTO edge VALUES(0, 3, 30);
|
||||
INSERT INTO edge VALUES(2, 4, 40);
|
||||
INSERT INTO edge VALUES(3, 4, 40);
|
||||
INSERT INTO edge VALUES(2, 5, 50);
|
||||
INSERT INTO edge VALUES(3, 6, 60);
|
||||
INSERT INTO edge VALUES(5, 7, 70);
|
||||
INSERT INTO edge VALUES(3, 7, 70);
|
||||
INSERT INTO edge VALUES(4, 8, 80);
|
||||
INSERT INTO edge VALUES(7, 8, 80);
|
||||
INSERT INTO edge VALUES(8, 9, 90);
|
||||
|
||||
WITH RECURSIVE
|
||||
ancest(id, mtime) AS
|
||||
(VALUES(0, 0)
|
||||
UNION
|
||||
SELECT edge.xto, edge.seq FROM edge, ancest
|
||||
WHERE edge.xfrom=ancest.id
|
||||
ORDER BY 2
|
||||
)
|
||||
SELECT * FROM ancest;
|
||||
} {0 0 1 10 2 20 3 30 4 40 5 50 6 60 7 70 8 80 9 90}
|
||||
do_execsql_test 5.2.2 {
|
||||
WITH RECURSIVE
|
||||
ancest(id, mtime) AS
|
||||
(VALUES(0, 0)
|
||||
UNION ALL
|
||||
SELECT edge.xto, edge.seq FROM edge, ancest
|
||||
WHERE edge.xfrom=ancest.id
|
||||
ORDER BY 2
|
||||
)
|
||||
SELECT * FROM ancest;
|
||||
} {0 0 1 10 2 20 3 30 4 40 4 40 5 50 6 60 7 70 7 70 8 80 8 80 8 80 8 80 9 90 9 90 9 90 9 90}
|
||||
do_execsql_test 5.2.3 {
|
||||
WITH RECURSIVE
|
||||
ancest(id, mtime) AS
|
||||
(VALUES(0, 0)
|
||||
UNION ALL
|
||||
SELECT edge.xto, edge.seq FROM edge, ancest
|
||||
WHERE edge.xfrom=ancest.id
|
||||
ORDER BY 2 LIMIT 4 OFFSET 2
|
||||
)
|
||||
SELECT * FROM ancest;
|
||||
} {2 20 3 30 4 40 4 40}
|
||||
|
||||
do_catchsql_test 5.3 {
|
||||
WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i LIMIT 5)
|
||||
SELECT x FROM i;
|
||||
} {0 {1 2 3 4 5}}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
WITH i(x) AS ( VALUES(1) UNION ALL SELECT (x+1)%10 FROM i)
|
||||
SELECT x FROM i LIMIT 20;
|
||||
} {1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0}
|
||||
|
||||
do_execsql_test 5.5 {
|
||||
WITH i(x) AS ( VALUES(1) UNION SELECT (x+1)%10 FROM i)
|
||||
SELECT x FROM i LIMIT 20;
|
||||
} {1 2 3 4 5 6 7 8 9 0}
|
||||
|
||||
do_catchsql_test 5.6.1 {
|
||||
WITH i(x, y) AS ( VALUES(1) )
|
||||
SELECT * FROM i;
|
||||
} {1 {table i has 1 values for 2 columns}}
|
||||
|
||||
do_catchsql_test 5.6.2 {
|
||||
WITH i(x) AS ( VALUES(1,2) )
|
||||
SELECT * FROM i;
|
||||
} {1 {table i has 2 values for 1 columns}}
|
||||
|
||||
do_catchsql_test 5.6.3 {
|
||||
CREATE TABLE t5(a, b);
|
||||
WITH i(x) AS ( SELECT * FROM t5 )
|
||||
SELECT * FROM i;
|
||||
} {1 {table i has 2 values for 1 columns}}
|
||||
|
||||
do_catchsql_test 5.6.4 {
|
||||
WITH i(x) AS ( SELECT 1, 2 UNION ALL SELECT 1 )
|
||||
SELECT * FROM i;
|
||||
} {1 {table i has 2 values for 1 columns}}
|
||||
|
||||
do_catchsql_test 5.6.5 {
|
||||
WITH i(x) AS ( SELECT 1 UNION ALL SELECT 1, 2 )
|
||||
SELECT * FROM i;
|
||||
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
|
||||
|
||||
do_catchsql_test 5.6.6 {
|
||||
WITH i(x) AS ( SELECT 1 UNION ALL SELECT x+1, x*2 FROM i )
|
||||
SELECT * FROM i;
|
||||
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
|
||||
|
||||
do_catchsql_test 5.6.7 {
|
||||
WITH i(x) AS ( SELECT 1, 2 UNION SELECT x+1 FROM i )
|
||||
SELECT * FROM i;
|
||||
} {1 {table i has 2 values for 1 columns}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 6.1 {
|
||||
CREATE TABLE f(
|
||||
id INTEGER PRIMARY KEY, parentid REFERENCES f, name TEXT
|
||||
);
|
||||
|
||||
INSERT INTO f VALUES(0, NULL, '');
|
||||
INSERT INTO f VALUES(1, 0, 'bin');
|
||||
INSERT INTO f VALUES(2, 1, 'true');
|
||||
INSERT INTO f VALUES(3, 1, 'false');
|
||||
INSERT INTO f VALUES(4, 1, 'ls');
|
||||
INSERT INTO f VALUES(5, 1, 'grep');
|
||||
INSERT INTO f VALUES(6, 0, 'etc');
|
||||
INSERT INTO f VALUES(7, 6, 'rc.d');
|
||||
INSERT INTO f VALUES(8, 7, 'rc.apache');
|
||||
INSERT INTO f VALUES(9, 7, 'rc.samba');
|
||||
INSERT INTO f VALUES(10, 0, 'home');
|
||||
INSERT INTO f VALUES(11, 10, 'dan');
|
||||
INSERT INTO f VALUES(12, 11, 'public_html');
|
||||
INSERT INTO f VALUES(13, 12, 'index.html');
|
||||
INSERT INTO f VALUES(14, 13, 'logo.gif');
|
||||
}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
WITH flat(fid, fpath) AS (
|
||||
SELECT id, '' FROM f WHERE parentid IS NULL
|
||||
UNION ALL
|
||||
SELECT id, fpath || '/' || name FROM f, flat WHERE parentid=fid
|
||||
)
|
||||
SELECT fpath FROM flat WHERE fpath!='' ORDER BY 1;
|
||||
} {
|
||||
/bin
|
||||
/bin/false /bin/grep /bin/ls /bin/true
|
||||
/etc
|
||||
/etc/rc.d
|
||||
/etc/rc.d/rc.apache /etc/rc.d/rc.samba
|
||||
/home
|
||||
/home/dan
|
||||
/home/dan/public_html
|
||||
/home/dan/public_html/index.html
|
||||
/home/dan/public_html/index.html/logo.gif
|
||||
}
|
||||
|
||||
do_execsql_test 6.3 {
|
||||
WITH flat(fid, fpath) AS (
|
||||
SELECT id, '' FROM f WHERE parentid IS NULL
|
||||
UNION ALL
|
||||
SELECT id, fpath || '/' || name FROM f, flat WHERE parentid=fid
|
||||
)
|
||||
SELECT count(*) FROM flat;
|
||||
} {15}
|
||||
|
||||
do_execsql_test 6.4 {
|
||||
WITH x(i) AS (
|
||||
SELECT 1
|
||||
UNION ALL
|
||||
SELECT i+1 FROM x WHERE i<10
|
||||
)
|
||||
SELECT count(*) FROM x
|
||||
} {10}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE tree(i, p);
|
||||
INSERT INTO tree VALUES(1, NULL);
|
||||
INSERT INTO tree VALUES(2, 1);
|
||||
INSERT INTO tree VALUES(3, 1);
|
||||
INSERT INTO tree VALUES(4, 2);
|
||||
INSERT INTO tree VALUES(5, 4);
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
WITH t(id, path) AS (
|
||||
SELECT i, '' FROM tree WHERE p IS NULL
|
||||
UNION ALL
|
||||
SELECT i, path || '/' || i FROM tree, t WHERE p = id
|
||||
)
|
||||
SELECT path FROM t;
|
||||
} {{} /2 /3 /2/4 /2/4/5}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
WITH t(id) AS (
|
||||
VALUES(2)
|
||||
UNION ALL
|
||||
SELECT i FROM tree, t WHERE p = id
|
||||
)
|
||||
SELECT id FROM t;
|
||||
} {2 4 5}
|
||||
|
||||
do_catchsql_test 7.4 {
|
||||
WITH t(id) AS (
|
||||
VALUES(2)
|
||||
UNION ALL
|
||||
SELECT i FROM tree WHERE p IN (SELECT id FROM t)
|
||||
)
|
||||
SELECT id FROM t;
|
||||
} {1 {recursive reference in a subquery: t}}
|
||||
|
||||
do_catchsql_test 7.5 {
|
||||
WITH t(id) AS (
|
||||
VALUES(2)
|
||||
UNION ALL
|
||||
SELECT i FROM tree, t WHERE p = id AND p IN (SELECT id FROM t)
|
||||
)
|
||||
SELECT id FROM t;
|
||||
} {1 {multiple recursive references: t}}
|
||||
|
||||
do_catchsql_test 7.6 {
|
||||
WITH t(id) AS (
|
||||
SELECT i FROM tree WHERE 2 IN (SELECT id FROM t)
|
||||
UNION ALL
|
||||
SELECT i FROM tree, t WHERE p = id
|
||||
)
|
||||
SELECT id FROM t;
|
||||
} {1 {circular reference: t}}
|
||||
|
||||
# Compute the mandelbrot set using a recursive query
|
||||
#
|
||||
do_execsql_test 8.1-mandelbrot {
|
||||
WITH RECURSIVE
|
||||
xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+0.05 FROM xaxis WHERE x<1.2),
|
||||
yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+0.1 FROM yaxis WHERE y<1.0),
|
||||
m(iter, cx, cy, x, y) AS (
|
||||
SELECT 0, x, y, 0.0, 0.0 FROM xaxis, yaxis
|
||||
UNION ALL
|
||||
SELECT iter+1, cx, cy, x*x-y*y + cx, 2.0*x*y + cy FROM m
|
||||
WHERE (x*x + y*y) < 4.0 AND iter<28
|
||||
),
|
||||
m2(iter, cx, cy) AS (
|
||||
SELECT max(iter), cx, cy FROM m GROUP BY cx, cy
|
||||
),
|
||||
a(t) AS (
|
||||
SELECT group_concat( substr(' .+*#', 1+min(iter/7,4), 1), '')
|
||||
FROM m2 GROUP BY cy
|
||||
)
|
||||
SELECT group_concat(rtrim(t),x'0a') FROM a;
|
||||
} {{ ....#
|
||||
..#*..
|
||||
..+####+.
|
||||
.......+####.... +
|
||||
..##+*##########+.++++
|
||||
.+.##################+.
|
||||
.............+###################+.+
|
||||
..++..#.....*#####################+.
|
||||
...+#######++#######################.
|
||||
....+*################################.
|
||||
#############################################...
|
||||
....+*################################.
|
||||
...+#######++#######################.
|
||||
..++..#.....*#####################+.
|
||||
.............+###################+.+
|
||||
.+.##################+.
|
||||
..##+*##########+.++++
|
||||
.......+####.... +
|
||||
..+####+.
|
||||
..#*..
|
||||
....#
|
||||
+.}}
|
||||
|
||||
# Solve a sudoku puzzle using a recursive query
|
||||
#
|
||||
do_execsql_test 8.2-soduko {
|
||||
WITH RECURSIVE
|
||||
input(sud) AS (
|
||||
VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79')
|
||||
),
|
||||
|
||||
/* A table filled with digits 1..9, inclusive. */
|
||||
digits(z, lp) AS (
|
||||
VALUES('1', 1)
|
||||
UNION ALL SELECT
|
||||
CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9
|
||||
),
|
||||
|
||||
/* The tricky bit. */
|
||||
x(s, ind) AS (
|
||||
SELECT sud, instr(sud, '.') FROM input
|
||||
UNION ALL
|
||||
SELECT
|
||||
substr(s, 1, ind-1) || z || substr(s, ind+1),
|
||||
instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' )
|
||||
FROM x, digits AS z
|
||||
WHERE ind>0
|
||||
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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
254
tool/lemon.c
254
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; i<lem.nsymbol; i++) lem.symbols[i]->index = i;
|
||||
qsort(lem.symbols,lem.nsymbol,sizeof(struct symbol*), Symbolcmpp);
|
||||
for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = 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; i<rp->nrhs; i++){
|
||||
sp = rp->rhs[i];
|
||||
printf(" %s", sp->name);
|
||||
if( sp->type==MULTITERMINAL ){
|
||||
printf(" %s", sp->subsym[0]->name);
|
||||
for(j=1; j<sp->nsubsym; 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; j<sp->nsubsym; 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; j<rp->nrhs; 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; k<sp->nsubsym; 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; i<lemp->nsymbol; 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; i<lemp->nterminal && 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; i<lemp->nterminal; 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<size; i++) array.ht[i] = 0;
|
||||
|
@ -4406,11 +4553,15 @@ struct symbol *Symbol_new(const char *x)
|
|||
return sp;
|
||||
}
|
||||
|
||||
/* Compare two symbols for working purposes
|
||||
/* Compare two symbols for sorting purposes. Return negative,
|
||||
** zero, or positive if a is less then, equal to, or greater
|
||||
** than b.
|
||||
**
|
||||
** Symbols that begin with upper case letters (terminals or tokens)
|
||||
** must sort before symbols that begin with lower case letters
|
||||
** (non-terminals). Other than that, the order does not matter.
|
||||
** (non-terminals). And MULTITERMINAL symbols (created using the
|
||||
** %token_class directive) must sort at the very end. Other than
|
||||
** that, the order does not matter.
|
||||
**
|
||||
** We find experimentally that leaving the symbols in their original
|
||||
** order (the order they appeared in the grammar file) gives the
|
||||
|
@ -4418,12 +4569,11 @@ struct symbol *Symbol_new(const char *x)
|
|||
*/
|
||||
int Symbolcmpp(const void *_a, const void *_b)
|
||||
{
|
||||
const struct symbol **a = (const struct symbol **) _a;
|
||||
const struct symbol **b = (const struct symbol **) _b;
|
||||
int i1 = (**a).index + 10000000*((**a).name[0]>'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; i<size; i++) array.ht[i] = 0;
|
||||
|
@ -4657,8 +4805,7 @@ void State_init(){
|
|||
if( x3a ){
|
||||
x3a->size = 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; i<size; i++) array.ht[i] = 0;
|
||||
|
@ -4753,7 +4899,7 @@ struct state **State_arrayof()
|
|||
int i,size;
|
||||
if( x3a==0 ) return 0;
|
||||
size = x3a->count;
|
||||
array = (struct state **)malloc( sizeof(struct state *)*size );
|
||||
array = (struct state **)calloc(size, sizeof(struct state *));
|
||||
if( array ){
|
||||
for(i=0; i<size; i++) array[i] = x3a->tbl[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<size; i++) array.ht[i] = 0;
|
||||
|
|
|
@ -138,6 +138,11 @@ struct Keyword {
|
|||
#else
|
||||
# define AUTOVACUUM 0x00020000
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_CTE
|
||||
# define CTE 0
|
||||
#else
|
||||
# define CTE 0x00040000
|
||||
#endif
|
||||
|
||||
/*
|
||||
** These are the keywords
|
||||
|
@ -234,6 +239,7 @@ static Keyword aKeywordTable[] = {
|
|||
{ "PRIMARY", "TK_PRIMARY", ALWAYS },
|
||||
{ "QUERY", "TK_QUERY", EXPLAIN },
|
||||
{ "RAISE", "TK_RAISE", TRIGGER },
|
||||
{ "RECURSIVE", "TK_RECURSIVE", CTE },
|
||||
{ "REFERENCES", "TK_REFERENCES", FKEY },
|
||||
{ "REGEXP", "TK_LIKE_KW", ALWAYS },
|
||||
{ "REINDEX", "TK_REINDEX", REINDEX },
|
||||
|
@ -262,6 +268,7 @@ static Keyword aKeywordTable[] = {
|
|||
{ "VALUES", "TK_VALUES", ALWAYS },
|
||||
{ "VIEW", "TK_VIEW", VIEW },
|
||||
{ "VIRTUAL", "TK_VIRTUAL", VTAB },
|
||||
{ "WITH", "TK_WITH", CTE },
|
||||
{ "WITHOUT", "TK_WITHOUT", ALWAYS },
|
||||
{ "WHEN", "TK_WHEN", ALWAYS },
|
||||
{ "WHERE", "TK_WHERE", ALWAYS },
|
||||
|
|
Loading…
Reference in New Issue