Compare commits
103 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 425ab50c8f | |||
| 5eba9b73da | |||
| 34c5ed29e8 | |||
| e255f2d2ce | |||
| c3caa4f091 | |||
| 5dfd3d9080 | |||
| e08ecad58a | |||
| ee06c99b49 | |||
| 7df89c8c8a | |||
| fe21a796f7 | |||
| e39a732ce2 | |||
| 6645370e5b | |||
| dd31490e02 | |||
| d49c545968 | |||
| a3fdec7172 | |||
| bb44b3de39 | |||
| 9a2555f538 | |||
| a88aca5442 | |||
| b8e59bcb86 | |||
| 81a034b62b | |||
| b04275121c | |||
| ec2da85412 | |||
| db718d8a3b | |||
| 4d06798e56 | |||
| 9c2552f2e8 | |||
| 705a48758a | |||
| a820c05aa0 | |||
| d0879bad42 | |||
| 729677720d | |||
| abfe034692 | |||
| b68b97789e | |||
| 693e671936 | |||
| 53bed45ecb | |||
| 6ef5e12e1f | |||
| 05d3dc29c5 | |||
| dc3bb0deb2 | |||
| 9e2c7ae1b0 | |||
| 3c2aeae16e | |||
| b090352b5a | |||
| afcf9bd806 | |||
| cfe24586a8 | |||
| edf83d1e3d | |||
| 953dfa4e60 | |||
| aa9ce7078a | |||
| a8a0617e06 | |||
| fe1c6bb9c2 | |||
| 781def29c7 | |||
| 41028151be | |||
| 340309fd69 | |||
| e73f059093 | |||
| 8561c81ed6 | |||
| a4ff825095 | |||
| 7df42aba12 | |||
| c25e2ebc01 | |||
| 7f3068aa83 | |||
| 717c09c4a9 | |||
| 1fe3c4b526 | |||
| 75303a2c68 | |||
| ebbf08a012 | |||
| 7c82932723 | |||
| c59731c4ae | |||
| 98f45e53a7 | |||
| a026b98500 | |||
| 6785bcca58 | |||
| 718569443d | |||
| b290f11775 | |||
| 2d4dc5fc60 | |||
| 65a2aaa633 | |||
| 727a99f1e3 | |||
| f2655fe8b6 | |||
| eae73fbfb9 | |||
| 6ade453cd8 | |||
| 8290c2ad5a | |||
| 7b19f25247 | |||
| 93c36bb399 | |||
| eede6a538d | |||
| c49832c208 | |||
| 60c1a2f0b5 | |||
| 62ba4e418d | |||
| f43fe6e9f6 | |||
| 60e7068d75 | |||
| a379b32f33 | |||
| bfe31e7f80 | |||
| f9db522fee | |||
| 8ce7184bc2 | |||
| 7a429658d7 | |||
| 866b53eb5a | |||
| c3d6ba49c6 | |||
| 859bc542c6 | |||
| a9f5c13d0c | |||
| 4e9119d9e8 | |||
| 7d562dbe02 | |||
| 8b4718636c | |||
| 03e1b1f5ff | |||
| f59b12fbc1 | |||
| ecaa9d399c | |||
| 61f92cd045 | |||
| 898799fa00 | |||
| 144ffe7bfb | |||
| 75593d96be | |||
| c740752470 | |||
| 9871c59a2d | |||
| 1bcbc621df |
+21
-10
@@ -270,6 +270,17 @@ TCC = $(TCC) -I$(TOP)\ext\rtree
|
||||
RCC = $(RCC) -I$(TOP)\ext\rtree
|
||||
!ENDIF
|
||||
|
||||
# The mksqlite3c.tcl script accepts some options on the command
|
||||
# line. When compiling with debugging enabled, some of these
|
||||
# options are necessary in order to allow debugging symbols to
|
||||
# work correctly with Visual Studio when using the amalgamation.
|
||||
#
|
||||
!IF $(DEBUG)>0
|
||||
MKSQLITE3C_ARGS = --linemacros
|
||||
!ELSE
|
||||
MKSQLITE3C_ARGS =
|
||||
!ENDIF
|
||||
|
||||
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
# includes extra comments when "EXPLAIN stmt" is used.
|
||||
@@ -916,7 +927,7 @@ mptester.exe: $(TOP)\mptest\mptest.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h
|
||||
echo > .target_source
|
||||
|
||||
sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
|
||||
copy tsrc\shell.c .
|
||||
copy tsrc\sqlite3ext.h .
|
||||
|
||||
@@ -1334,17 +1345,17 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
$(LTLINK) -DBUILD_sqlite -DTCLSH=2 -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
showdb.exe: $(TOP)\tool\showdb.c sqlite3.c
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o $@ \
|
||||
$(TOP)\tool\showdb.c sqlite3.c
|
||||
showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\tool\showdb.c $(SQLITE3C)
|
||||
|
||||
wordcount.exe: $(TOP)\test\wordcount.c sqlite3.c
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o $@ \
|
||||
$(TOP)\test\wordcount.c sqlite3.c
|
||||
wordcount.exe: $(TOP)\test\wordcount.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\test\wordcount.c $(SQLITE3C)
|
||||
|
||||
speedtest1.exe: $(TOP)\test\speedtest1.c sqlite3.c
|
||||
$(LTLINK) -DSQLITE_OMIT_LOAD_EXTENSION -o $@ \
|
||||
$(TOP)\test\speedtest1.c sqlite3.c
|
||||
speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\test\speedtest1.c $(SQLITE3C)
|
||||
|
||||
clean:
|
||||
del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -11,5 +11,5 @@ SQLite3 is a self-contains, zero-configuration, transactional SQL database
|
||||
engine. This extension provides an easy to use interface for accessing
|
||||
SQLite database files from Tcl.
|
||||
.PP
|
||||
For full documentation see http://www.sqlite.org/ and
|
||||
in particular http://www.sqlite.org/tclsqlite.html.
|
||||
For full documentation see \fIhttp://www.sqlite.org/\fR and
|
||||
in particular \fIhttp://www.sqlite.org/tclsqlite.html\fR.
|
||||
|
||||
@@ -96,13 +96,13 @@ void sqlite3Fts3HashClear(Fts3Hash *pH){
|
||||
*/
|
||||
static int fts3StrHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
int h = 0;
|
||||
unsigned h = 0;
|
||||
if( nKey<=0 ) nKey = (int) strlen(z);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ *z++;
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
return (int)(h & 0x7fffffff);
|
||||
}
|
||||
static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
|
||||
+28
-4
@@ -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);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
C Start\sa\snew\sexperimental\sbranch\sfor\ssupport\sof\sOracle-style\sCONNECT\sBY\nsyntax.
|
||||
D 2014-01-06T18:32:18.802
|
||||
C Change\smore\sOP_OpenEphemeral\soperations\sto\sOP_OpenHash.
|
||||
D 2014-02-06T14:59:47.807
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc 4d4ead6b71d1bf03028fbd61da0ba0ec5e1556e1
|
||||
F Makefile.msc 6ff3ff2eef45c7dd309a8626ff7947b20bd387e7
|
||||
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
|
||||
@@ -27,7 +27,7 @@ F autoconf/tea/Makefile.in 5c3b0bdfb66c20d55ebff59d1718864461570ca9
|
||||
F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
|
||||
F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
|
||||
F autoconf/tea/configure.in e0466b881b53f31f5a4a69e7a91ad130902fb359
|
||||
F autoconf/tea/doc/sqlite3.n e268faa1691c33663d3a7faf9fa30673d14bd879
|
||||
F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb
|
||||
F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
|
||||
F autoconf/tea/pkgIndex.tcl.in 3ef61715cf1c7bdcff56947ffadb26bc991ca39d
|
||||
F autoconf/tea/tclconfig/install-sh b087e5c4b92820c60bffb74acc1d9c2d40d80b8f
|
||||
@@ -83,7 +83,7 @@ F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h eb5f8029589f3d8f1dc7fd50c773326a640388b1
|
||||
F ext/fts3/fts3_aux.c 5c211e17a64885faeb16b9ba7772f9d5445c2365
|
||||
F ext/fts3/fts3_expr.c 5165c365cb5a035f5be8bb296f7aa3211d43e4ac
|
||||
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
|
||||
F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
|
||||
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
|
||||
F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5
|
||||
F ext/fts3/fts3_porter.c 7f8b4bf5af7c0f20f73b8e87e14fa9298f52e290
|
||||
@@ -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
|
||||
@@ -157,7 +157,7 @@ F mptest/mptest.c 499a74af4be293b7c1c7c3d40f332b67227dd739
|
||||
F mptest/multiwrite01.test 499ad0310da8dff8e8f98d2e272fc2a8aa741b2e
|
||||
F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083
|
||||
F src/analyze.c 581d5c18ce89c6f45d4dca65914d0de5b4dad41f
|
||||
@@ -166,29 +166,29 @@ 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.h a61ddebc78c66795a2b93181321a116746302cc9
|
||||
F src/btree.c ae408b77caf025df8c1baab52aad8a5a393bed35
|
||||
F src/btree.h f1c65e0511d7a228fd1a7c7463ef842525364348
|
||||
F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0
|
||||
F src/build.c 3609c8aa26947d7a035faa23eb1cb2cfc54b4680
|
||||
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 91e1321021db5dc266360531b8b6550009d771ff
|
||||
F src/expr.c 4115ad67088cdd55f4fa0ef3ddd22cb8da8f9c94
|
||||
F src/delete.c 3987ac3e32f6a447b1f3ca0268d67bdaf0a8b97f
|
||||
F src/expr.c fa9cd9b4bdc8989efce99d49c7e46484e17f41f4
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 2ab0f5384b70594468ef3ac5c7ed8ca24bfd17d5
|
||||
F src/func.c 6325ac2ec10833ccf4d5c36d323709221d37ea19
|
||||
F src/func.c f4499b39d66b71825514334ce67b32ff14bd19f5
|
||||
F src/global.c 1d7bb7ea8254ae6a68ed9bfaf65fcb3d1690b486
|
||||
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
|
||||
F src/hash.c d139319967164f139c8d1bb8a11b14db9c4ba3cd
|
||||
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c e6b3d7847aaf06170967ff6ea9076717fbc08d3f
|
||||
F src/insert.c c6b4ba486e62d0b600632fdfce4bca76213158cb
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303
|
||||
F src/main.c 37f55de2000f6c882414ad3e1bcdb751c531fddd
|
||||
F src/main.c 07225af6a00be0b7f34ac52e60f99cf5cbb2a475
|
||||
F src/malloc.c 0203ebce9152c6a0e5de520140b8ba65187350be
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b
|
||||
@@ -205,44 +205,44 @@ 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 abeb9d54036aaea6f4395050ce823f51217ae4d4
|
||||
F src/os_win.c 16eac0961603182ffc10c02b39fe830126538e07
|
||||
F src/os_unix.c f3ed0e406cbf9c820565b2118232d0796346130f
|
||||
F src/os_win.c d4284f003445054a26689f1264b1b9bf7261bd1b
|
||||
F src/pager.c efa923693e958696eee69b205a20bfbc402c8480
|
||||
F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428
|
||||
F src/parse.y 201902e23b29ecee8e58bbd013733493cee6da86
|
||||
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
|
||||
F src/parse.y bd51bc17cbfe7549adb4ca3747b1c3d384645065
|
||||
F src/pcache.c d8eafac28290d4bb80332005435db44991d07fc2
|
||||
F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222
|
||||
F src/pcache1.c 57fee9a9a617218f5037afbbe49b09da65bde56b
|
||||
F src/pcache1.c 102e6f5a2fbc646154463eb856d1fd716867b64c
|
||||
F src/pragma.c ed409ce4104cf4d9de6ead40ace70974f124853b
|
||||
F src/prepare.c 677521ab7132615a8a26107a1d1c3132f44ae337
|
||||
F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269
|
||||
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 d94a8b89522f526ba711182ee161e06f8669bcc9
|
||||
F src/select.c d1acf45637f70d71a76cb8312507550e3aee6caa
|
||||
F src/shell.c 24722d24d4ea8ca93db35e44db7308de786767ca
|
||||
F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b
|
||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h 672ef78e9e6f8b03fc9d37884f9784fb02e6ce2d
|
||||
F src/sqliteInt.h 29b97acb02309ad07de107a81c381512066fc2e4
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c c43379f77f90399802b0e215faa71c0adc3a4d2e
|
||||
F src/test1.c db16ba651453b15001c7f2838c446284dde4ecaf
|
||||
F src/tclsqlite.c 21ca0043d7c48cde5dabed5c1116eee1be692f62
|
||||
F src/test1.c 2401eee14a4309a7cfe2aeb2f30ad517a1d9c299
|
||||
F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35
|
||||
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
|
||||
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
|
||||
F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013
|
||||
F src/test6.c 41cacf3b0dd180823919bf9e1fbab287c9266723
|
||||
F src/test7.c 72b732baa5642f795655ba1126ea032af46ecfd2
|
||||
F src/test8.c c7aab1d9fbbf54fc33d43b73aa24aa55f9eaf534
|
||||
F src/test8.c 54ccd7b1df5062f0ecbf50a8f7b618f8b1f13b20
|
||||
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
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 10d0e00dd6315879a6d9fac20bd063c7bbbfb8f8
|
||||
F src/test_config.c 0336e0bdbe541b4af89d7e3dd0656e8e6b51e585
|
||||
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
|
||||
@@ -274,27 +274,27 @@ 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/update.c c2706a6eb232a96345c35b7e1e75a188e26812bb
|
||||
F src/tokenize.c 6da2de6e12218ccb0aea5184b56727d011f4bee7
|
||||
F src/trigger.c 5c1c0b899ac0ce284763dcb8fdbaa38ecf15ef98
|
||||
F src/update.c 3d2bdfe24d78303cf7fd3017aaa2d848d47a1020
|
||||
F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
|
||||
F src/util.c e71f19b272f05c8695cf747b4bac1732685f9e5c
|
||||
F src/util.c 15ac2627f548f5481d0d7e6c4eb67be673027695
|
||||
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
|
||||
F src/vdbe.c b110887e415b5d2af58c2374c4dfdcf774c5d46c
|
||||
F src/vdbe.c 2e46c464e910cf0f11acf6690e8f72d7239a5f63
|
||||
F src/vdbe.h e6c4c610fcabad4fa80ebb1efc6822a9367e2b26
|
||||
F src/vdbeInt.h 42db251e9f863401ff847b90d5fe1614c89a6a56
|
||||
F src/vdbeapi.c ce4e68ea4842cc6081046f533d088dcf01d247ad
|
||||
F src/vdbeaux.c 9f4bfc52672acbb0bb4493d6a03603dc5a595ac1
|
||||
F src/vdbeaux.c a3327afa8cfcc5bb3d38f2b2a599bac5fb63c6be
|
||||
F src/vdbeblob.c bc40f98f256f0b34116d6a44b114da4a81a15d33
|
||||
F src/vdbemem.c 0e69351b2c6ff7d8b638688c0ae336a26befa6b2
|
||||
F src/vdbemem.c 23cdc14ed43e0aafa57bd72b9bf3d5b1641afa91
|
||||
F src/vdbesort.c 9d83601f9d6243fe70dd0169a2820c5ddfd48147
|
||||
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 18f07fd0fd116a5880773c689eb7cd8e60175107
|
||||
F src/whereInt.h 96a75c61f1d2b9d4a8e4bb17d89deb0cf7cba358
|
||||
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
|
||||
F src/where.c 087307272e374c35c7eb1eb722f0ab7db09317f2
|
||||
F src/whereInt.h 921f935af8b684ffb49705610bda7284db1db138
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
|
||||
@@ -327,7 +327,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
|
||||
@@ -364,15 +364,15 @@ 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
|
||||
F test/check.test 5831ddb6f2c687782eaf2e1a07b6e17f24c4f763
|
||||
F test/close.test 340bd24cc58b16c6bc01967402755027c37eb815
|
||||
F test/closure01.test dbb28f1ea9eeaf0a53ec5bc0fed352e479def8c7
|
||||
F test/closure01.test b1703ba40639cfc9b295cf478d70739415eec6a4
|
||||
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
|
||||
F test/collate1.test 73b91005f264b7c403e2d63a6708d150679ac99a
|
||||
F test/collate2.test 9aaa410a00734e48bcb27f3872617d6f69b2a621
|
||||
@@ -406,6 +406,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 9d8186f6f8751efdfd445d8546fd98f073499039
|
||||
F test/count.test 42a251178e32f617eda33f76236a7f79825a50b5
|
||||
F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62
|
||||
F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
|
||||
@@ -431,20 +432,20 @@ F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test 44028aaf161a5e80a2f229622b3a174d3b352810
|
||||
F test/distinct.test b9f34f3827a67baf5b5bcb1cdf5185963d629d49
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_createtable.test ee95d48664503d40f6cc9ef4a7d03216188e2ada
|
||||
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
|
||||
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
|
||||
F test/e_select.test d3226cb94fae4af3f198e68e71f655e106d0be47
|
||||
F test/e_select2.test 22c660a7becf0712c95e1ca1b2d9e716a1261460
|
||||
F test/e_select.test 52692ff3849541e828ad4661fe3773a9b8711763
|
||||
F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f
|
||||
F test/e_update.test 312cb8f5ccfe41515a6bb092f8ea562a9bd54d52
|
||||
F test/e_uri.test a2c92d80093a7efdcfbb11093651cbea87097b6b
|
||||
F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9
|
||||
@@ -592,9 +593,9 @@ F test/hook.test 162d7cef7a2d2b04839fe14402934e6a1b79442f
|
||||
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
|
||||
F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in3.test dbf41c0c073c10a8c0fee280cba3e9dddbd4a9c6
|
||||
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
|
||||
F test/in5.test 99f9a40af01711b06d2d614ecfe96129f334fba3
|
||||
F test/in5.test 9d8c15bfc9a06da5b354d4d4ecfea9f928769641
|
||||
F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328
|
||||
F test/incrblob2.test bf4d549aa4a466d7fbe3e3a3693d3861263d5600
|
||||
F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
|
||||
@@ -643,10 +644,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
|
||||
@@ -693,14 +694,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
|
||||
@@ -727,8 +728,8 @@ F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
|
||||
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
|
||||
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
|
||||
F test/pager4.test b40ecb4cc7dff957ee7916e41ab21d1ed702a642
|
||||
F test/pagerfault.test 7285379906ab2f1108b8e82bbdf2d386cc8ff3ff
|
||||
F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e
|
||||
F test/pagerfault.test ae9ee0db5a30aecda9db8290ce3dd12e5f7bbaa1
|
||||
F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f
|
||||
F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8
|
||||
F test/pageropt.test 6b8f6a123a5572c195ad4ae40f2987007923bbd6
|
||||
@@ -736,11 +737,11 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
|
||||
F test/permutations.test af3278cbea3a19e025d5169be8193ff48dc3f862
|
||||
F test/permutations.test 40add071ba71aefe1c04f5845308cf46f7de8d04
|
||||
F test/pragma.test e882183ecd21d064cec5c7aaea174fbd36293429
|
||||
F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
F test/printf2.test 414fcba6d6c10e0a5e58efd213811e5ead4635a0
|
||||
F test/printf2.test bed79b4c3e5da08ba88ad637c0bf62586843cfb1
|
||||
F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
|
||||
@@ -782,7 +783,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
|
||||
@@ -824,7 +825,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 61309f5efbec53603b3f86457d34a504f80abafe
|
||||
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
|
||||
F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de
|
||||
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
|
||||
@@ -843,7 +844,7 @@ F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
|
||||
F test/tester.tcl 08e9f317afe60d398fa900993503ecaef3295bad
|
||||
F test/tester.tcl 9bd04481b8b0ef1f2049ad01f28e175ee9a14f7b
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1026,7 +1027,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
|
||||
@@ -1091,6 +1092,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 ce15d69d34a2576b0e47d78c244d1ba2a31679d1
|
||||
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
|
||||
@@ -1109,11 +1113,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 86acc19b96361e90e18d2324b3f56af621aeda85
|
||||
F tool/mkkeywordhash.c c9e05e4a7bcab8fab9f583d5b321fb72f565ad97
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 78a77b2c554d534c6f2dc903130186ed15715460
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
@@ -1123,12 +1127,12 @@ F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12
|
||||
F tool/mksqlite3internalh.tcl 3dca7bb5374cee003379b8cbac73714f610ef795
|
||||
F tool/mkvsix.tcl 6477fb9dab838b7eea1eed50658ff1cda04850b5
|
||||
F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091
|
||||
F tool/omittest.tcl 4665982e95a6e5c1bd806cf7bc3dea95be422d77
|
||||
F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97
|
||||
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
|
||||
F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
|
||||
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
|
||||
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
|
||||
F tool/showdb.c 525ecc443578647703051308ad50a93de6ba2c4b
|
||||
F tool/showdb.c 1f3fe634d6f690b8d39ab1b9fd34583d468921e1
|
||||
F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
|
||||
F tool/showwal.c 3f7f7da5ec0cba51b1449a75f700493377da57b5
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
@@ -1148,10 +1152,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 f461e2b3973d0fe6a7b8cb7a3aaab8a30b3e16c0
|
||||
R 8ea35e9854af1f75afacc581ccadba4b
|
||||
T *branch * connect-by
|
||||
T *sym-connect-by *
|
||||
T -sym-trunk *
|
||||
P 715fac7749a6b1523fe9f7de8263f0c4d1571d07
|
||||
R f507eae713e5b72113d4535fdf641347
|
||||
U drh
|
||||
Z 2785f3b4e9984046b3600748ab3ddac7
|
||||
Z 759564bb3627cf7a97644b4104ef33de
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
4365ddd62d88dc73c59962cf27a3e038eabdb50b
|
||||
881164cf6e5c987ae0e40804267715d878135198
|
||||
@@ -2,7 +2,7 @@
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH SQLITE3 1 "Mon Apr 15 23:49:17 2002"
|
||||
.TH SQLITE3 1 "Mon Jan 31 11:14:00 2014"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
@@ -49,7 +49,7 @@ a table named "memos" and insert a couple of records into that table:
|
||||
$
|
||||
.B sqlite3 mydata.db
|
||||
.br
|
||||
SQLite version 3.1.3
|
||||
SQLite version 3.8.3
|
||||
.br
|
||||
Enter ".help" for instructions
|
||||
.br
|
||||
@@ -108,15 +108,24 @@ sqlite>
|
||||
.B .help
|
||||
.nf
|
||||
.cc |
|
||||
.backup ?DB? FILE Backup DB (default "main") to FILE
|
||||
.bail ON|OFF Stop after hitting an error. Default OFF
|
||||
.databases List names and files of attached databases
|
||||
.dump ?TABLE? ... Dump the database in an SQL text format
|
||||
If TABLE specified, only dump tables matching
|
||||
LIKE pattern TABLE.
|
||||
.echo ON|OFF Turn command echo on or off
|
||||
.exit Exit this program
|
||||
.explain ON|OFF Turn output mode suitable for EXPLAIN on or off.
|
||||
.explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.
|
||||
With no args, it turns EXPLAIN on.
|
||||
.header(s) ON|OFF Turn display of headers on or off
|
||||
.help Show this message
|
||||
.import FILE TABLE Import data from FILE into TABLE
|
||||
.indices TABLE Show names of all indices on TABLE
|
||||
.indices ?TABLE? Show names of all indices
|
||||
If TABLE specified, only show indices for tables
|
||||
matching LIKE pattern TABLE.
|
||||
.load FILE ?ENTRY? Load an extension library
|
||||
.log FILE|off Turn logging on or off. FILE can be stderr/stdout
|
||||
.mode MODE ?TABLE? Set output mode where MODE is one of:
|
||||
csv Comma-separated values
|
||||
column Left-aligned columns. (See .width)
|
||||
@@ -126,46 +135,76 @@ sqlite>
|
||||
list Values delimited by .separator string
|
||||
tabs Tab-separated values
|
||||
tcl TCL list elements
|
||||
.nullvalue STRING Print STRING in place of NULL values
|
||||
.nullvalue STRING Use STRING in place of NULL values
|
||||
.open ?FILENAME? Close existing database and reopen FILENAME
|
||||
.output FILENAME Send output to FILENAME
|
||||
.output stdout Send output to the screen
|
||||
.print STRING... Print literal STRING
|
||||
.prompt MAIN CONTINUE Replace the standard prompts
|
||||
.quit Exit this program
|
||||
.read FILENAME Execute SQL in FILENAME
|
||||
.restore ?DB? FILE Restore content of DB (default "main") from FILE
|
||||
.schema ?TABLE? Show the CREATE statements
|
||||
If TABLE specified, only show tables matching
|
||||
LIKE pattern TABLE.
|
||||
.separator STRING Change separator used by output mode and .import
|
||||
.show Show the current values for various settings
|
||||
.tables ?PATTERN? List names of tables matching a LIKE pattern
|
||||
.stats ON|OFF Turn stats on or off
|
||||
.tables ?TABLE? List names of tables
|
||||
If TABLE specified, only list tables matching
|
||||
LIKE pattern TABLE.
|
||||
.timeout MS Try opening locked tables for MS milliseconds
|
||||
.width NUM NUM ... Set column widths for "column" mode
|
||||
.trace FILE|off Output each SQL statement as it is run
|
||||
.vfsname ?AUX? Print the name of the VFS stack
|
||||
.width NUM1 NUM2 ... Set column widths for "column" mode
|
||||
.timer ON|OFF Turn the CPU timer measurement on or off
|
||||
sqlite>
|
||||
|cc .
|
||||
.sp
|
||||
.fi
|
||||
|
||||
.SH OPTIONS
|
||||
.B sqlite3
|
||||
has the following options:
|
||||
.TP
|
||||
.BI \-init\ file
|
||||
Read and execute commands from
|
||||
.I file
|
||||
, which can contain a mix of SQL statements and meta-commands.
|
||||
.B \-bail
|
||||
Stop after hitting an error.
|
||||
.TP
|
||||
.B \-echo
|
||||
Print commands before execution.
|
||||
.TP
|
||||
.B \-[no]header
|
||||
Turn headers on or off.
|
||||
.B \-batch
|
||||
Force batch I/O.
|
||||
.TP
|
||||
.B \-column
|
||||
Query results will be displayed in a table like form, using
|
||||
whitespace characters to separate the columns and align the
|
||||
output.
|
||||
.TP
|
||||
.BI \-cmd\ command
|
||||
run
|
||||
.I command
|
||||
before reading stdin
|
||||
.TP
|
||||
.B \-csv
|
||||
Set output mode to CSV (comma separated values).
|
||||
.TP
|
||||
.B \-echo
|
||||
Print commands before execution.
|
||||
.TP
|
||||
.BI \-init\ file
|
||||
Read and execute commands from
|
||||
.I file
|
||||
, which can contain a mix of SQL statements and meta-commands.
|
||||
.TP
|
||||
.B \-[no]header
|
||||
Turn headers on or off.
|
||||
.TP
|
||||
.B \-help
|
||||
Show help on options and exit.
|
||||
.TP
|
||||
.B \-html
|
||||
Query results will be output as simple HTML tables.
|
||||
.TP
|
||||
.B \-interactive
|
||||
Force interactive I/O.
|
||||
.TP
|
||||
.B \-line
|
||||
Query results will be displayed with one value per line, rows
|
||||
separated by a blank line. Designed to be easily parsed by
|
||||
@@ -175,18 +214,28 @@ scripts or other programs
|
||||
Query results will be displayed with the separator (|, by default)
|
||||
character between each field value. The default.
|
||||
.TP
|
||||
.BI \-separator\ separator
|
||||
Set output field separator. Default is '|'.
|
||||
.BI \-mmap\ N
|
||||
Set default mmap size to
|
||||
.I N
|
||||
\.
|
||||
.TP
|
||||
.BI \-nullvalue\ string
|
||||
Set string used to represent NULL values. Default is ''
|
||||
(empty string).
|
||||
.TP
|
||||
.BI \-separator\ separator
|
||||
Set output field separator. Default is '|'.
|
||||
.TP
|
||||
.B \-stats
|
||||
Print memory stats before each finalize.
|
||||
.TP
|
||||
.B \-version
|
||||
Show SQLite version.
|
||||
.TP
|
||||
.B \-help
|
||||
Show help on options and exit.
|
||||
.BI \-vfs\ name
|
||||
Use
|
||||
.I name
|
||||
as the default VFS.
|
||||
|
||||
|
||||
.SH INIT FILE
|
||||
@@ -222,8 +271,9 @@ o All other command line options are processed.
|
||||
.SH SEE ALSO
|
||||
http://www.sqlite.org/
|
||||
.br
|
||||
The sqlite-doc package
|
||||
The sqlite3-doc package.
|
||||
.SH AUTHOR
|
||||
This manual page was originally written by Andreas Rottmann
|
||||
<rotty@debian.org>, for the Debian GNU/Linux system (but may be used
|
||||
by others). It was subsequently revised by Bill Bumgarner <bbum@mac.com>.
|
||||
by others). It was subsequently revised by Bill Bumgarner <bbum@mac.com> and
|
||||
further updated by Laszlo Boszormenyi <gcs@debian.hu> .
|
||||
|
||||
+49
-25
@@ -1527,7 +1527,7 @@ static int btreeInitPage(MemPage *pPage){
|
||||
** Set up a raw page so that it looks like a database page holding
|
||||
** no entries.
|
||||
*/
|
||||
static void zeroPage(MemPage *pPage, int flags){
|
||||
static void zeroPage(MemPage *pPage, u8 flags){
|
||||
unsigned char *data = pPage->aData;
|
||||
BtShared *pBt = pPage->pBt;
|
||||
u8 hdr = pPage->hdrOffset;
|
||||
@@ -2585,6 +2585,7 @@ static int newDatabase(BtShared *pBt){
|
||||
MemPage *pP1;
|
||||
unsigned char *data;
|
||||
int rc;
|
||||
u8 flags;
|
||||
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
if( pBt->nPage>0 ){
|
||||
@@ -2607,7 +2608,9 @@ static int newDatabase(BtShared *pBt){
|
||||
data[22] = 32;
|
||||
data[23] = 32;
|
||||
memset(&data[24], 0, 100-24);
|
||||
zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
|
||||
flags = (pBt->openFlags&BTREE_SINGLE_INDEX) ? PTF_ZERODATA|PTF_LEAF
|
||||
: PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF;
|
||||
zeroPage(pP1, flags);
|
||||
pBt->btsFlags |= BTS_PAGESIZE_FIXED;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 );
|
||||
@@ -3754,7 +3757,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 +4393,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;
|
||||
@@ -4779,6 +4780,15 @@ int sqlite3BtreeEof(BtCursor *pCur){
|
||||
** successful then set *pRes=0. If the cursor
|
||||
** was already pointing to the last entry in the database before
|
||||
** this routine was called, then set *pRes=1.
|
||||
**
|
||||
** The calling function will set *pRes to 0 or 1. The initial *pRes value
|
||||
** will be 1 if the cursor being stepped corresponds to an SQL index and
|
||||
** if this routine could have been skipped if that SQL index had been
|
||||
** a unique index. Otherwise the caller will have set *pRes to zero.
|
||||
** Zero is the common case. The btree implementation is free to use the
|
||||
** initial *pRes value as a hint to improve performance, but the current
|
||||
** SQLite btree implementation does not. (Note that the comdb2 btree
|
||||
** implementation does use this hint, however.)
|
||||
*/
|
||||
int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
||||
int rc;
|
||||
@@ -4787,6 +4797,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pRes!=0 );
|
||||
assert( *pRes==0 || *pRes==1 );
|
||||
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
|
||||
if( pCur->eState!=CURSOR_VALID ){
|
||||
rc = restoreCursorPosition(pCur);
|
||||
@@ -4865,6 +4876,15 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
||||
** successful then set *pRes=0. If the cursor
|
||||
** was already pointing to the first entry in the database before
|
||||
** this routine was called, then set *pRes=1.
|
||||
**
|
||||
** The calling function will set *pRes to 0 or 1. The initial *pRes value
|
||||
** will be 1 if the cursor being stepped corresponds to an SQL index and
|
||||
** if this routine could have been skipped if that SQL index had been
|
||||
** a unique index. Otherwise the caller will have set *pRes to zero.
|
||||
** Zero is the common case. The btree implementation is free to use the
|
||||
** initial *pRes value as a hint to improve performance, but the current
|
||||
** SQLite btree implementation does not. (Note that the comdb2 btree
|
||||
** implementation does use this hint, however.)
|
||||
*/
|
||||
int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
||||
int rc;
|
||||
@@ -4872,6 +4892,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pRes!=0 );
|
||||
assert( *pRes==0 || *pRes==1 );
|
||||
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
|
||||
pCur->atLast = 0;
|
||||
if( pCur->eState!=CURSOR_VALID ){
|
||||
@@ -5251,6 +5272,7 @@ end_allocate_page:
|
||||
if( rc==SQLITE_OK ){
|
||||
if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
|
||||
releasePage(*ppPage);
|
||||
*ppPage = 0;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
(*ppPage)->isInit = 0;
|
||||
@@ -6090,7 +6112,7 @@ static int balance_nonroot(
|
||||
u16 leafCorrection; /* 4 if pPage is a leaf. 0 if not */
|
||||
int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
|
||||
int usableSpace; /* Bytes in pPage beyond the header */
|
||||
int pageFlags; /* Value of pPage->aData[0] */
|
||||
u8 pageFlags; /* Value of pPage->aData[0] */
|
||||
int subtotal; /* Subtotal of bytes in cells on one page */
|
||||
int iSpace1 = 0; /* First unused byte of aSpace1[] */
|
||||
int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
|
||||
@@ -7097,7 +7119,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
** sub-tree headed by the child page of the cell being deleted. This makes
|
||||
** balancing the tree following the delete operation easier. */
|
||||
if( !pPage->leaf ){
|
||||
int notUsed;
|
||||
int notUsed = 0;
|
||||
rc = sqlite3BtreePrevious(pCur, ¬Used);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
@@ -7191,7 +7213,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
||||
MemPage *pRoot;
|
||||
Pgno pgnoRoot;
|
||||
int rc;
|
||||
int ptfFlags; /* Page-type flage for the root page of new table */
|
||||
u8 ptfFlags; /* Page-type flage for the root page of new table */
|
||||
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
@@ -7350,6 +7372,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 +7381,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 +7392,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 +7401,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:
|
||||
|
||||
+5
-4
@@ -56,10 +56,11 @@ int sqlite3BtreeOpen(
|
||||
** NOTE: These values must match the corresponding PAGER_ values in
|
||||
** pager.h.
|
||||
*/
|
||||
#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */
|
||||
#define BTREE_MEMORY 2 /* This is an in-memory DB */
|
||||
#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */
|
||||
#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */
|
||||
#define BTREE_OMIT_JOURNAL 0x01 /* Do not create or use a rollback journal */
|
||||
#define BTREE_MEMORY 0x02 /* This is an in-memory DB */
|
||||
#define BTREE_SINGLE 0x04 /* The file contains at most 1 b-tree */
|
||||
#define BTREE_UNORDERED 0x08 /* Use of a hash implementation is OK */
|
||||
#define BTREE_SINGLE_INDEX 0x10 /* File contains one index btree */
|
||||
|
||||
int sqlite3BtreeClose(Btree*);
|
||||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
|
||||
+74
-4
@@ -1452,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++){
|
||||
@@ -4198,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) */
|
||||
|
||||
+6
-7
@@ -97,10 +97,8 @@ void sqlite3MaterializeView(
|
||||
SrcList *pFrom;
|
||||
sqlite3 *db = pParse->db;
|
||||
int iDb = sqlite3SchemaToIndex(db, pView->pSchema);
|
||||
|
||||
pWhere = sqlite3ExprDup(db, pWhere, 0);
|
||||
pFrom = sqlite3SrcListAppend(db, 0, 0, 0);
|
||||
|
||||
if( pFrom ){
|
||||
assert( pFrom->nSrc==1 );
|
||||
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
|
||||
@@ -108,10 +106,7 @@ void sqlite3MaterializeView(
|
||||
assert( pFrom->a[0].pOn==0 );
|
||||
assert( pFrom->a[0].pUsing==0 );
|
||||
}
|
||||
|
||||
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
|
||||
if( pSel ) pSel->selFlags |= SF_Materialize;
|
||||
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
|
||||
sqlite3Select(pParse, pSel, &dest);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
@@ -388,7 +383,9 @@ void sqlite3DeleteFrom(
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
||||
}else{
|
||||
/* For a WITHOUT ROWID table, create an ephermeral table used to
|
||||
** hold all primary keys for rows to be deleted. */
|
||||
** hold all primary keys for rows to be deleted. Use OP_OpenEphemeral
|
||||
** rather than OP_OpenHash since for efficiency reasons it is good to
|
||||
** process the primary keys in order. */
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
@@ -636,7 +633,9 @@ void sqlite3GenerateRowDelete(
|
||||
** used by any BEFORE and AFTER triggers that exist. */
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld);
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( mask==0xffffffff || mask&(1<<iCol) ){
|
||||
testcase( mask!=0xffffffff && iCol==31 );
|
||||
testcase( mask!=0xffffffff && iCol==32 );
|
||||
if( mask==0xffffffff || (iCol<=31 && (mask & MASKBIT32(iCol))!=0) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1);
|
||||
}
|
||||
}
|
||||
|
||||
+252
-215
@@ -895,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
|
||||
@@ -975,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;
|
||||
@@ -1036,6 +1064,8 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
||||
pNew->addrOpenEphm[0] = -1;
|
||||
pNew->addrOpenEphm[1] = -1;
|
||||
pNew->addrOpenEphm[2] = -1;
|
||||
pNew->nSelectRow = p->nSelectRow;
|
||||
pNew->pWith = withDup(db, p->pWith);
|
||||
return pNew;
|
||||
}
|
||||
#else
|
||||
@@ -1460,6 +1490,177 @@ int sqlite3CodeOnce(Parse *pParse){
|
||||
return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that constructs a transient table for the RHS of an IN
|
||||
** operator:
|
||||
**
|
||||
** x IN (4,5,11) -- IN operator with list on right-hand side
|
||||
** x IN (SELECT a FROM b) -- IN operator with subquery on the right
|
||||
**
|
||||
** The pExpr parameter is the IN expression.
|
||||
**
|
||||
** If parameter isRowid is non-zero, then expression pExpr is guaranteed
|
||||
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
|
||||
** to some integer key column of a table B-Tree. In this case, use an
|
||||
** intkey B-Tree to store the set of IN(...) values instead of the usual
|
||||
** (slower) variable length keys B-Tree.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
static void sqlite3CreateInOperatorRhsTable(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
|
||||
int isRowid, /* If true, LHS of IN operator is a rowid */
|
||||
int bOrdered /* If true, must use btree, not a hash */
|
||||
){
|
||||
int testAddr = -1; /* One-time test address */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* prepared stmt under construction */
|
||||
char affinity; /* Affinity of the LHS of the IN */
|
||||
int addr; /* Address of OP_Open.. instruction */
|
||||
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
||||
KeyInfo *pKeyInfo = 0; /* Key information */
|
||||
|
||||
assert( v!=0 );
|
||||
assert( pExpr->op==TK_IN );
|
||||
sqlite3ExprCachePush(pParse);
|
||||
|
||||
/* This code must be run in its entirety every time it is encountered
|
||||
** if any of the following is true:
|
||||
**
|
||||
** * The right-hand side is a correlated subquery
|
||||
** * We are inside a trigger
|
||||
**
|
||||
** If all of the above are false, then we can run this code just once
|
||||
** save the results, and reuse the same result on subsequent invocations.
|
||||
*/
|
||||
if( !ExprHasProperty(pExpr, EP_VarSelect) ){
|
||||
testAddr = sqlite3CodeOnce(pParse);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "EXECUTE %sLIST SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
|
||||
pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
affinity = sqlite3ExprAffinity(pLeft);
|
||||
|
||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||
** expression it is handled the same way. An ephemeral table is
|
||||
** filled with single-field index keys representing the results
|
||||
** from the SELECT or the <exprlist>.
|
||||
**
|
||||
** If the 'x' expression is a column value, or the SELECT...
|
||||
** statement returns a column value, then the affinity of that
|
||||
** column is used to build the index keys. If both 'x' and the
|
||||
** SELECT... statement are columns, then numeric affinity is used
|
||||
** if either column has NUMERIC or INTEGER affinity. If neither
|
||||
** 'x' nor the SELECT... statement are columns, then numeric affinity
|
||||
** is used.
|
||||
*/
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
addr = sqlite3VdbeAddOp2(v, bOrdered ? OP_OpenEphemeral : OP_OpenHash,
|
||||
pExpr->iTable, !isRowid);
|
||||
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
|
||||
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
** table allocated and opened above.
|
||||
*/
|
||||
SelectDest dest;
|
||||
ExprList *pEList;
|
||||
|
||||
assert( !isRowid );
|
||||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||
dest.affSdst = (u8)affinity;
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
pExpr->x.pSelect->iLimit = 0;
|
||||
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
sqlite3KeyInfoUnref(pKeyInfo);
|
||||
return;
|
||||
}
|
||||
pEList = pExpr->x.pSelect->pEList;
|
||||
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
|
||||
assert( pEList!=0 );
|
||||
assert( pEList->nExpr>0 );
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
||||
pEList->a[0].pExpr);
|
||||
}else if( ALWAYS(pExpr->x.pList!=0) ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
**
|
||||
** For each expression, build an index key from the evaluation and
|
||||
** store it in the temporary table. If <expr> is a column, then use
|
||||
** that columns affinity when building index keys. If <expr> is not
|
||||
** a column, use numeric affinity.
|
||||
*/
|
||||
int i;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
struct ExprList_item *pItem;
|
||||
int r1, r2, r3;
|
||||
|
||||
if( !affinity ){
|
||||
affinity = SQLITE_AFF_NONE;
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
|
||||
}
|
||||
|
||||
/* Loop through each expression in <exprlist>. */
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
|
||||
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
|
||||
Expr *pE2 = pItem->pExpr;
|
||||
int iValToIns;
|
||||
|
||||
/* If the expression is not constant then we will need to
|
||||
** disable the test that was generated above that makes sure
|
||||
** this code only executes once. Because for a non-constant
|
||||
** expression we need to rerun this code each time.
|
||||
*/
|
||||
if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){
|
||||
sqlite3VdbeChangeToNoop(v, testAddr);
|
||||
testAddr = -1;
|
||||
}
|
||||
|
||||
/* Evaluate the expression and insert it into the temp table */
|
||||
if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
|
||||
sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
|
||||
}else{
|
||||
r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
|
||||
if( isRowid ){
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
|
||||
sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
|
||||
}else{
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r3, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
|
||||
if( testAddr>=0 ){
|
||||
sqlite3VdbeJumpHere(v, testAddr);
|
||||
}
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_SUBQUERY */
|
||||
|
||||
/*
|
||||
** This function is used by the implementation of the IN (...) operator.
|
||||
** The pX parameter is the expression on the RHS of the IN operator, which
|
||||
@@ -1523,15 +1724,19 @@ int sqlite3CodeOnce(Parse *pParse){
|
||||
**
|
||||
** in order to avoid running the <test if data structure contains null>
|
||||
** test more often than is necessary.
|
||||
**
|
||||
** IN_INDEX_EPH ephemeral tables must be in key order if the bOrdered flag
|
||||
** is true. If bOrdered is false, the generated table can be a hash.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound, int bOrdered){
|
||||
Select *p; /* SELECT to the right of IN operator */
|
||||
int eType = 0; /* Type of RHS table. IN_INDEX_* */
|
||||
int iTab = pParse->nTab++; /* Cursor of the RHS table */
|
||||
int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
|
||||
|
||||
assert( v!=0 );
|
||||
assert( pX->op==TK_IN );
|
||||
|
||||
/* Check to see if an existing table or index can be used to
|
||||
@@ -1540,11 +1745,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
*/
|
||||
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
|
||||
if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Table *pTab; /* Table <table>. */
|
||||
Expr *pExpr; /* Expression <column> */
|
||||
i16 iCol; /* Index of column <column> */
|
||||
i16 iDb; /* Database idx for pTab */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Table *pTab; /* Table <table>. */
|
||||
Expr *pExpr; /* Expression <column> */
|
||||
i16 iCol; /* Index of column <column> */
|
||||
i16 iDb; /* Database idx for pTab */
|
||||
|
||||
assert( p ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
@@ -1559,19 +1764,10 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
/* This function is only called from two places. In both cases the vdbe
|
||||
** has already been allocated. So assume sqlite3GetVdbe() is always
|
||||
** successful here.
|
||||
*/
|
||||
assert(v);
|
||||
if( iCol<0 ){
|
||||
int iAddr;
|
||||
|
||||
iAddr = sqlite3CodeOnce(pParse);
|
||||
|
||||
int iAddr = sqlite3CodeOnce(pParse);
|
||||
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
|
||||
eType = IN_INDEX_ROWID;
|
||||
|
||||
sqlite3VdbeJumpHere(v, iAddr);
|
||||
}else{
|
||||
Index *pIdx; /* Iterator variable */
|
||||
@@ -1626,7 +1822,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
eType = IN_INDEX_ROWID;
|
||||
}
|
||||
}
|
||||
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
|
||||
sqlite3CreateInOperatorRhsTable(pParse, pX, eType==IN_INDEX_ROWID,bOrdered);
|
||||
pParse->nQueryLoop = savedNQueryLoop;
|
||||
}else{
|
||||
pX->iTable = iTab;
|
||||
@@ -1636,56 +1832,37 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
|
||||
** or IN operators. Examples:
|
||||
** Generate code for scalar subqueries used as a subquery expression:
|
||||
**
|
||||
** (SELECT a FROM b) -- subquery
|
||||
** EXISTS (SELECT a FROM b) -- EXISTS subquery
|
||||
** x IN (4,5,11) -- IN operator with list on right-hand side
|
||||
** x IN (SELECT a FROM b) -- IN operator with subquery on the right
|
||||
**
|
||||
** The pExpr parameter describes the expression that contains the IN
|
||||
** operator or subquery.
|
||||
** The pExpr parameter describes the expression that contains the subquery.
|
||||
**
|
||||
** If parameter isRowid is non-zero, then expression pExpr is guaranteed
|
||||
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
|
||||
** to some integer key column of a table B-Tree. In this case, use an
|
||||
** intkey B-Tree to store the set of IN(...) values instead of the usual
|
||||
** (slower) variable length keys B-Tree.
|
||||
**
|
||||
** If rMayHaveNull is non-zero, that means that the operation is an IN
|
||||
** (not a SELECT or EXISTS) and that the RHS might contains NULLs.
|
||||
** Furthermore, the IN is in a WHERE clause and that we really want
|
||||
** to iterate over the RHS of the IN operator in order to quickly locate
|
||||
** all corresponding LHS elements. All this routine does is initialize
|
||||
** the register given by rMayHaveNull to NULL. Calling routines will take
|
||||
** care of changing this register value to non-NULL if the RHS is NULL-free.
|
||||
**
|
||||
** If rMayHaveNull is zero, that means that the subquery is being used
|
||||
** for membership testing only. There is no need to initialize any
|
||||
** registers to indicate the presence or absence of NULLs on the RHS.
|
||||
**
|
||||
** For a SELECT or EXISTS operator, return the register that holds the
|
||||
** result. For IN operators or if an error occurs, the return value is 0.
|
||||
** Return the register that holds the result.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3CodeSubselect(
|
||||
static int sqlite3CodeScalarSubquery(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
|
||||
int rMayHaveNull, /* Register that records whether NULLs exist in RHS */
|
||||
int isRowid /* If true, LHS of IN operator is a rowid */
|
||||
Expr *pExpr /* The SELECT or EXISTS operator */
|
||||
){
|
||||
int testAddr = -1; /* One-time test address */
|
||||
int rReg = 0; /* Register storing resulting */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
Select *pSel; /* SELECT statement to encode */
|
||||
SelectDest dest; /* How to deal with SELECt result */
|
||||
|
||||
if( NEVER(v==0) ) return 0;
|
||||
testcase( pExpr->op==TK_EXISTS );
|
||||
testcase( pExpr->op==TK_SELECT );
|
||||
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
sqlite3ExprCachePush(pParse);
|
||||
|
||||
/* This code must be run in its entirety every time it is encountered
|
||||
** if any of the following is true:
|
||||
**
|
||||
** * The right-hand side is a correlated subquery
|
||||
** * The right-hand side is an expression list containing variables
|
||||
** * pExpr is a correlated subquery
|
||||
** * We are inside a trigger
|
||||
**
|
||||
** If all of the above are false, then we can run this code just once
|
||||
@@ -1698,174 +1875,33 @@ int sqlite3CodeSubselect(
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
|
||||
pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
|
||||
pParse->db, "EXECUTE %sSCALAR SUBQUERY %d",
|
||||
testAddr>=0?"":"CORRELATED ", pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch( pExpr->op ){
|
||||
case TK_IN: {
|
||||
char affinity; /* Affinity of the LHS of the IN */
|
||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
||||
KeyInfo *pKeyInfo = 0; /* Key information */
|
||||
|
||||
if( rMayHaveNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
|
||||
}
|
||||
|
||||
affinity = sqlite3ExprAffinity(pLeft);
|
||||
|
||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||
** expression it is handled the same way. An ephemeral table is
|
||||
** filled with single-field index keys representing the results
|
||||
** from the SELECT or the <exprlist>.
|
||||
**
|
||||
** If the 'x' expression is a column value, or the SELECT...
|
||||
** statement returns a column value, then the affinity of that
|
||||
** column is used to build the index keys. If both 'x' and the
|
||||
** SELECT... statement are columns, then numeric affinity is used
|
||||
** if either column has NUMERIC or INTEGER affinity. If neither
|
||||
** 'x' nor the SELECT... statement are columns, then numeric affinity
|
||||
** is used.
|
||||
*/
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
|
||||
if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
|
||||
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
** table allocated and opened above.
|
||||
*/
|
||||
SelectDest dest;
|
||||
ExprList *pEList;
|
||||
|
||||
assert( !isRowid );
|
||||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||
dest.affSdst = (u8)affinity;
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
pExpr->x.pSelect->iLimit = 0;
|
||||
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
sqlite3KeyInfoUnref(pKeyInfo);
|
||||
return 0;
|
||||
}
|
||||
pEList = pExpr->x.pSelect->pEList;
|
||||
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
|
||||
assert( pEList!=0 );
|
||||
assert( pEList->nExpr>0 );
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
||||
pEList->a[0].pExpr);
|
||||
}else if( ALWAYS(pExpr->x.pList!=0) ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
**
|
||||
** For each expression, build an index key from the evaluation and
|
||||
** store it in the temporary table. If <expr> is a column, then use
|
||||
** that columns affinity when building index keys. If <expr> is not
|
||||
** a column, use numeric affinity.
|
||||
*/
|
||||
int i;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
struct ExprList_item *pItem;
|
||||
int r1, r2, r3;
|
||||
|
||||
if( !affinity ){
|
||||
affinity = SQLITE_AFF_NONE;
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
|
||||
}
|
||||
|
||||
/* Loop through each expression in <exprlist>. */
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
|
||||
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
|
||||
Expr *pE2 = pItem->pExpr;
|
||||
int iValToIns;
|
||||
|
||||
/* If the expression is not constant then we will need to
|
||||
** disable the test that was generated above that makes sure
|
||||
** this code only executes once. Because for a non-constant
|
||||
** expression we need to rerun this code each time.
|
||||
*/
|
||||
if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){
|
||||
sqlite3VdbeChangeToNoop(v, testAddr);
|
||||
testAddr = -1;
|
||||
}
|
||||
|
||||
/* Evaluate the expression and insert it into the temp table */
|
||||
if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
|
||||
sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
|
||||
}else{
|
||||
r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
|
||||
if( isRowid ){
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
|
||||
sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
|
||||
}else{
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r3, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TK_EXISTS:
|
||||
case TK_SELECT:
|
||||
default: {
|
||||
/* If this has to be a scalar SELECT. Generate code to put the
|
||||
** value of this select in a memory cell and record the number
|
||||
** of the memory cell in iColumn. If this is an EXISTS, write
|
||||
** an integer 0 (not exists) or 1 (exists) into a memory cell
|
||||
** and record that memory cell in iColumn.
|
||||
*/
|
||||
Select *pSel; /* SELECT statement to encode */
|
||||
SelectDest dest; /* How to deal with SELECt result */
|
||||
|
||||
testcase( pExpr->op==TK_EXISTS );
|
||||
testcase( pExpr->op==TK_SELECT );
|
||||
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
||||
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pSel = pExpr->x.pSelect;
|
||||
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
|
||||
if( pExpr->op==TK_SELECT ){
|
||||
dest.eDest = SRT_Mem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init subquery result"));
|
||||
}else{
|
||||
dest.eDest = SRT_Exists;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init EXISTS result"));
|
||||
}
|
||||
sqlite3ExprDelete(pParse->db, pSel->pLimit);
|
||||
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
|
||||
&sqlite3IntTokens[1]);
|
||||
pSel->iLimit = 0;
|
||||
if( sqlite3Select(pParse, pSel, &dest) ){
|
||||
return 0;
|
||||
}
|
||||
rReg = dest.iSDParm;
|
||||
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
||||
break;
|
||||
}
|
||||
pSel = pExpr->x.pSelect;
|
||||
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
|
||||
if( pExpr->op==TK_SELECT ){
|
||||
dest.eDest = SRT_Mem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init subquery result"));
|
||||
}else{
|
||||
dest.eDest = SRT_Exists;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init EXISTS result"));
|
||||
}
|
||||
sqlite3ExprDelete(pParse->db, pSel->pLimit);
|
||||
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
|
||||
&sqlite3IntTokens[1]);
|
||||
pSel->iLimit = 0;
|
||||
if( sqlite3Select(pParse, pSel, &dest) ){
|
||||
return 0;
|
||||
}
|
||||
rReg = dest.iSDParm;
|
||||
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
||||
|
||||
if( testAddr>=0 ){
|
||||
sqlite3VdbeJumpHere(v, testAddr);
|
||||
@@ -1912,7 +1948,7 @@ static void sqlite3ExprCodeIN(
|
||||
v = pParse->pVdbe;
|
||||
assert( v!=0 ); /* OOM detected prior to this routine */
|
||||
VdbeNoopComment((v, "begin IN expr"));
|
||||
eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull);
|
||||
eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull, 0);
|
||||
|
||||
/* Figure out the affinity to use to create a key from the results
|
||||
** of the expression. affinityStr stores a static string suitable for
|
||||
@@ -2656,7 +2692,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
FuncDef *pDef; /* The function definition object */
|
||||
int nId; /* Length of the function name in bytes */
|
||||
const char *zId; /* The function name */
|
||||
int constMask = 0; /* Mask of function arguments that are constant */
|
||||
u32 constMask = 0; /* Mask of function arguments that are constant */
|
||||
int i; /* Loop counter */
|
||||
u8 enc = ENC(db); /* The text encoding used by this database */
|
||||
CollSeq *pColl = 0; /* A collating sequence */
|
||||
@@ -2707,7 +2743,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
|
||||
for(i=0; i<nFarg; i++){
|
||||
if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
|
||||
constMask |= (1<<i);
|
||||
testcase( i==31 );
|
||||
constMask |= MASKBIT32(i);
|
||||
}
|
||||
if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
|
||||
pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
|
||||
@@ -2783,7 +2820,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
case TK_SELECT: {
|
||||
testcase( op==TK_EXISTS );
|
||||
testcase( op==TK_SELECT );
|
||||
inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
|
||||
inReg = sqlite3CodeScalarSubquery(pParse, pExpr);
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
case SQLITE_INTEGER: {
|
||||
i64 iVal = sqlite3_value_int64(argv[0]);
|
||||
if( iVal<0 ){
|
||||
if( (iVal<<1)==0 ){
|
||||
if( iVal==SMALLEST_INT64 ){
|
||||
/* IMP: R-31676-45509 If X is the integer -9223372036854775808
|
||||
** then abs(X) throws an integer overflow error since there is no
|
||||
** equivalent positive 64-bit two complement value. */
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ void sqlite3HashClear(Hash *pH){
|
||||
** The hashing function.
|
||||
*/
|
||||
static unsigned int strHash(const char *z, int nKey){
|
||||
int h = 0;
|
||||
unsigned int h = 0;
|
||||
assert( nKey>=0 );
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
|
||||
|
||||
+21
-1
@@ -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 );
|
||||
@@ -708,6 +719,9 @@ void sqlite3Insert(
|
||||
** insert row from R..R+n into temp table
|
||||
** goto L
|
||||
** M: ...
|
||||
**
|
||||
** Use OP_OpenEphemeral, not OP_OpenHash, so that srcTab behaves as
|
||||
** a FIFO.
|
||||
*/
|
||||
int regRec; /* Register to hold packed record */
|
||||
int regTempRowid; /* Register to hold temp table ROWID */
|
||||
@@ -1847,6 +1861,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 */
|
||||
}
|
||||
|
||||
+1
-8
@@ -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,
|
||||
@@ -3310,7 +3303,7 @@ int sqlite3_test_control(int op, ...){
|
||||
** that demonstrat invariants on well-formed database files.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_NEVER_CORRUPT: {
|
||||
sqlite3Config.neverCorrupt = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -4097,7 +4097,7 @@ static int unixShmSystemLock(
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ u16 mask;
|
||||
OSTRACE(("SHM-LOCK "));
|
||||
mask = ofst>31 ? 0xffffffff : (1<<(ofst+n)) - (1<<ofst);
|
||||
mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( lockType==F_UNLCK ){
|
||||
OSTRACE(("unlock %d ok", ofst));
|
||||
@@ -4848,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. */
|
||||
@@ -4867,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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -3201,7 +3201,7 @@ static int winDeviceCharacteristics(sqlite3_file *id){
|
||||
** During sqlite3_os_init() we do a GetSystemInfo()
|
||||
** to get the granularity size.
|
||||
*/
|
||||
SYSTEM_INFO winSysInfo;
|
||||
static SYSTEM_INFO winSysInfo;
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
+95
-123
@@ -94,23 +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;
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this structure stores the optional Oracle Hierarchical
|
||||
** Query clauses - the START WITH and CONNECT BY clauses.
|
||||
*/
|
||||
struct HierarchicalQuery {
|
||||
Expr *pStartWith; /* The START WITH condition */
|
||||
Expr *pConnectBy; /* The CONNECT BY condition */
|
||||
};
|
||||
|
||||
} // end %include
|
||||
|
||||
// Input is a single SQL command
|
||||
@@ -211,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.
|
||||
@@ -223,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
|
||||
@@ -258,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:
|
||||
//
|
||||
@@ -417,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);
|
||||
@@ -427,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;
|
||||
@@ -447,10 +438,27 @@ multiselect_op(A) ::= UNION(OP). {A = @OP;}
|
||||
multiselect_op(A) ::= UNION ALL. {A = TK_ALL;}
|
||||
multiselect_op(A) ::= EXCEPT|INTERSECT(OP). {A = @OP;}
|
||||
%endif SQLITE_OMIT_COMPOUND_SELECT
|
||||
oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) hq_opt
|
||||
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.
|
||||
@@ -648,31 +656,20 @@ limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y).
|
||||
limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
|
||||
{A.pOffset = X.pExpr; A.pLimit = Y.pExpr;}
|
||||
|
||||
///////// Oracle-style CONNECT BY clauses for hierarchical queries ////////////
|
||||
|
||||
%type hq_opt {struct HierarchicalQuery}
|
||||
%destructor hq_opt {
|
||||
sqlite3ExprDelete(pParse->db, $$.pStartWith);
|
||||
sqlite3ExprDelete(pParse->db, $$.pConnectBy);
|
||||
}
|
||||
|
||||
hq_opt(A) ::= START WITH expr(X) CONNECT BY expr(Y).
|
||||
{ A.pStartWith=X.pExpr; A.pConnectBy=Y.pExpr; }
|
||||
hq_opt(A) ::= CONNECT BY expr(Y). { A.pStartWith=0; A.pConnectBy=Y.pExpr; }
|
||||
hq_opt(A) ::= . { A.pStartWith=0; A.pConnectBy=0; }
|
||||
|
||||
/////////////////////////// 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);
|
||||
}
|
||||
@@ -687,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");
|
||||
@@ -696,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);
|
||||
@@ -718,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*}
|
||||
@@ -832,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). {
|
||||
@@ -861,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);
|
||||
}
|
||||
@@ -871,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);
|
||||
}
|
||||
@@ -910,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);
|
||||
@@ -1007,8 +966,6 @@ expr(A) ::= MINUS(B) expr(X). [BITNOT]
|
||||
{spanUnaryPrefix(&A,pParse,TK_UMINUS,&X,&B);}
|
||||
expr(A) ::= PLUS(B) expr(X). [BITNOT]
|
||||
{spanUnaryPrefix(&A,pParse,TK_UPLUS,&X,&B);}
|
||||
expr(A) ::= PRIOR(B) expr(X). [BITNOT]
|
||||
{spanUnaryPrefix(&A,pParse,TK_PRIOR,&X,&B);}
|
||||
|
||||
%type between_op {int}
|
||||
between_op(A) ::= BETWEEN. {A = 0;}
|
||||
@@ -1223,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
|
||||
@@ -1319,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).
|
||||
@@ -1430,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
|
||||
|
||||
+26
-8
@@ -23,7 +23,8 @@ struct PCache {
|
||||
int szCache; /* Configured cache size */
|
||||
int szPage; /* Size of every page in this cache */
|
||||
int szExtra; /* Size of extra space for each page */
|
||||
int bPurgeable; /* True if pages are on backing store */
|
||||
u8 bPurgeable; /* True if pages are on backing store */
|
||||
u8 eCreate; /* eCreate value for for xFetch() */
|
||||
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
|
||||
void *pStress; /* Argument to xStress */
|
||||
sqlite3_pcache *pCache; /* Pluggable cache module */
|
||||
@@ -90,6 +91,10 @@ static void pcacheRemoveFromDirtyList(PgHdr *pPage){
|
||||
}else{
|
||||
assert( pPage==p->pDirty );
|
||||
p->pDirty = pPage->pDirtyNext;
|
||||
if( p->pDirty==0 && p->bPurgeable ){
|
||||
assert( p->eCreate==1 );
|
||||
p->eCreate = 2;
|
||||
}
|
||||
}
|
||||
pPage->pDirtyNext = 0;
|
||||
pPage->pDirtyPrev = 0;
|
||||
@@ -110,6 +115,9 @@ static void pcacheAddToDirtyList(PgHdr *pPage){
|
||||
if( pPage->pDirtyNext ){
|
||||
assert( pPage->pDirtyNext->pDirtyPrev==0 );
|
||||
pPage->pDirtyNext->pDirtyPrev = pPage;
|
||||
}else if( p->bPurgeable ){
|
||||
assert( p->eCreate==2 );
|
||||
p->eCreate = 1;
|
||||
}
|
||||
p->pDirty = pPage;
|
||||
if( !p->pDirtyTail ){
|
||||
@@ -179,6 +187,7 @@ void sqlite3PcacheOpen(
|
||||
p->szPage = szPage;
|
||||
p->szExtra = szExtra;
|
||||
p->bPurgeable = bPurgeable;
|
||||
p->eCreate = 2;
|
||||
p->xStress = xStress;
|
||||
p->pStress = pStress;
|
||||
p->szCache = 100;
|
||||
@@ -218,7 +227,7 @@ int sqlite3PcacheFetch(
|
||||
int createFlag, /* If true, create page if it does not exist already */
|
||||
PgHdr **ppPage /* Write the page here */
|
||||
){
|
||||
sqlite3_pcache_page *pPage = 0;
|
||||
sqlite3_pcache_page *pPage;
|
||||
PgHdr *pPgHdr = 0;
|
||||
int eCreate;
|
||||
|
||||
@@ -229,8 +238,12 @@ int sqlite3PcacheFetch(
|
||||
/* If the pluggable cache (sqlite3_pcache*) has not been allocated,
|
||||
** allocate it now.
|
||||
*/
|
||||
if( !pCache->pCache && createFlag ){
|
||||
if( !pCache->pCache ){
|
||||
sqlite3_pcache *p;
|
||||
if( !createFlag ){
|
||||
*ppPage = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
p = sqlite3GlobalConfig.pcache2.xCreate(
|
||||
pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable
|
||||
);
|
||||
@@ -241,11 +254,16 @@ int sqlite3PcacheFetch(
|
||||
pCache->pCache = p;
|
||||
}
|
||||
|
||||
eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty));
|
||||
if( pCache->pCache ){
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
}
|
||||
|
||||
/* eCreate defines what to do if the page does not exist.
|
||||
** 0 Do not allocate a new page. (createFlag==0)
|
||||
** 1 Allocate a new page if doing so is inexpensive.
|
||||
** (createFlag==1 AND bPurgeable AND pDirty)
|
||||
** 2 Allocate a new page even it doing so is difficult.
|
||||
** (createFlag==1 AND !(bPurgeable AND pDirty)
|
||||
*/
|
||||
eCreate = createFlag==0 ? 0 : pCache->eCreate;
|
||||
assert( (createFlag*(1+(!pCache->bPurgeable||!pCache->pDirty)))==eCreate );
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
if( !pPage && eCreate==1 ){
|
||||
PgHdr *pPg;
|
||||
|
||||
|
||||
+2
-1
@@ -720,6 +720,7 @@ static sqlite3_pcache_page *pcache1Fetch(
|
||||
PGroup *pGroup;
|
||||
PgHdr1 *pPage = 0;
|
||||
|
||||
assert( offsetof(PgHdr1,page)==0 );
|
||||
assert( pCache->bPurgeable || createFlag!=1 );
|
||||
assert( pCache->bPurgeable || pCache->nMin==0 );
|
||||
assert( pCache->bPurgeable==0 || pCache->nMin==10 );
|
||||
@@ -825,7 +826,7 @@ fetch_out:
|
||||
pCache->iMaxKey = iKey;
|
||||
}
|
||||
pcache1LeaveMutex(pGroup);
|
||||
return &pPage->page;
|
||||
return (sqlite3_pcache_page*)pPage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+556
-143
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2561,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
|
||||
|
||||
+119
-24
@@ -760,6 +760,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
|
||||
@@ -1428,7 +1429,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 */
|
||||
@@ -1981,6 +1982,7 @@ typedef u64 Bitmask;
|
||||
** A bit in a Bitmask
|
||||
*/
|
||||
#define MASKBIT(n) (((Bitmask)1)<<(n))
|
||||
#define MASKBIT32(n) (((unsigned int)1)<<(n))
|
||||
|
||||
/*
|
||||
** The following structure describes the FROM clause of a SELECT statement.
|
||||
@@ -2017,6 +2019,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
|
||||
@@ -2143,6 +2146,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. */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2157,14 +2161,73 @@ struct Select {
|
||||
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
|
||||
#define SF_UseSorter 0x0040 /* Sort using a sorter */
|
||||
#define SF_Values 0x0080 /* Synthesized from VALUES clause */
|
||||
#define SF_Materialize 0x0100 /* Force materialization of views */
|
||||
#define SF_Materialize 0x0100 /* NOT USED */
|
||||
#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 */
|
||||
@@ -2177,20 +2240,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 */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2291,22 +2358,23 @@ struct Parse {
|
||||
int nSet; /* Number of sets used so far */
|
||||
int nOnce; /* Number of OP_Once instructions so far */
|
||||
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 */
|
||||
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
|
||||
int nLabel; /* Number of labels used */
|
||||
int *aLabel; /* Space to hold the labels */
|
||||
struct yColCache {
|
||||
int iTable; /* Table cursor number */
|
||||
int iColumn; /* Table column number */
|
||||
i16 iColumn; /* Table column number */
|
||||
u8 tempReg; /* iReg is a temp register that needs to be freed */
|
||||
int iLevel; /* Nesting level */
|
||||
int iReg; /* Reg with value of this column. 0 means none. */
|
||||
int lru; /* Least recently used entry has the smallest value */
|
||||
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
|
||||
ExprList *pConstExpr;/* Constant expressions */
|
||||
Token constraintName;/* Name of the constraint currently being parsed */
|
||||
yDbMask writeMask; /* Start a write transaction on these databases */
|
||||
yDbMask cookieMask; /* Bitmask of schema verified databases */
|
||||
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
|
||||
@@ -2314,7 +2382,6 @@ struct Parse {
|
||||
int regRowid; /* Register holding rowid of CREATE TABLE entry */
|
||||
int regRoot; /* Register holding root page number for new objects */
|
||||
int nMaxArg; /* Max args passed to user function by sub-program */
|
||||
Token constraintName;/* Name of the constraint currently being parsed */
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int nTableLock; /* Number of locks in aTableLock */
|
||||
TableLock *aTableLock; /* Required table locks for shared-cache mode */
|
||||
@@ -2333,12 +2400,17 @@ struct Parse {
|
||||
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
|
||||
u8 disableTriggers; /* True to disable triggers */
|
||||
|
||||
/* Above is constant between recursions. Below is reset before and after
|
||||
** each recursion */
|
||||
/************************************************************************
|
||||
** Above is constant between recursions. Below is reset before and after
|
||||
** each recursion. The boundary between these two regions is determined
|
||||
** using offsetof(Parse,nVar) so the nVar field must be the first field
|
||||
** in the recursive region.
|
||||
************************************************************************/
|
||||
|
||||
int nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
int nzVar; /* Number of available slots in azVar[] */
|
||||
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
|
||||
u8 bFreeWith; /* True if pWith should be freed with parser */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
|
||||
@@ -2364,6 +2436,7 @@ 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 */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2483,7 +2556,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 */
|
||||
@@ -2605,9 +2678,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 */
|
||||
@@ -2631,6 +2704,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.
|
||||
@@ -2898,7 +2986,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*);
|
||||
@@ -3036,7 +3124,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*);
|
||||
@@ -3195,7 +3283,6 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
|
||||
int sqlite3GetToken(const unsigned char *, int *);
|
||||
void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
void sqlite3ExpirePreparedStatements(sqlite3*);
|
||||
int sqlite3CodeSubselect(Parse *, Expr *, int, int);
|
||||
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
|
||||
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
|
||||
int sqlite3ResolveExprNames(NameContext*, Expr*);
|
||||
@@ -3329,6 +3416,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
|
||||
@@ -3383,7 +3478,7 @@ const char *sqlite3JournalModename(int);
|
||||
#define IN_INDEX_EPH 2
|
||||
#define IN_INDEX_INDEX_ASC 3
|
||||
#define IN_INDEX_INDEX_DESC 4
|
||||
int sqlite3FindInIndex(Parse *, Expr *, int*);
|
||||
int sqlite3FindInIndex(Parse *, Expr *, int*, int);
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
|
||||
|
||||
+9
-8
@@ -873,7 +873,7 @@ static int auth_callback(
|
||||
const char *zArg3,
|
||||
const char *zArg4
|
||||
){
|
||||
char *zCode;
|
||||
const char *zCode;
|
||||
Tcl_DString str;
|
||||
int rc;
|
||||
const char *zReply;
|
||||
@@ -914,6 +914,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);
|
||||
@@ -998,7 +999,7 @@ static int DbTransPostCmd(
|
||||
Tcl_Interp *interp, /* Tcl interpreter */
|
||||
int result /* Result of evaluating SCRIPT */
|
||||
){
|
||||
static const char *azEnd[] = {
|
||||
static const char *const azEnd[] = {
|
||||
"RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */
|
||||
"COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */
|
||||
"ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction",
|
||||
@@ -1936,7 +1937,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
Tcl_AppendResult(interp, pDb->zCommit, 0);
|
||||
}
|
||||
}else{
|
||||
char *zCommit;
|
||||
const char *zCommit;
|
||||
int len;
|
||||
if( pDb->zCommit ){
|
||||
Tcl_Free(pDb->zCommit);
|
||||
@@ -2009,14 +2010,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
char *zSql; /* An SQL statement */
|
||||
char *zLine; /* A single line of input from the file */
|
||||
char **azCol; /* zLine[] broken up into columns */
|
||||
char *zCommit; /* How to commit changes */
|
||||
const char *zCommit; /* How to commit changes */
|
||||
FILE *in; /* The input file */
|
||||
int lineno = 0; /* Line number of input file */
|
||||
char zLineNum[80]; /* Line number print buffer */
|
||||
Tcl_Obj *pResult; /* interp result */
|
||||
|
||||
char *zSep;
|
||||
char *zNull;
|
||||
const char *zSep;
|
||||
const char *zNull;
|
||||
if( objc<5 || objc>7 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv,
|
||||
"CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
|
||||
@@ -2934,7 +2935,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
if( objc==2 ){
|
||||
zArg = Tcl_GetStringFromObj(objv[1], 0);
|
||||
if( strcmp(zArg,"-version")==0 ){
|
||||
Tcl_AppendResult(interp,sqlite3_version,0);
|
||||
Tcl_AppendResult(interp,sqlite3_libversion(),0);
|
||||
return TCL_OK;
|
||||
}
|
||||
if( strcmp(zArg,"-has-codec")==0 ){
|
||||
@@ -3016,7 +3017,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
zErrMsg = 0;
|
||||
p = (SqliteDb*)Tcl_Alloc( sizeof(*p) );
|
||||
if( p==0 ){
|
||||
Tcl_SetResult(interp, "malloc failed", TCL_STATIC);
|
||||
Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
+23
-1
@@ -242,7 +242,28 @@ static int test_io_trace(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: clang_sanitize_address
|
||||
**
|
||||
** Returns true if the program was compiled using clang with the
|
||||
** -fsanitize=address switch on the command line. False otherwise.
|
||||
*/
|
||||
static int clang_sanitize_address(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
int res = 0;
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
res = 1;
|
||||
# endif
|
||||
#endif
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_exec_printf DB FORMAT STRING
|
||||
**
|
||||
@@ -6323,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;
|
||||
|
||||
+1
-1
@@ -892,7 +892,7 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
pIdxInfo->estimatedCost = cost;
|
||||
}else if( useIdx ){
|
||||
/* Approximation of log2(nRow). */
|
||||
for( ii=0; ii<(sizeof(int)*8); ii++ ){
|
||||
for( ii=0; ii<(sizeof(int)*8)-1; ii++ ){
|
||||
if( nRow & (1<<ii) ){
|
||||
pIdxInfo->estimatedCost = (double)ii;
|
||||
}
|
||||
|
||||
@@ -225,6 +225,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
|
||||
|
||||
+4
-12
@@ -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);
|
||||
|
||||
+1
-6
@@ -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
|
||||
|
||||
+4
-4
@@ -467,9 +467,10 @@ void sqlite3Update(
|
||||
);
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( oldmask==0xffffffff
|
||||
|| (i<32 && (oldmask & (1<<i)))
|
||||
|| (i<32 && (oldmask & MASKBIT32(i))!=0)
|
||||
|| (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
|
||||
){
|
||||
testcase( oldmask!=0xffffffff && i==31 );
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
|
||||
@@ -504,7 +505,7 @@ void sqlite3Update(
|
||||
j = aXRef[i];
|
||||
if( j>=0 ){
|
||||
sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
|
||||
}else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){
|
||||
}else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){
|
||||
/* This branch loads the value of a column that will not be changed
|
||||
** into a register. This is done if there are no BEFORE triggers, or
|
||||
** if there are one or more BEFORE triggers that use this value via
|
||||
@@ -734,8 +735,7 @@ static void updateVirtualTable(
|
||||
*/
|
||||
assert( v );
|
||||
ephemTab = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenHash, ephemTab, pTab->nCol+1+(pRowid!=0));
|
||||
|
||||
/* fill the ephemeral table
|
||||
*/
|
||||
|
||||
+2
-1
@@ -1001,7 +1001,8 @@ int sqlite3VarintLen(u64 v){
|
||||
** Read or write a four-byte big-endian integer value.
|
||||
*/
|
||||
u32 sqlite3Get4byte(const u8 *p){
|
||||
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
||||
testcase( p[0]&0x80 );
|
||||
return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
||||
}
|
||||
void sqlite3Put4byte(unsigned char *p, u32 v){
|
||||
p[0] = (u8)(v>>24);
|
||||
|
||||
+55
-36
@@ -3295,7 +3295,7 @@ case OP_OpenWrite: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: OpenEphemeral P1 P2 * P4 P5
|
||||
/* Opcode: OpenEphemeral P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
**
|
||||
** Open a new cursor P1 to a transient table.
|
||||
@@ -3307,11 +3307,18 @@ case OP_OpenWrite: {
|
||||
** The cursor points to a BTree table if P4==0 and to a BTree index
|
||||
** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure
|
||||
** that defines the format of keys in the index.
|
||||
*/
|
||||
/* Opcode: OpenHash P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
**
|
||||
** The P5 parameter can be a mask of the BTREE_* flags defined
|
||||
** in btree.h. These flags control aspects of the operation of
|
||||
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
|
||||
** added automatically.
|
||||
** Open a new cursor P1 to a transient table.
|
||||
** P2 is the number of columns in the ephemeral table.
|
||||
** The cursor points to a BTree table if P4==0 and to a BTree index
|
||||
** if P4 is a KeyInfo structure.
|
||||
**
|
||||
** This opcode is identical to OP_OpenEphemeral except that it
|
||||
** adds the BTREE_UNORDERED parameter to the sqlite3BtreeOpen() call,
|
||||
** thus causing the underlying table to unordered.
|
||||
*/
|
||||
/* Opcode: OpenAutoindex P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
@@ -3322,9 +3329,10 @@ case OP_OpenWrite: {
|
||||
** indices in joins.
|
||||
*/
|
||||
case OP_OpenAutoindex:
|
||||
case OP_OpenHash:
|
||||
case OP_OpenEphemeral: {
|
||||
VdbeCursor *pCx;
|
||||
KeyInfo *pKeyInfo;
|
||||
int btreeFlags;
|
||||
|
||||
static const int vfsFlags =
|
||||
SQLITE_OPEN_READWRITE |
|
||||
@@ -3334,38 +3342,35 @@ case OP_OpenEphemeral: {
|
||||
SQLITE_OPEN_TRANSIENT_DB;
|
||||
assert( pOp->p1>=0 );
|
||||
assert( pOp->p2>=0 );
|
||||
assert( pOp->p5==0 );
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt,
|
||||
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
|
||||
btreeFlags = BTREE_OMIT_JOURNAL | BTREE_SINGLE;
|
||||
if( pOp->opcode==OP_OpenHash ){
|
||||
btreeFlags |= BTREE_UNORDERED;
|
||||
pCx->isOrdered = 0;
|
||||
}else{
|
||||
pCx->isOrdered = 1;
|
||||
}
|
||||
if( pOp->p4.pKeyInfo ){
|
||||
assert( pOp->p4type==P4_KEYINFO );
|
||||
assert( pOp->p4.pKeyInfo->db==db );
|
||||
assert( pOp->p4.pKeyInfo->enc==ENC(db) );
|
||||
btreeFlags |= BTREE_SINGLE_INDEX;
|
||||
pCx->isTable = 0;
|
||||
}else{
|
||||
pCx->isTable = 1;
|
||||
}
|
||||
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt, btreeFlags, vfsFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
/* If a transient index is required, create it by calling
|
||||
** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
|
||||
** opening it. If a transient table is required, just use the
|
||||
** automatically created table with root-page 1 (an BLOB_INTKEY table).
|
||||
*/
|
||||
if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
|
||||
int pgno;
|
||||
assert( pOp->p4type==P4_KEYINFO );
|
||||
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pgno==MASTER_ROOT+1 );
|
||||
assert( pKeyInfo->db==db );
|
||||
assert( pKeyInfo->enc==ENC(db) );
|
||||
pCx->pKeyInfo = pKeyInfo;
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, pKeyInfo, pCx->pCursor);
|
||||
}
|
||||
pCx->isTable = 0;
|
||||
}else{
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor);
|
||||
pCx->isTable = 1;
|
||||
}
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, pOp->p4.pKeyInfo,
|
||||
pCx->pCursor);
|
||||
}
|
||||
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
|
||||
pCx->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3599,6 +3604,7 @@ case OP_SeekGt: { /* jump, in3 */
|
||||
#endif
|
||||
if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt );
|
||||
if( res<0 || (res==0 && oc==OP_SeekGt) ){
|
||||
res = 0;
|
||||
rc = sqlite3BtreeNext(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
pC->rowidIsValid = 0;
|
||||
@@ -3608,6 +3614,7 @@ case OP_SeekGt: { /* jump, in3 */
|
||||
}else{
|
||||
assert( oc==OP_SeekLt || oc==OP_SeekLe );
|
||||
if( res>0 || (res==0 && oc==OP_SeekLt) ){
|
||||
res = 0;
|
||||
rc = sqlite3BtreePrevious(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
pC->rowidIsValid = 0;
|
||||
@@ -4366,7 +4373,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);
|
||||
}
|
||||
@@ -4461,7 +4467,7 @@ case OP_Rewind: { /* jump */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Next P1 P2 * * P5
|
||||
/* Opcode: Next P1 P2 P3 * P5
|
||||
**
|
||||
** Advance cursor P1 so that it points to the next key/data pair in its
|
||||
** table or index. If there are no more key/value pairs then fall through
|
||||
@@ -4471,6 +4477,11 @@ case OP_Rewind: { /* jump */
|
||||
** The P1 cursor must be for a real table, not a pseudo-table. P1 must have
|
||||
** been opened prior to this opcode or the program will segfault.
|
||||
**
|
||||
** The P3 value is a hint to the btree implementation. If P3==1, that
|
||||
** means P1 is an SQL index and that this instruction could have been
|
||||
** omitted if that index had been unique. P3 is usually 0. P3 is
|
||||
** always either 0 or 1.
|
||||
**
|
||||
** P4 is always of type P4_ADVANCE. The function pointer points to
|
||||
** sqlite3BtreeNext().
|
||||
**
|
||||
@@ -4479,12 +4490,12 @@ case OP_Rewind: { /* jump */
|
||||
**
|
||||
** See also: Prev, NextIfOpen
|
||||
*/
|
||||
/* Opcode: NextIfOpen P1 P2 * * P5
|
||||
/* Opcode: NextIfOpen P1 P2 P3 * P5
|
||||
**
|
||||
** This opcode works just like OP_Next except that if cursor P1 is not
|
||||
** open it behaves a no-op.
|
||||
*/
|
||||
/* Opcode: Prev P1 P2 * * P5
|
||||
/* Opcode: Prev P1 P2 P3 * P5
|
||||
**
|
||||
** Back up cursor P1 so that it points to the previous key/data pair in its
|
||||
** table or index. If there is no previous key/value pairs then fall through
|
||||
@@ -4494,13 +4505,18 @@ case OP_Rewind: { /* jump */
|
||||
** The P1 cursor must be for a real table, not a pseudo-table. If P1 is
|
||||
** not open then the behavior is undefined.
|
||||
**
|
||||
** The P3 value is a hint to the btree implementation. If P3==1, that
|
||||
** means P1 is an SQL index and that this instruction could have been
|
||||
** omitted if that index had been unique. P3 is usually 0. P3 is
|
||||
** always either 0 or 1.
|
||||
**
|
||||
** P4 is always of type P4_ADVANCE. The function pointer points to
|
||||
** sqlite3BtreePrevious().
|
||||
**
|
||||
** If P5 is positive and the jump is taken, then event counter
|
||||
** number P5-1 in the prepared statement is incremented.
|
||||
*/
|
||||
/* Opcode: PrevIfOpen P1 P2 * * P5
|
||||
/* Opcode: PrevIfOpen P1 P2 P3 * P5
|
||||
**
|
||||
** This opcode works just like OP_Prev except that if cursor P1 is not
|
||||
** open it behaves a no-op.
|
||||
@@ -4522,9 +4538,12 @@ case OP_Next: /* jump */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
assert( pOp->p5<ArraySize(p->aCounter) );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
res = pOp->p3;
|
||||
assert( pC!=0 );
|
||||
assert( pC->deferredMoveto==0 );
|
||||
assert( pC->pCursor );
|
||||
assert( res==0 || (res==1 && pC->isTable==0) );
|
||||
testcase( res==1 );
|
||||
assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
|
||||
assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
|
||||
assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext );
|
||||
@@ -6175,7 +6194,7 @@ case OP_Trace: {
|
||||
if( zTrace ){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( ((1<<i) & p->btreeMask)==0 ) continue;
|
||||
if( MASKBIT(i) & p->btreeMask)==0 ) continue;
|
||||
sqlite3_file_control(db, db->aDb[i].zName, SQLITE_FCNTL_TRACE, zTrace);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-11
@@ -2615,8 +2615,9 @@ void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
|
||||
while( *pp ){
|
||||
AuxData *pAux = *pp;
|
||||
if( (iOp<0)
|
||||
|| (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & ((u32)1<<pAux->iArg))))
|
||||
|| (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg))))
|
||||
){
|
||||
testcase( pAux->iArg==31 );
|
||||
if( pAux->xDelete ){
|
||||
pAux->xDelete(pAux->pAux);
|
||||
}
|
||||
@@ -2931,6 +2932,9 @@ u32 sqlite3VdbeSerialGet(
|
||||
u32 serial_type, /* Serial type to deserialize */
|
||||
Mem *pMem /* Memory cell to write value into */
|
||||
){
|
||||
u64 x;
|
||||
u32 y;
|
||||
int i;
|
||||
switch( serial_type ){
|
||||
case 10: /* Reserved for future use */
|
||||
case 11: /* Reserved for future use */
|
||||
@@ -2944,23 +2948,26 @@ u32 sqlite3VdbeSerialGet(
|
||||
return 1;
|
||||
}
|
||||
case 2: { /* 2-byte signed integer */
|
||||
pMem->u.i = (((signed char)buf[0])<<8) | buf[1];
|
||||
i = 256*(signed char)buf[0] | buf[1];
|
||||
pMem->u.i = (i64)i;
|
||||
pMem->flags = MEM_Int;
|
||||
return 2;
|
||||
}
|
||||
case 3: { /* 3-byte signed integer */
|
||||
pMem->u.i = (((signed char)buf[0])<<16) | (buf[1]<<8) | buf[2];
|
||||
i = 65536*(signed char)buf[0] | (buf[1]<<8) | buf[2];
|
||||
pMem->u.i = (i64)i;
|
||||
pMem->flags = MEM_Int;
|
||||
return 3;
|
||||
}
|
||||
case 4: { /* 4-byte signed integer */
|
||||
pMem->u.i = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
|
||||
y = ((unsigned)buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
|
||||
pMem->u.i = (i64)*(int*)&y;
|
||||
pMem->flags = MEM_Int;
|
||||
return 4;
|
||||
}
|
||||
case 5: { /* 6-byte signed integer */
|
||||
u64 x = (((signed char)buf[0])<<8) | buf[1];
|
||||
u32 y = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5];
|
||||
x = 256*(signed char)buf[0] + buf[1];
|
||||
y = ((unsigned)buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5];
|
||||
x = (x<<32) | y;
|
||||
pMem->u.i = *(i64*)&x;
|
||||
pMem->flags = MEM_Int;
|
||||
@@ -2968,8 +2975,6 @@ u32 sqlite3VdbeSerialGet(
|
||||
}
|
||||
case 6: /* 8-byte signed integer */
|
||||
case 7: { /* IEEE floating point */
|
||||
u64 x;
|
||||
u32 y;
|
||||
#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
||||
/* Verify that integers and floating point values use the same
|
||||
** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
|
||||
@@ -2982,9 +2987,8 @@ u32 sqlite3VdbeSerialGet(
|
||||
swapMixedEndianFloat(t2);
|
||||
assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 );
|
||||
#endif
|
||||
|
||||
x = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
|
||||
y = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7];
|
||||
x = ((unsigned)buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
|
||||
y = ((unsigned)buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7];
|
||||
x = (x<<32) | y;
|
||||
if( serial_type==6 ){
|
||||
pMem->u.i = *(i64*)&x;
|
||||
|
||||
+1
-1
@@ -598,7 +598,7 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
|
||||
/*
|
||||
** Size of struct Mem not including the Mem.zMalloc member.
|
||||
*/
|
||||
#define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc))
|
||||
#define MEMCELLSIZE offsetof(Mem,zMalloc)
|
||||
|
||||
/*
|
||||
** Make an shallow copy of pFrom into pTo. Prior contents of
|
||||
|
||||
+12
-11
@@ -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;
|
||||
}
|
||||
|
||||
+37
-23
@@ -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;
|
||||
@@ -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;
|
||||
@@ -2346,16 +2348,17 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
|
||||
** this routine sets up a loop that will iterate over all values of X.
|
||||
*/
|
||||
static int codeEqualityTerm(
|
||||
Parse *pParse, /* The parsing context */
|
||||
WhereInfo *pWInfo, /* WHERE clause */
|
||||
WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
|
||||
WhereLevel *pLevel, /* The level of the FROM clause we are working on */
|
||||
int iEq, /* Index of the equality term within this level */
|
||||
int bRev, /* True for reverse-order IN operations */
|
||||
int iTarget /* Attempt to leave results in this register */
|
||||
){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int iReg; /* Register holding results */
|
||||
Expr *pX = pTerm->pExpr; /* Expression to be coded */
|
||||
Parse *pParse = pWInfo->pParse; /* Parsing context */
|
||||
Vdbe *v = pParse->pVdbe; /* Prepared stmt under construction */
|
||||
int iReg; /* Register holding results */
|
||||
|
||||
assert( iTarget>0 );
|
||||
if( pX->op==TK_EQ ){
|
||||
@@ -2380,7 +2383,7 @@ static int codeEqualityTerm(
|
||||
}
|
||||
assert( pX->op==TK_IN );
|
||||
iReg = iTarget;
|
||||
eType = sqlite3FindInIndex(pParse, pX, 0);
|
||||
eType = sqlite3FindInIndex(pParse, pX, 0, pWInfo->bOBSat);
|
||||
if( eType==IN_INDEX_INDEX_DESC ){
|
||||
testcase( bRev );
|
||||
bRev = !bRev;
|
||||
@@ -2462,7 +2465,7 @@ static int codeEqualityTerm(
|
||||
** string in this example would be set to SQLITE_AFF_NONE.
|
||||
*/
|
||||
static int codeAllEqualityTerms(
|
||||
Parse *pParse, /* Parsing context */
|
||||
WhereInfo *pWInfo, /* WHERE clause */
|
||||
WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
|
||||
int bRev, /* Reverse the order of IN operators */
|
||||
int nExtraReg, /* Number of extra registers to allocate */
|
||||
@@ -2470,6 +2473,7 @@ static int codeAllEqualityTerms(
|
||||
){
|
||||
u16 nEq; /* The number of == or IN constraints to code */
|
||||
u16 nSkip; /* Number of left-most columns to skip */
|
||||
Parse *pParse = pWInfo->pParse; /* Parsing context */
|
||||
Vdbe *v = pParse->pVdbe; /* The vm under construction */
|
||||
Index *pIdx; /* The index being used for this loop */
|
||||
WhereTerm *pTerm; /* A single constraint term */
|
||||
@@ -2524,7 +2528,7 @@ static int codeAllEqualityTerms(
|
||||
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
|
||||
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
||||
r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j);
|
||||
r1 = codeEqualityTerm(pWInfo, pTerm, pLevel, j, bRev, regBase+j);
|
||||
if( r1!=regBase+j ){
|
||||
if( nReg==1 ){
|
||||
sqlite3ReleaseTempReg(pParse, regBase);
|
||||
@@ -2807,7 +2811,7 @@ static Bitmask codeOneLoopStart(
|
||||
pTerm = pLoop->aLTerm[j];
|
||||
if( pTerm==0 ) continue;
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
|
||||
codeEqualityTerm(pWInfo, pTerm, pLevel, j, bRev, iTarget);
|
||||
addrNotFound = pLevel->addrNxt;
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
|
||||
@@ -2847,7 +2851,7 @@ static Bitmask codeOneLoopStart(
|
||||
assert( pTerm->pExpr!=0 );
|
||||
assert( omitTable==0 );
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
||||
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
|
||||
iRowidReg = codeEqualityTerm(pWInfo, pTerm, pLevel, 0, bRev, iReleaseReg);
|
||||
addrNxt = pLevel->addrNxt;
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
|
||||
@@ -3037,7 +3041,7 @@ static Bitmask codeOneLoopStart(
|
||||
** and store the values of those terms in an array of registers
|
||||
** starting at regBase.
|
||||
*/
|
||||
regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
|
||||
regBase = codeAllEqualityTerms(pWInfo,pLevel,bRev,nExtraReg,&zStartAff);
|
||||
assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
|
||||
if( zStartAff ) cEndAff = zStartAff[nEq];
|
||||
addrNxt = pLevel->addrNxt;
|
||||
@@ -3182,6 +3186,8 @@ static Bitmask codeOneLoopStart(
|
||||
pLevel->op = OP_Next;
|
||||
}
|
||||
pLevel->p1 = iIdxCur;
|
||||
assert( (WHERE_UNQ_WANTED>>16)==1 );
|
||||
pLevel->p3 = (pLoop->wsFlags>>16)&1;
|
||||
if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){
|
||||
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
|
||||
}else{
|
||||
@@ -3408,10 +3414,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
|
||||
@@ -3978,12 +3990,13 @@ static int whereLoopAddBtreeIndex(
|
||||
|| nInMul==0
|
||||
);
|
||||
pNew->wsFlags |= WHERE_COLUMN_EQ;
|
||||
if( iCol<0
|
||||
|| (pProbe->onError!=OE_None && nInMul==0
|
||||
&& pNew->u.btree.nEq==pProbe->nKeyCol-1)
|
||||
){
|
||||
if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1)){
|
||||
assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 );
|
||||
pNew->wsFlags |= WHERE_ONEROW;
|
||||
if( iCol>=0 && pProbe->onError==OE_None ){
|
||||
pNew->wsFlags |= WHERE_UNQ_WANTED;
|
||||
}else{
|
||||
pNew->wsFlags |= WHERE_ONEROW;
|
||||
}
|
||||
}
|
||||
pNew->u.btree.nEq++;
|
||||
pNew->nOut = nRowEst + nInMul;
|
||||
@@ -4196,6 +4209,7 @@ static int whereLoopAddBtree(
|
||||
&& !pSrc->notIndexed
|
||||
&& HasRowid(pTab)
|
||||
&& !pSrc->isCorrelated
|
||||
&& !pSrc->isRecursive
|
||||
){
|
||||
/* Generate auto-index WhereLoops */
|
||||
WhereTerm *pTerm;
|
||||
@@ -5776,7 +5790,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pLoop = pLevel->pWLoop;
|
||||
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
|
||||
if( pLevel->op!=OP_Noop ){
|
||||
sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2);
|
||||
sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
|
||||
sqlite3VdbeChangeP5(v, pLevel->p5);
|
||||
}
|
||||
if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
|
||||
|
||||
+2
-1
@@ -70,7 +70,7 @@ struct WhereLevel {
|
||||
int addrFirst; /* First instruction of interior of the loop */
|
||||
int addrBody; /* Beginning of the body of this loop */
|
||||
u8 iFrom; /* Which entry in the FROM clause */
|
||||
u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */
|
||||
u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
|
||||
int p1, p2; /* Operands of the opcode used to ends the loop */
|
||||
union { /* Information that depends on pWLoop->wsFlags */
|
||||
struct {
|
||||
@@ -457,3 +457,4 @@ struct WhereInfo {
|
||||
#define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */
|
||||
#define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */
|
||||
#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */
|
||||
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
|
||||
|
||||
@@ -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"} {
|
||||
|
||||
+24
-18
@@ -179,16 +179,18 @@ do_test capi3-3.4 {
|
||||
do_test capi3-3.5 {
|
||||
sqlite3_close $db2
|
||||
} {SQLITE_OK}
|
||||
do_test capi3-3.6.1-misuse {
|
||||
sqlite3_close $db2
|
||||
} {SQLITE_MISUSE}
|
||||
do_test capi3-3.6.2-misuse {
|
||||
sqlite3_errmsg $db2
|
||||
} {library routine called out of sequence}
|
||||
ifcapable {utf16} {
|
||||
do_test capi3-3.6.3-misuse {
|
||||
utf8 [sqlite3_errmsg16 $db2]
|
||||
if {[clang_sanitize_address]==0} {
|
||||
do_test capi3-3.6.1-misuse {
|
||||
sqlite3_close $db2
|
||||
} {SQLITE_MISUSE}
|
||||
do_test capi3-3.6.2-misuse {
|
||||
sqlite3_errmsg $db2
|
||||
} {library routine called out of sequence}
|
||||
ifcapable {utf16} {
|
||||
do_test capi3-3.6.3-misuse {
|
||||
utf8 [sqlite3_errmsg16 $db2]
|
||||
} {library routine called out of sequence}
|
||||
}
|
||||
}
|
||||
|
||||
do_test capi3-3.7 {
|
||||
@@ -661,10 +663,12 @@ do_test capi3-6.3 {
|
||||
sqlite3_finalize $STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test capi3-6.4-misuse {
|
||||
db cache flush
|
||||
sqlite3_close $DB
|
||||
} {SQLITE_OK}
|
||||
if {[clang_sanitize_address]==0} {
|
||||
do_test capi3-6.4-misuse {
|
||||
db cache flush
|
||||
sqlite3_close $DB
|
||||
} {SQLITE_OK}
|
||||
}
|
||||
db close
|
||||
|
||||
# This procedure sets the value of the file-format in file 'test.db'
|
||||
@@ -1060,11 +1064,13 @@ if {[llength [info commands sqlite3_sleep]]>0} {
|
||||
}
|
||||
|
||||
# Ticket #1219: Make sure binding APIs can handle a NULL pointer.
|
||||
#
|
||||
do_test capi3-14.1-misuse {
|
||||
set rc [catch {sqlite3_bind_text 0 1 hello 5} msg]
|
||||
lappend rc $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
#
|
||||
if {[clang_sanitize_address]==0} {
|
||||
do_test capi3-14.1-misuse {
|
||||
set rc [catch {sqlite3_bind_text 0 1 hello 5} msg]
|
||||
lappend rc $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
}
|
||||
|
||||
# Ticket #1650: Honor the nBytes parameter to sqlite3_prepare.
|
||||
#
|
||||
|
||||
+21
-15
@@ -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.
|
||||
|
||||
+75
-25
@@ -15,51 +15,68 @@ set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix closure01
|
||||
|
||||
ifcapable !vtab { finish_test ; return }
|
||||
ifcapable !vtab||!cte { finish_test ; return }
|
||||
|
||||
load_static_extension db closure
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y INTEGER);
|
||||
WITH RECURSIVE
|
||||
cnt(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM cnt LIMIT 131072)
|
||||
INSERT INTO t1(x, y) SELECT i, nullif(i,1)/2 FROM cnt;
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
INSERT INTO t1(x) VALUES(1),(2);
|
||||
INSERT INTO t1(x) SELECT x+2 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+4 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+8 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+16 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+32 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+64 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+128 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+256 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+512 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+1024 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+2048 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+4096 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+8192 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+16384 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+32768 FROM t1;
|
||||
INSERT INTO t1(x) SELECT x+65536 FROM t1;
|
||||
UPDATE t1 SET y=x/2 WHERE x>1;
|
||||
COMMIT;
|
||||
CREATE VIRTUAL TABLE cx
|
||||
USING transitive_closure(tablename=t1, idcolumn=x, parentcolumn=y);
|
||||
} {}
|
||||
|
||||
# The entire table
|
||||
do_execsql_test 1.1 {
|
||||
do_timed_execsql_test 1.1 {
|
||||
SELECT count(*), depth FROM cx WHERE root=1 GROUP BY depth ORDER BY 1;
|
||||
} {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/}
|
||||
do_timed_execsql_test 1.1-cte {
|
||||
WITH RECURSIVE
|
||||
below(id,depth) AS (
|
||||
VALUES(1,0)
|
||||
UNION ALL
|
||||
SELECT t1.x, below.depth+1
|
||||
FROM t1 JOIN below on t1.y=below.id
|
||||
)
|
||||
SELECT count(*), depth FROM below GROUP BY depth ORDER BY 1;
|
||||
} {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/}
|
||||
|
||||
# descendents of 32768
|
||||
do_execsql_test 1.2 {
|
||||
do_timed_execsql_test 1.2 {
|
||||
SELECT * FROM cx WHERE root=32768 ORDER BY id;
|
||||
} {32768 0 65536 1 65537 1 131072 2}
|
||||
do_timed_execsql_test 1.2-cte {
|
||||
WITH RECURSIVE
|
||||
below(id,depth) AS (
|
||||
VALUES(32768,0)
|
||||
UNION ALL
|
||||
SELECT t1.x, below.depth+1
|
||||
FROM t1 JOIN below on t1.y=below.id
|
||||
WHERE below.depth<2
|
||||
)
|
||||
SELECT id, depth FROM below ORDER BY id;
|
||||
} {32768 0 65536 1 65537 1 131072 2}
|
||||
|
||||
# descendents of 16384
|
||||
do_execsql_test 1.3 {
|
||||
do_timed_execsql_test 1.3 {
|
||||
SELECT * FROM cx WHERE root=16384 AND depth<=2 ORDER BY id;
|
||||
} {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2}
|
||||
do_timed_execsql_test 1.3-cte {
|
||||
WITH RECURSIVE
|
||||
below(id,depth) AS (
|
||||
VALUES(16384,0)
|
||||
UNION ALL
|
||||
SELECT t1.x, below.depth+1
|
||||
FROM t1 JOIN below on t1.y=below.id
|
||||
WHERE below.depth<2
|
||||
)
|
||||
SELECT id, depth FROM below ORDER BY id;
|
||||
} {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2}
|
||||
|
||||
# children of 16384
|
||||
do_execsql_test 1.4 {
|
||||
@@ -70,19 +87,41 @@ do_execsql_test 1.4 {
|
||||
} {32768 1 {} t1 x y 32769 1 {} t1 x y}
|
||||
|
||||
# great-grandparent of 16384
|
||||
do_execsql_test 1.5 {
|
||||
do_timed_execsql_test 1.5 {
|
||||
SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx
|
||||
WHERE root=16384
|
||||
AND depth=3
|
||||
AND idcolumn='Y'
|
||||
AND parentcolumn='X';
|
||||
} {2048 3 {} t1 Y X}
|
||||
do_timed_execsql_test 1.5-cte {
|
||||
WITH RECURSIVE
|
||||
above(id,depth) AS (
|
||||
VALUES(16384,0)
|
||||
UNION ALL
|
||||
SELECT t1.y, above.depth+1
|
||||
FROM t1 JOIN above ON t1.x=above.id
|
||||
WHERE above.depth<3
|
||||
)
|
||||
SELECT id FROM above WHERE depth=3;
|
||||
} {2048}
|
||||
|
||||
# depth<5
|
||||
do_execsql_test 1.6 {
|
||||
do_timed_execsql_test 1.6 {
|
||||
SELECT count(*), depth FROM cx WHERE root=1 AND depth<5
|
||||
GROUP BY depth ORDER BY 1;
|
||||
} {1 0 2 1 4 2 8 3 16 4}
|
||||
do_timed_execsql_test 1.6-cte {
|
||||
WITH RECURSIVE
|
||||
below(id,depth) AS (
|
||||
VALUES(1,0)
|
||||
UNION ALL
|
||||
SELECT t1.x, below.depth+1
|
||||
FROM t1 JOIN below ON t1.y=below.id
|
||||
WHERE below.depth<4
|
||||
)
|
||||
SELECT count(*), depth FROM below GROUP BY depth ORDER BY 1;
|
||||
} {1 0 2 1 4 2 8 3 16 4}
|
||||
|
||||
# depth<=5
|
||||
do_execsql_test 1.7 {
|
||||
@@ -103,9 +142,20 @@ do_execsql_test 1.9 {
|
||||
} {8 3 16 4 32 5}
|
||||
|
||||
# depth==5 with min() and max()
|
||||
do_execsql_test 1.10 {
|
||||
do_timed_execsql_test 1.10 {
|
||||
SELECT count(*), min(id), max(id) FROM cx WHERE root=1 AND depth=5;
|
||||
} {32 32 63}
|
||||
do_timed_execsql_test 1.10-cte {
|
||||
WITH RECURSIVE
|
||||
below(id,depth) AS (
|
||||
VALUES(1,0)
|
||||
UNION ALL
|
||||
SELECT t1.x, below.depth+1
|
||||
FROM t1 JOIN below ON t1.y=below.id
|
||||
WHERE below.depth<5
|
||||
)
|
||||
SELECT count(*), min(id), max(id) FROM below WHERE depth=5;
|
||||
} {32 32 63}
|
||||
|
||||
# Create a much smaller table t2 with only 32 elements
|
||||
db eval {
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# 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 auto_vacuum=0;
|
||||
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
|
||||
} {}
|
||||
|
||||
|
||||
# The corruption migration caused by the test case below does not
|
||||
# cause corruption to be detected in mmap mode.
|
||||
#
|
||||
# The trick here is that the root page of the tree scanned by the outer
|
||||
# query is also currently on the free-list. So while the first seek on
|
||||
# the table (for a==1) works, by the time the second is attempted The
|
||||
# "INSERT INTO t2..." statements have recycled the root page of t1 and
|
||||
# used it as an index leaf. Normally, BtreeMovetoUnpacked() detects
|
||||
# that the PgHdr object associated with said root page does not match
|
||||
# the cursor (as it is now marked with PgHdr.intKey==0) and returns
|
||||
# SQLITE_CORRUPT.
|
||||
#
|
||||
# However, in mmap mode, the outer query and the inner queries use
|
||||
# different PgHdr objects (same data, but different PgHdr container
|
||||
# objects). And so the corruption is not detected. Instead, the second
|
||||
# seek fails to find anything and only a single row is returned.
|
||||
#
|
||||
set res23 {1 {database disk image is malformed}}
|
||||
if {[permutation]=="mmap"} {
|
||||
set res23 {0 one}
|
||||
}
|
||||
do_test 2.3 {
|
||||
list [catch {
|
||||
set res [list]
|
||||
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;
|
||||
}
|
||||
lappend res $b
|
||||
}
|
||||
set res
|
||||
} msg] $msg
|
||||
} $res23
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
+3
-6
@@ -51,12 +51,9 @@ proc do_temptables_test {tn sql temptables} {
|
||||
set ret ""
|
||||
db eval "EXPLAIN [set sql]" {
|
||||
if {$opcode == "OpenEphemeral" || $opcode == "SorterOpen"} {
|
||||
if {$p5 != "08" && $p5!="00"} { error "p5 = $p5" }
|
||||
if {$p5 == "08"} {
|
||||
lappend ret hash
|
||||
} else {
|
||||
lappend ret btree
|
||||
}
|
||||
lappend ret btree
|
||||
} elseif {$opcode == "OpenHash"} {
|
||||
lappend ret hash
|
||||
}
|
||||
}
|
||||
set ret
|
||||
|
||||
+39
-32
@@ -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
|
||||
|
||||
+51
-43
@@ -333,9 +333,9 @@ do_select_tests e_select-1.1 {
|
||||
6 "SELECT count(*) WHERE 1" {1}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-48114-33255 If there is only a single table in the
|
||||
# join-source following the FROM clause, then the input data used by the
|
||||
# SELECT statement is the contents of the named table.
|
||||
# EVIDENCE-OF: R-45424-07352 If there is only a single table or subquery
|
||||
# in the FROM clause, then the input data used by the SELECT statement
|
||||
# is the contents of the named table.
|
||||
#
|
||||
# The results of the SELECT queries suggest that they are operating on the
|
||||
# contents of the table 'xx'.
|
||||
@@ -357,10 +357,10 @@ do_select_tests e_select-1.2 {
|
||||
3 "SELECT sum(x), sum(y) FROM xx" {-17.89 -16.87}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-23593-12456 If there is more than one table specified
|
||||
# as part of the join-source following the FROM keyword, then the
|
||||
# contents of each named table are joined into a single dataset for the
|
||||
# simple SELECT statement to operate on.
|
||||
# EVIDENCE-OF: R-28355-09804 If there is more than one table or subquery
|
||||
# in FROM clause then the contents of all tables and/or subqueries are
|
||||
# joined into a single dataset for the simple SELECT statement to
|
||||
# operate on.
|
||||
#
|
||||
# There are more detailed tests for subsequent requirements that add
|
||||
# more detail to this idea. We just add a single test that shows that
|
||||
@@ -383,10 +383,10 @@ do_select_tests e_select-1.3 {
|
||||
# of cartesian joins in the SELECT documentation is consistent with SQLite.
|
||||
# In doing so, we test the following three requirements as a side-effect:
|
||||
#
|
||||
# EVIDENCE-OF: R-46122-14930 If the join-op is "CROSS JOIN", "INNER
|
||||
# JOIN", "JOIN" or a comma (",") and there is no ON or USING clause,
|
||||
# then the result of the join is simply the cartesian product of the
|
||||
# left and right-hand datasets.
|
||||
# EVIDENCE-OF: R-49872-03192 If the join-operator is "CROSS JOIN",
|
||||
# "INNER JOIN", "JOIN" or a comma (",") and there is no ON or USING
|
||||
# clause, then the result of the join is simply the cartesian product of
|
||||
# the left and right-hand datasets.
|
||||
#
|
||||
# The tests are built on this assertion. Really, they test that the output
|
||||
# of a CROSS JOIN, JOIN, INNER JOIN or "," join matches the expected result
|
||||
@@ -464,10 +464,10 @@ do_join_test e_select-1.4.2.1 {
|
||||
]
|
||||
# TODO: Come back and add a few more like the above.
|
||||
|
||||
# EVIDENCE-OF: R-20659-43267 In other words, if the left-hand dataset
|
||||
# consists of Nlhs rows of Mlhs columns, and the right-hand dataset of
|
||||
# Nrhs rows of Mrhs columns, then the cartesian product is a dataset of
|
||||
# Nlhs.Nrhs rows, each containing Mlhs+Mrhs columns.
|
||||
# EVIDENCE-OF: R-18439-38548 In other words, if the left-hand dataset
|
||||
# consists of Nleft rows of Mleft columns, and the right-hand dataset of
|
||||
# Nright rows of Mright columns, then the cartesian product is a dataset
|
||||
# of Nleft×Nright rows, each containing Mleft+Mright columns.
|
||||
#
|
||||
# x1, x2 (Nlhs=3, Nrhs=2) (Mlhs=2, Mrhs=3)
|
||||
do_join_test e_select-1.4.3.1 {
|
||||
@@ -513,11 +513,10 @@ do_select_tests e_select-1.4.5 [list \
|
||||
4 { SELECT * FROM t1 AS y INNER JOIN t1 AS x } $t1_cross_t1 \
|
||||
]
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-22775-56496 If there is an ON clause specified, then
|
||||
# the ON expression is evaluated for each row of the cartesian product
|
||||
# as a boolean expression. All rows for which the expression evaluates
|
||||
# to false are excluded from the dataset.
|
||||
# EVIDENCE-OF: R-38465-03616 If there is an ON clause then the ON
|
||||
# expression is evaluated for each row of the cartesian product as a
|
||||
# boolean expression. Only rows for which the expression evaluates to
|
||||
# true are included from the dataset.
|
||||
#
|
||||
foreach {tn select res} [list \
|
||||
1 { SELECT * FROM t1 %JOIN% t2 ON (1) } $t1_cross_t2 \
|
||||
@@ -540,9 +539,9 @@ foreach {tn select res} [list \
|
||||
do_join_test e_select-1.3.$tn $select $res
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-63358-54862 If there is a USING clause specified as
|
||||
# part of the join-constraint, then each of the column names specified
|
||||
# must exist in the datasets to both the left and right of the join-op.
|
||||
# EVIDENCE-OF: R-49933-05137 If there is a USING clause then each of the
|
||||
# column names specified must exist in the datasets to both the left and
|
||||
# right of the join-operator.
|
||||
#
|
||||
do_select_tests e_select-1.4 -error {
|
||||
cannot join using column %s - column not present in both tables
|
||||
@@ -552,10 +551,10 @@ do_select_tests e_select-1.4 -error {
|
||||
3 { SELECT * FROM t3, (SELECT a AS b, b AS c FROM t1) USING (a) } "a"
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-55987-04584 For each pair of namesake columns, the
|
||||
# EVIDENCE-OF: R-22776-52830 For each pair of named columns, the
|
||||
# expression "lhs.X = rhs.X" is evaluated for each row of the cartesian
|
||||
# product as a boolean expression. All rows for which one or more of the
|
||||
# expressions evaluates to false are excluded from the result set.
|
||||
# product as a boolean expression. Only rows for which all such
|
||||
# expressions evaluates to true are included from the result set.
|
||||
#
|
||||
do_select_tests e_select-1.5 {
|
||||
1 { SELECT * FROM t1, t3 USING (a) } {a one 1 b two 2}
|
||||
@@ -566,8 +565,8 @@ do_select_tests e_select-1.5 {
|
||||
# USING clause, the normal rules for handling affinities, collation
|
||||
# sequences and NULL values in comparisons apply.
|
||||
#
|
||||
# EVIDENCE-OF: R-35466-18578 The column from the dataset on the
|
||||
# left-hand side of the join operator is considered to be on the
|
||||
# EVIDENCE-OF: R-38422-04402 The column from the dataset on the
|
||||
# left-hand side of the join-operator is considered to be on the
|
||||
# left-hand side of the comparison operator (=) for the purposes of
|
||||
# collation sequence and affinity precedence.
|
||||
#
|
||||
@@ -622,10 +621,9 @@ foreach {tn select res} {
|
||||
} {
|
||||
do_join_test e_select-1.7.$tn $select $res
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-41434-12448 If the join-op is a "LEFT JOIN" or "LEFT
|
||||
# OUTER JOIN", then after the ON or USING filtering clauses have been
|
||||
# applied, an extra row is added to the output for each row in the
|
||||
# EVIDENCE-OF: R-42531-52874 If the join-operator is a "LEFT JOIN" or
|
||||
# "LEFT OUTER JOIN", then after the ON or USING filtering clauses have
|
||||
# been applied, an extra row is added to the output for each row in the
|
||||
# original left-hand input dataset that corresponds to no rows at all in
|
||||
# the composite dataset (if any).
|
||||
#
|
||||
@@ -660,8 +658,8 @@ do_select_tests e_select-1.9 {
|
||||
2b "SELECT * FROM t7 LEFT JOIN t8 USING (a)" {x ex 24 abc 24 y why 25 {} {}}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-01809-52134 If the NATURAL keyword is added to any of
|
||||
# the join-ops, then an implicit USING clause is added to the
|
||||
# EVIDENCE-OF: R-04932-55942 If the NATURAL keyword is in the
|
||||
# join-operator then an implicit USING clause is added to the
|
||||
# join-constraints. The implicit USING clause contains each of the
|
||||
# column names that appear in both the left and right-hand input
|
||||
# datasets.
|
||||
@@ -734,10 +732,10 @@ do_execsql_test e_select-3.0 {
|
||||
INSERT INTO x2 VALUES(7, 'mistrusted', 'standardized');
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-06999-14330 If a WHERE clause is specified, the WHERE
|
||||
# EVIDENCE-OF: R-60775-64916 If a WHERE clause is specified, the WHERE
|
||||
# expression is evaluated for each row in the input data as a boolean
|
||||
# expression. All rows for which the WHERE clause expression evaluates
|
||||
# to false are excluded from the dataset before continuing.
|
||||
# expression. Only rows for which the WHERE clause expression evaluates
|
||||
# to true are included from the dataset before continuing.
|
||||
#
|
||||
do_execsql_test e_select-3.1.1 { SELECT k FROM x1 WHERE x } {3}
|
||||
do_execsql_test e_select-3.1.2 { SELECT k FROM x1 WHERE y } {3 5 6}
|
||||
@@ -815,8 +813,8 @@ do_select_tests e_select-4.1 {
|
||||
}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-61869-22578 It is an error to use a "*" or "alias.*"
|
||||
# expression in any context other than than a result expression list.
|
||||
# EVIDENCE-OF: R-38023-18396 It is an error to use a "*" or "alias.*"
|
||||
# expression in any context other than a result expression list.
|
||||
#
|
||||
# EVIDENCE-OF: R-44324-41166 It is also an error to use a "*" or
|
||||
# "alias.*" expression in a simple SELECT query that does not have a
|
||||
@@ -1009,12 +1007,12 @@ do_execsql_test e_select-4.9.0 {
|
||||
INSERT INTO b3 VALUES('dEF', 'dEF');
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-57754-57109 If the SELECT statement is an aggregate
|
||||
# EVIDENCE-OF: R-07284-35990 If the SELECT statement is an aggregate
|
||||
# query with a GROUP BY clause, then each of the expressions specified
|
||||
# as part of the GROUP BY clause is evaluated for each row of the
|
||||
# dataset. Each row is then assigned to a "group" based on the results;
|
||||
# rows for which the results of evaluating the GROUP BY expressions are
|
||||
# the same are assigned to the same group.
|
||||
# the same get assigned to the same group.
|
||||
#
|
||||
# These tests also show that the following is not untrue:
|
||||
#
|
||||
@@ -1373,8 +1371,9 @@ foreach {tn select op1 op2} {
|
||||
do_catchsql_test e_select-7.2.$tn $select [list 1 $err]
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-22874-32655 ORDER BY and LIMIT clauses may only occur
|
||||
# at the end of the entire compound SELECT.
|
||||
# EVIDENCE-OF: R-45440-25633 ORDER BY and LIMIT clauses may only occur
|
||||
# at the end of the entire compound SELECT, and then only if the final
|
||||
# element of the compound is not a VALUES clause.
|
||||
#
|
||||
foreach {tn select} {
|
||||
1 "SELECT * FROM j1 UNION ALL SELECT * FROM j2,j3 ORDER BY a"
|
||||
@@ -1386,6 +1385,7 @@ foreach {tn select} {
|
||||
|
||||
7 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 ORDER BY a"
|
||||
8 "SELECT count(*) FROM j1 UNION SELECT max(e) FROM j2 ORDER BY 1"
|
||||
8b "VALUES('8b') UNION SELECT max(e) FROM j2 ORDER BY 1"
|
||||
9 "SELECT count(*), * FROM j1 UNION SELECT *,* FROM j2 ORDER BY 1,2,3"
|
||||
10 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 LIMIT 10"
|
||||
11 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 LIMIT 10 OFFSET 5"
|
||||
@@ -1407,6 +1407,14 @@ foreach {tn select} {
|
||||
} {
|
||||
do_test e_select-7.3.$tn { catch {execsql $select} msg } 0
|
||||
}
|
||||
foreach {tn select} {
|
||||
50 "SELECT * FROM j1 ORDER BY 1 UNION ALL SELECT * FROM j2,j3"
|
||||
51 "SELECT * FROM j1 LIMIT 1 UNION ALL SELECT * FROM j2,j3"
|
||||
52 "SELECT count(*) FROM j1 UNION ALL VALUES(11) ORDER BY 1"
|
||||
53 "SELECT count(*) FROM j1 UNION ALL VALUES(11) LIMIT 1"
|
||||
} {
|
||||
do_test e_select-7.3.$tn { catch {execsql $select} msg } 1
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-08531-36543 A compound SELECT created using UNION ALL
|
||||
# operator returns all the rows from the SELECT to the left of the UNION
|
||||
|
||||
+15
-15
@@ -344,10 +344,10 @@ foreach {tn indexes} {
|
||||
catchsql { DROP INDEX i3 }
|
||||
execsql $indexes
|
||||
|
||||
# EVIDENCE-OF: R-46122-14930 If the join-op is "CROSS JOIN", "INNER
|
||||
# JOIN", "JOIN" or a comma (",") and there is no ON or USING clause,
|
||||
# then the result of the join is simply the cartesian product of the
|
||||
# left and right-hand datasets.
|
||||
# EVIDENCE-OF: R-49872-03192 If the join-operator is "CROSS JOIN",
|
||||
# "INNER JOIN", "JOIN" or a comma (",") and there is no ON or USING
|
||||
# clause, then the result of the join is simply the cartesian product of
|
||||
# the left and right-hand datasets.
|
||||
#
|
||||
# EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER
|
||||
# JOIN", "JOIN" and "," join operators.
|
||||
@@ -368,10 +368,10 @@ foreach {tn indexes} {
|
||||
test_join $tn.1.11 "t2 CROSS JOIN t2 AS x" {t2 t2}
|
||||
test_join $tn.1.12 "t2 JOIN t2 AS x" {t2 t2}
|
||||
|
||||
# EVIDENCE-OF: R-22775-56496 If there is an ON clause specified, then
|
||||
# the ON expression is evaluated for each row of the cartesian product
|
||||
# as a boolean expression. All rows for which the expression evaluates
|
||||
# to false are excluded from the dataset.
|
||||
# EVIDENCE-OF: R-38465-03616 If there is an ON clause then the ON
|
||||
# expression is evaluated for each row of the cartesian product as a
|
||||
# boolean expression. Only rows for which the expression evaluates to
|
||||
# true are included from the dataset.
|
||||
#
|
||||
test_join $tn.2.1 "t1, t2 ON (t1.a=t2.a)" {t1 t2 -on {te_equals a a}}
|
||||
test_join $tn.2.2 "t2, t1 ON (t1.a=t2.a)" {t2 t1 -on {te_equals a a}}
|
||||
@@ -504,14 +504,14 @@ do_execsql_test e_select-2.2.0 {
|
||||
INSERT INTO t5 VALUES(2, 'two');
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-55824-40976 A sub-select specified in the join-source
|
||||
# following the FROM clause in a simple SELECT statement is handled as
|
||||
# if it was a table containing the data returned by executing the
|
||||
# sub-select statement.
|
||||
# EVIDENCE-OF: R-59237-46742 A subquery specified in the
|
||||
# table-or-subquery following the FROM clause in a simple SELECT
|
||||
# statement is handled as if it was a table containing the data returned
|
||||
# by executing the subquery statement.
|
||||
#
|
||||
# EVIDENCE-OF: R-42612-06757 Each column of the sub-select dataset
|
||||
# inherits the collation sequence and affinity of the corresponding
|
||||
# expression in the sub-select statement.
|
||||
# EVIDENCE-OF: R-27438-53558 Each column of the subquery has the
|
||||
# collation sequence and affinity of the corresponding expression in the
|
||||
# subquery statement.
|
||||
#
|
||||
foreach {tn subselect select spec} {
|
||||
1 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss%"
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ ifcapable !subquery {
|
||||
proc nEphemeral {sql} {
|
||||
set nEph 0
|
||||
foreach op [execsql "EXPLAIN $sql"] {
|
||||
if {$op eq "OpenEphemeral"} {incr nEph}
|
||||
if {$op eq "OpenEphemeral" || $op eq "OpenHash"} {incr nEph}
|
||||
}
|
||||
set nEph
|
||||
}
|
||||
|
||||
+3
-3
@@ -65,17 +65,17 @@ do_test in5-2.4 {
|
||||
}
|
||||
} {12a 56e}
|
||||
do_test in5-2.5.1 {
|
||||
regexp {OpenEphemeral} [db eval {
|
||||
regexp {Open(Ephemeral|Hash)} [db eval {
|
||||
EXPLAIN SELECT d FROM t2 WHERE a IN t3x AND b IN t1y AND c IN t1z
|
||||
}]
|
||||
} {1}
|
||||
do_test in5-2.5.2 {
|
||||
regexp {OpenEphemeral} [db eval {
|
||||
regexp {Open(Ephemeral|Hash)} [db eval {
|
||||
EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t3y AND c IN t1z
|
||||
}]
|
||||
} {1}
|
||||
do_test in5-2.5.3 {
|
||||
regexp {OpenEphemeral} [db eval {
|
||||
regexp {Open(Ephemeral|Hash)} [db eval {
|
||||
EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t3z
|
||||
}]
|
||||
} {1}
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-3
@@ -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
|
||||
|
||||
+32
-29
@@ -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
|
||||
|
||||
+6
-1
@@ -9,7 +9,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests for the SQLITE_IOERR_NODB error condition: the database file file
|
||||
# Tests for the SQLITE_READONLY_DBMOVED error condition: the database file
|
||||
# is unlinked or renamed out from under SQLite.
|
||||
#
|
||||
|
||||
@@ -18,6 +18,11 @@ if {$tcl_platform(platform)!="unix"} return
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
if {[permutation]=="inmemory_journal"} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Create a database file for testing
|
||||
#
|
||||
do_execsql_test pager4-1.1 {
|
||||
|
||||
@@ -1544,5 +1544,6 @@ do_faultsim_test pagerfault-36 -prep {
|
||||
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 0
|
||||
sqlite3_initialize
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -642,7 +642,7 @@ test_suite "inmemory_journal" -description {
|
||||
ioerr.test ioerr2.test ioerr3.test ioerr4.test ioerr5.test
|
||||
vacuum3.test incrblob_err.test diskfull.test backup_ioerr.test
|
||||
e_fts3.test fts3cov.test fts3malloc.test fts3rnd.test
|
||||
fts3snippet.test
|
||||
fts3snippet.test mmapfault.test
|
||||
|
||||
# Exclude test scripts that use tcl IO to access journal files or count
|
||||
# the number of fsync() calls.
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ do_execsql_test printf2-1.11 {
|
||||
SELECT printf('%lld%n',314159.2653,'hi');
|
||||
} {314159}
|
||||
|
||||
# EVIDENCE-OF: R-20555-31089 The %z format is interchangable with %s.
|
||||
# EVIDENCE-OF: R-17002-27534 The %z format is interchangeable with %s.
|
||||
#
|
||||
do_execsql_test printf2-1.12 {
|
||||
SELECT printf('%.*z',5,'abcdefghijklmnop');
|
||||
|
||||
+16
-14
@@ -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,53 @@ 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}
|
||||
|
||||
ifcapable trace {
|
||||
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
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
# do_test TESTNAME SCRIPT EXPECTED
|
||||
# do_execsql_test TESTNAME SQL EXPECTED
|
||||
# do_catchsql_test TESTNAME SQL EXPECTED
|
||||
# do_timed_execsql_test TESTNAME SQL EXPECTED
|
||||
#
|
||||
# Commands providing a lower level interface to the global test counters:
|
||||
#
|
||||
@@ -717,6 +718,11 @@ proc do_catchsql_test {testname sql result} {
|
||||
fix_testname testname
|
||||
uplevel do_test [list $testname] [list "catchsql {$sql}"] [list $result]
|
||||
}
|
||||
proc do_timed_execsql_test {testname sql {result {}}} {
|
||||
fix_testname testname
|
||||
uplevel do_test [list $testname] [list "execsql_timed {$sql}"]\
|
||||
[list [list {*}$result]]
|
||||
}
|
||||
proc do_eqp_test {name sql res} {
|
||||
uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
|
||||
}
|
||||
@@ -1013,6 +1019,14 @@ proc execsql {sql {db db}} {
|
||||
# puts "SQL = $sql"
|
||||
uplevel [list $db eval $sql]
|
||||
}
|
||||
proc execsql_timed {sql {db db}} {
|
||||
set tm [time {
|
||||
set x [uplevel [list $db eval $sql]]
|
||||
} 1]
|
||||
set tm [lindex $tm 0]
|
||||
puts -nonewline " ([expr {$tm*0.001}]ms) "
|
||||
set x
|
||||
}
|
||||
|
||||
# Execute SQL and catch exceptions.
|
||||
#
|
||||
|
||||
@@ -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
|
||||
|
||||
+820
@@ -0,0 +1,820 @@
|
||||
# 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}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Some tests that use LIMIT and OFFSET in the definition of recursive CTEs.
|
||||
#
|
||||
set I [list 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
|
||||
proc limit_test {tn iLimit iOffset} {
|
||||
if {$iOffset < 0} { set iOffset 0 }
|
||||
if {$iLimit < 0 } {
|
||||
set result [lrange $::I $iOffset end]
|
||||
} else {
|
||||
set result [lrange $::I $iOffset [expr $iLimit+$iOffset-1]]
|
||||
}
|
||||
uplevel [list do_execsql_test $tn [subst -nocommands {
|
||||
WITH ii(a) AS (
|
||||
VALUES(1)
|
||||
UNION ALL
|
||||
SELECT a+1 FROM ii WHERE a<20
|
||||
LIMIT $iLimit OFFSET $iOffset
|
||||
)
|
||||
SELECT * FROM ii
|
||||
}] $result]
|
||||
}
|
||||
|
||||
limit_test 9.1 20 0
|
||||
limit_test 9.2 0 0
|
||||
limit_test 9.3 19 1
|
||||
limit_test 9.4 20 -1
|
||||
limit_test 9.5 5 5
|
||||
limit_test 9.6 0 -1
|
||||
limit_test 9.7 40 -1
|
||||
limit_test 9.8 -1 -1
|
||||
limit_test 9.9 -1 -1
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test the ORDER BY clause on recursive tables.
|
||||
#
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
DROP TABLE IF EXISTS tree;
|
||||
CREATE TABLE tree(id INTEGER PRIMARY KEY, parentid, payload);
|
||||
}
|
||||
|
||||
proc insert_into_tree {L} {
|
||||
db eval { DELETE FROM tree }
|
||||
foreach key $L {
|
||||
unset -nocomplain parentid
|
||||
foreach seg [split $key /] {
|
||||
if {$seg==""} continue
|
||||
set id [db one {
|
||||
SELECT id FROM tree WHERE parentid IS $parentid AND payload=$seg
|
||||
}]
|
||||
if {$id==""} {
|
||||
db eval { INSERT INTO tree VALUES(NULL, $parentid, $seg) }
|
||||
set parentid [db last_insert_rowid]
|
||||
} else {
|
||||
set parentid $id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insert_into_tree {
|
||||
/a/a/a
|
||||
/a/b/c
|
||||
/a/b/c/d
|
||||
/a/b/d
|
||||
}
|
||||
do_execsql_test 10.2 {
|
||||
WITH flat(fid, p) AS (
|
||||
SELECT id, '/' || payload FROM tree WHERE parentid IS NULL
|
||||
UNION ALL
|
||||
SELECT id, p || '/' || payload FROM flat, tree WHERE parentid=fid
|
||||
)
|
||||
SELECT p FROM flat ORDER BY p;
|
||||
} {
|
||||
/a /a/a /a/a/a
|
||||
/a/b /a/b/c /a/b/c/d
|
||||
/a/b/d
|
||||
}
|
||||
|
||||
# Scan the tree-structure currently stored in table tree. Return a list
|
||||
# of nodes visited.
|
||||
#
|
||||
proc scan_tree {bDepthFirst bReverse} {
|
||||
|
||||
set order "ORDER BY "
|
||||
if {$bDepthFirst==0} { append order "2 ASC," }
|
||||
if {$bReverse==0} {
|
||||
append order " 3 ASC"
|
||||
} else {
|
||||
append order " 3 DESC"
|
||||
}
|
||||
|
||||
db eval "
|
||||
WITH flat(fid, depth, p) AS (
|
||||
SELECT id, 1, '/' || payload FROM tree WHERE parentid IS NULL
|
||||
UNION ALL
|
||||
SELECT id, depth+1, p||'/'||payload FROM flat, tree WHERE parentid=fid
|
||||
$order
|
||||
)
|
||||
SELECT p FROM flat;
|
||||
"
|
||||
}
|
||||
|
||||
insert_into_tree {
|
||||
/a/b
|
||||
/a/b/c
|
||||
/a/d
|
||||
/a/d/e
|
||||
/a/d/f
|
||||
/g/h
|
||||
}
|
||||
|
||||
# Breadth first, siblings in ascending order.
|
||||
#
|
||||
do_test 10.3 {
|
||||
scan_tree 0 0
|
||||
} [list {*}{
|
||||
/a /g
|
||||
/a/b /a/d /g/h
|
||||
/a/b/c /a/d/e /a/d/f
|
||||
}]
|
||||
|
||||
# Depth first, siblings in ascending order.
|
||||
#
|
||||
do_test 10.4 {
|
||||
scan_tree 1 0
|
||||
} [list {*}{
|
||||
/a /a/b /a/b/c
|
||||
/a/d /a/d/e
|
||||
/a/d/f
|
||||
/g /g/h
|
||||
}]
|
||||
|
||||
# Breadth first, siblings in descending order.
|
||||
#
|
||||
do_test 10.5 {
|
||||
scan_tree 0 1
|
||||
} [list {*}{
|
||||
/g /a
|
||||
/g/h /a/d /a/b
|
||||
/a/d/f /a/d/e /a/b/c
|
||||
}]
|
||||
|
||||
# Depth first, siblings in ascending order.
|
||||
#
|
||||
do_test 10.6 {
|
||||
scan_tree 1 1
|
||||
} [list {*}{
|
||||
/g /g/h
|
||||
/a /a/d /a/d/f
|
||||
/a/d/e
|
||||
/a/b /a/b/c
|
||||
}]
|
||||
|
||||
|
||||
# Test name resolution in ORDER BY clauses.
|
||||
#
|
||||
do_catchsql_test 10.7.1 {
|
||||
WITH t(a) AS (
|
||||
SELECT 1 AS b UNION ALL SELECT a+1 AS c FROM t WHERE a<5 ORDER BY a
|
||||
)
|
||||
SELECT * FROM t
|
||||
} {1 {1st ORDER BY term does not match any column in the result set}}
|
||||
do_execsql_test 10.7.2 {
|
||||
WITH t(a) AS (
|
||||
SELECT 1 AS b UNION ALL SELECT a+1 AS c FROM t WHERE a<5 ORDER BY b
|
||||
)
|
||||
SELECT * FROM t
|
||||
} {1 2 3 4 5}
|
||||
do_execsql_test 10.7.3 {
|
||||
WITH t(a) AS (
|
||||
SELECT 1 AS b UNION ALL SELECT a+1 AS c FROM t WHERE a<5 ORDER BY c
|
||||
)
|
||||
SELECT * FROM t
|
||||
} {1 2 3 4 5}
|
||||
|
||||
# Test COLLATE clauses attached to ORDER BY.
|
||||
#
|
||||
insert_into_tree {
|
||||
/a/b
|
||||
/a/C
|
||||
/a/d
|
||||
/B/e
|
||||
/B/F
|
||||
/B/g
|
||||
/c/h
|
||||
/c/I
|
||||
/c/j
|
||||
}
|
||||
|
||||
do_execsql_test 10.8.1 {
|
||||
WITH flat(fid, depth, p) AS (
|
||||
SELECT id, 1, '/' || payload FROM tree WHERE parentid IS NULL
|
||||
UNION ALL
|
||||
SELECT id, depth+1, p||'/'||payload FROM flat, tree WHERE parentid=fid
|
||||
ORDER BY 2, 3 COLLATE nocase
|
||||
)
|
||||
SELECT p FROM flat;
|
||||
} {
|
||||
/a /B /c
|
||||
/a/b /a/C /a/d /B/e /B/F /B/g /c/h /c/I /c/j
|
||||
}
|
||||
do_execsql_test 10.8.2 {
|
||||
WITH flat(fid, depth, p) AS (
|
||||
SELECT id, 1, ('/' || payload) COLLATE nocase
|
||||
FROM tree WHERE parentid IS NULL
|
||||
UNION ALL
|
||||
SELECT id, depth+1, (p||'/'||payload)
|
||||
FROM flat, tree WHERE parentid=fid
|
||||
ORDER BY 2, 3
|
||||
)
|
||||
SELECT p FROM flat;
|
||||
} {
|
||||
/a /B /c
|
||||
/a/b /a/C /a/d /B/e /B/F /B/g /c/h /c/I /c/j
|
||||
}
|
||||
|
||||
do_execsql_test 10.8.3 {
|
||||
WITH flat(fid, depth, p) AS (
|
||||
SELECT id, 1, ('/' || payload)
|
||||
FROM tree WHERE parentid IS NULL
|
||||
UNION ALL
|
||||
SELECT id, depth+1, (p||'/'||payload) COLLATE nocase
|
||||
FROM flat, tree WHERE parentid=fid
|
||||
ORDER BY 2, 3
|
||||
)
|
||||
SELECT p FROM flat;
|
||||
} {
|
||||
/a /B /c
|
||||
/a/b /a/C /a/d /B/e /B/F /B/g /c/h /c/I /c/j
|
||||
}
|
||||
|
||||
do_execsql_test 10.8.4.1 {
|
||||
CREATE TABLE tst(a,b);
|
||||
INSERT INTO tst VALUES('a', 'A');
|
||||
INSERT INTO tst VALUES('b', 'B');
|
||||
INSERT INTO tst VALUES('c', 'C');
|
||||
SELECT a COLLATE nocase FROM tst UNION ALL SELECT b FROM tst ORDER BY 1;
|
||||
} {a A b B c C}
|
||||
do_execsql_test 10.8.4.2 {
|
||||
SELECT a FROM tst UNION ALL SELECT b COLLATE nocase FROM tst ORDER BY 1;
|
||||
} {A B C a b c}
|
||||
do_execsql_test 10.8.4.3 {
|
||||
SELECT a||'' FROM tst UNION ALL SELECT b COLLATE nocase FROM tst ORDER BY 1;
|
||||
} {a A b B c C}
|
||||
|
||||
# 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 11.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 11.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 11.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
|
||||
|
||||
+391
@@ -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
|
||||
|
||||
|
||||
|
||||
+199
-55
@@ -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,10 +138,10 @@ struct Keyword {
|
||||
#else
|
||||
# define AUTOVACUUM 0x00020000
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_CONNECTBY
|
||||
# define CONNECTBY 0
|
||||
#ifdef SQLITE_OMIT_CTE
|
||||
# define CTE 0
|
||||
#else
|
||||
# define CONNECTBY 0x00040000
|
||||
# define CTE 0x00040000
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -172,7 +172,6 @@ static Keyword aKeywordTable[] = {
|
||||
{ "COLUMN", "TK_COLUMNKW", ALTER },
|
||||
{ "COMMIT", "TK_COMMIT", ALWAYS },
|
||||
{ "CONFLICT", "TK_CONFLICT", CONFLICT },
|
||||
{ "CONNECT", "TK_CONNECT", CONNECTBY },
|
||||
{ "CONSTRAINT", "TK_CONSTRAINT", ALWAYS },
|
||||
{ "CREATE", "TK_CREATE", ALWAYS },
|
||||
{ "CROSS", "TK_JOIN_KW", ALWAYS },
|
||||
@@ -238,9 +237,9 @@ static Keyword aKeywordTable[] = {
|
||||
{ "PLAN", "TK_PLAN", EXPLAIN },
|
||||
{ "PRAGMA", "TK_PRAGMA", PRAGMA },
|
||||
{ "PRIMARY", "TK_PRIMARY", ALWAYS },
|
||||
{ "PRIOR", "TK_PRIOR", CONNECTBY },
|
||||
{ "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 },
|
||||
@@ -254,7 +253,6 @@ static Keyword aKeywordTable[] = {
|
||||
{ "SAVEPOINT", "TK_SAVEPOINT", ALWAYS },
|
||||
{ "SELECT", "TK_SELECT", ALWAYS },
|
||||
{ "SET", "TK_SET", ALWAYS },
|
||||
{ "START", "TK_START", CONNECTBY },
|
||||
{ "TABLE", "TK_TABLE", ALWAYS },
|
||||
{ "TEMP", "TK_TEMP", ALWAYS },
|
||||
{ "TEMPORARY", "TK_TEMP", ALWAYS },
|
||||
@@ -270,7 +268,7 @@ static Keyword aKeywordTable[] = {
|
||||
{ "VALUES", "TK_VALUES", ALWAYS },
|
||||
{ "VIEW", "TK_VIEW", VIEW },
|
||||
{ "VIRTUAL", "TK_VIRTUAL", VTAB },
|
||||
{ "WITH", "TK_WITH", CONNECTBY },
|
||||
{ "WITH", "TK_WITH", CTE },
|
||||
{ "WITHOUT", "TK_WITHOUT", ALWAYS },
|
||||
{ "WHEN", "TK_WHEN", ALWAYS },
|
||||
{ "WHERE", "TK_WHERE", ALWAYS },
|
||||
|
||||
@@ -190,6 +190,7 @@ proc main {argv} {
|
||||
SQLITE_OMIT_COMPILEOPTION_DIAGS \
|
||||
SQLITE_OMIT_COMPLETE \
|
||||
SQLITE_OMIT_COMPOUND_SELECT \
|
||||
SQLITE_OMIT_CTE \
|
||||
SQLITE_OMIT_DATETIME_FUNCS \
|
||||
SQLITE_OMIT_DECLTYPE \
|
||||
SQLITE_OMIT_DEPRECATED \
|
||||
|
||||
+5
-6
@@ -119,7 +119,7 @@ static unsigned char *print_byte_range(
|
||||
/*
|
||||
** Print an entire page of content as hex
|
||||
*/
|
||||
static print_page(int iPg){
|
||||
static void print_page(int iPg){
|
||||
int iStart;
|
||||
unsigned char *aData;
|
||||
iStart = (iPg-1)*pagesize;
|
||||
@@ -131,7 +131,7 @@ static print_page(int iPg){
|
||||
|
||||
/* Print a line of decode output showing a 4-byte integer.
|
||||
*/
|
||||
static print_decode_line(
|
||||
static void print_decode_line(
|
||||
unsigned char *aData, /* Content being decoded */
|
||||
int ofst, int nByte, /* Start and size of decode */
|
||||
const char *zMsg /* Message to append */
|
||||
@@ -428,7 +428,7 @@ static void decode_trunk_page(
|
||||
int detail, /* Show leaf pages if true */
|
||||
int recursive /* Follow the trunk change if true */
|
||||
){
|
||||
int n, i, k;
|
||||
int n, i;
|
||||
unsigned char *a;
|
||||
while( pgno>0 ){
|
||||
a = getContent((pgno-1)*pagesize, pagesize);
|
||||
@@ -495,7 +495,6 @@ static void page_usage_cell(
|
||||
int cellno /* Index of the cell on the page */
|
||||
){
|
||||
int i;
|
||||
int nDesc = 0;
|
||||
int n = 0;
|
||||
i64 nPayload;
|
||||
i64 rowid;
|
||||
@@ -677,7 +676,7 @@ static void page_usage_report(const char *zDbName){
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
int pgno = sqlite3_column_int(pStmt, 2);
|
||||
page_usage_btree(pgno, 0, 0, sqlite3_column_text(pStmt, 1));
|
||||
page_usage_btree(pgno, 0, 0, (const char*)sqlite3_column_text(pStmt,1));
|
||||
}
|
||||
}else{
|
||||
printf("ERROR: cannot query database: %s\n", sqlite3_errmsg(db));
|
||||
@@ -835,7 +834,6 @@ int main(int argc, char **argv){
|
||||
free(a);
|
||||
continue;
|
||||
}else if( zLeft && zLeft[0]=='t' ){
|
||||
unsigned char *a;
|
||||
int detail = 0;
|
||||
int recursive = 0;
|
||||
int i;
|
||||
@@ -861,4 +859,5 @@ int main(int argc, char **argv){
|
||||
}
|
||||
}
|
||||
close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user