aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in5
-rw-r--r--Makefile.msc4
-rw-r--r--ext/fts3/fts3.c16
-rw-r--r--ext/fts3/fts3Int.h1
-rw-r--r--ext/fts3/fts3_write.c13
-rw-r--r--ext/fts5/fts5_index.c2
-rw-r--r--ext/misc/json1.c29
-rw-r--r--main.mk5
-rw-r--r--manifest79
-rw-r--r--manifest.uuid2
-rw-r--r--src/btree.c4
-rw-r--r--src/build.c13
-rw-r--r--src/delete.c12
-rw-r--r--src/expr.c12
-rw-r--r--src/fkey.c11
-rw-r--r--src/insert.c14
-rw-r--r--src/main.c7
-rw-r--r--src/mutex_unix.c4
-rw-r--r--src/mutex_w32.c4
-rw-r--r--src/pragma.c4
-rw-r--r--src/select.c48
-rw-r--r--src/shell.c57
-rw-r--r--src/sqlite.h.in29
-rw-r--r--src/sqliteInt.h8
-rw-r--r--src/threads.c13
-rw-r--r--src/update.c185
-rw-r--r--src/vdbeblob.c2
-rw-r--r--src/vtab.c2
-rw-r--r--src/where.c74
-rw-r--r--src/wherecode.c8
-rw-r--r--test/delete4.test43
-rw-r--r--test/fkey1.test34
-rw-r--r--test/fts3conf.test44
-rw-r--r--test/fts4onepass.test147
-rw-r--r--test/fuzzdata3.dbbin11842560 -> 11848704 bytes
-rw-r--r--test/indexexpr1.test55
-rw-r--r--test/view.test15
37 files changed, 771 insertions, 234 deletions
diff --git a/Makefile.in b/Makefile.in
index a10705e7f..d34ee84fb 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -551,7 +551,7 @@ FUZZDATA = \
# Extra arguments for including json1 in the build of tools
#
JSON1_DEP = $(TOP)/ext/misc/json1.c sqlite3ext.h
-JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE
+JSON1_OPT = -DSQLITE_SHELL_JSON1 -DSQLITE_CORE
JSON1_SRC = $(TOP)/ext/misc/json1.c
# Standard options to testfixture
@@ -633,6 +633,9 @@ sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
cp tsrc/shell.c tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
+sqlite3ext.h: .target_source
+ cp tsrc/sqlite3ext.h .
+
tclsqlite3.c: sqlite3.c
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
cat sqlite3.c >>tclsqlite3.c
diff --git a/Makefile.msc b/Makefile.msc
index 7f4ef98ab..b818ff160 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -390,9 +390,9 @@ CORE_LINK_OPTS = /DEF:sqlite3.def
#
!IFNDEF SHELL_COMPILE_OPTS
!IF $(DYNAMIC_SHELL)!=0
-SHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 $(SHELL_CCONV_OPTS) -DSQLITE_API=__declspec(dllimport)
+SHELL_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 $(SHELL_CCONV_OPTS) -DSQLITE_API=__declspec(dllimport)
!ELSE
-SHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 $(SHELL_CCONV_OPTS)
+SHELL_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 $(SHELL_CCONV_OPTS)
!ENDIF
!ENDIF
diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c
index 6a9b507fc..748faefec 100644
--- a/ext/fts3/fts3.c
+++ b/ext/fts3/fts3.c
@@ -1517,6 +1517,19 @@ static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
#endif
}
+/*
+** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support index-info flags. In that case this function is a no-op.
+*/
+static void fts3SetUniqueFlag(sqlite3_index_info *pIdxInfo){
+#if SQLITE_VERSION_NUMBER>=3008012
+ if( sqlite3_libversion_number()>=3008012 ){
+ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
+ }
+#endif
+}
+
/*
** Implementation of the xBestIndex method for FTS3 tables. There
** are three possible strategies, in order of preference:
@@ -1607,6 +1620,9 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
}
+ /* If using a docid=? or rowid=? strategy, set the UNIQUE flag. */
+ if( pInfo->idxNum==FTS3_DOCID_SEARCH ) fts3SetUniqueFlag(pInfo);
+
iIdx = 1;
if( iCons>=0 ){
pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h
index 981c37dee..06bcc7202 100644
--- a/ext/fts3/fts3Int.h
+++ b/ext/fts3/fts3Int.h
@@ -264,6 +264,7 @@ struct Fts3Table {
int nPendingData; /* Current bytes of pending data */
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
int iPrevLangid; /* Langid of recently inserted document */
+ int bPrevDelete; /* True if last operation was a delete */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
/* State variables used for validating that the transaction control
diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c
index 4cd2aebf6..6b7d5ef2a 100644
--- a/ext/fts3/fts3_write.c
+++ b/ext/fts3/fts3_write.c
@@ -860,10 +860,12 @@ static int fts3PendingTermsAdd(
*/
static int fts3PendingTermsDocid(
Fts3Table *p, /* Full-text table handle */
+ int bDelete, /* True if this op is a delete */
int iLangid, /* Language id of row being written */
sqlite_int64 iDocid /* Docid of row being written */
){
assert( iLangid>=0 );
+ assert( bDelete==1 || bDelete==0 );
/* TODO(shess) Explore whether partially flushing the buffer on
** forced-flush would provide better performance. I suspect that if
@@ -871,7 +873,8 @@ static int fts3PendingTermsDocid(
** buffer was half empty, that would let the less frequent terms
** generate longer doclists.
*/
- if( iDocid<=p->iPrevDocid
+ if( iDocid<p->iPrevDocid
+ || (iDocid==p->iPrevDocid && p->bPrevDelete==0)
|| p->iPrevLangid!=iLangid
|| p->nPendingData>p->nMaxPendingData
){
@@ -880,6 +883,7 @@ static int fts3PendingTermsDocid(
}
p->iPrevDocid = iDocid;
p->iPrevLangid = iLangid;
+ p->bPrevDelete = bDelete;
return SQLITE_OK;
}
@@ -1069,7 +1073,8 @@ static void fts3DeleteTerms(
if( SQLITE_ROW==sqlite3_step(pSelect) ){
int i;
int iLangid = langidFromSelect(p, pSelect);
- rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0));
+ i64 iDocid = sqlite3_column_int64(pSelect, 0);
+ rc = fts3PendingTermsDocid(p, 1, iLangid, iDocid);
for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){
int iCol = i-1;
if( p->abNotindexed[iCol]==0 ){
@@ -3512,7 +3517,7 @@ static int fts3DoRebuild(Fts3Table *p){
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
int iCol;
int iLangid = langidFromSelect(p, pStmt);
- rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0));
+ rc = fts3PendingTermsDocid(p, 0, iLangid, sqlite3_column_int64(pStmt, 0));
memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1));
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
if( p->abNotindexed[iCol]==0 ){
@@ -5617,7 +5622,7 @@ int sqlite3Fts3UpdateMethod(
}
}
if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
- rc = fts3PendingTermsDocid(p, iLangid, *pRowid);
+ rc = fts3PendingTermsDocid(p, 0, iLangid, *pRowid);
}
if( rc==SQLITE_OK ){
assert( p->iPrevDocid==*pRowid );
diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c
index b8aa52c2f..9910c59b0 100644
--- a/ext/fts5/fts5_index.c
+++ b/ext/fts5/fts5_index.c
@@ -5361,7 +5361,7 @@ static void fts5DecodeFunction(
memset(&term, 0, sizeof(Fts5Buffer));
if( n<4 ){
- sqlite3Fts5BufferSet(&rc, &s, 8, (const u8*)"corrupt");
+ sqlite3Fts5BufferSet(&rc, &s, 7, (const u8*)"corrupt");
goto decode_out;
}else{
iRowidOff = fts5GetU16(&a[0]);
diff --git a/ext/misc/json1.c b/ext/misc/json1.c
index 89b70f083..77857f544 100644
--- a/ext/misc/json1.c
+++ b/ext/misc/json1.c
@@ -1937,19 +1937,12 @@ static sqlite3_module jsonTreeModule = {
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/****************************************************************************
-** The following routine is the only publically visible identifier in this
-** file. Call the following routine in order to register the various SQL
+** The following routines are the only publically visible identifiers in this
+** file. Call the following routines in order to register the various SQL
** functions and the virtual table implemented by this file.
****************************************************************************/
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
-int sqlite3_json_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
+int sqlite3Json1Init(sqlite3 *db){
int rc = SQLITE_OK;
unsigned int i;
static const struct {
@@ -1987,8 +1980,6 @@ int sqlite3_json_init(
{ "json_tree", &jsonTreeModule },
};
#endif
- SQLITE_EXTENSION_INIT2(pApi);
- (void)pzErrMsg; /* Unused parameter */
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
@@ -2002,3 +1993,17 @@ int sqlite3_json_init(
#endif
return rc;
}
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_json_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return sqlite3Json1Init(db);
+}
diff --git a/main.mk b/main.mk
index 1584f57a6..401906d98 100644
--- a/main.mk
+++ b/main.mk
@@ -461,7 +461,7 @@ FUZZDATA = \
# Extra arguments for including json1 in the build of tools
#
JSON1_DEP = $(TOP)/ext/misc/json1.c sqlite3ext.h
-JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE
+JSON1_OPT = -DSQLITE_SHELL_JSON1 -DSQLITE_CORE
JSON1_SRC = $(TOP)/ext/misc/json1.c
# Standard options to testfixture
@@ -540,6 +540,9 @@ sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
+sqlite3ext.h: target_source
+ cp tsrc/sqlite3ext.h .
+
sqlite3.c-debug: target_source $(TOP)/tool/mksqlite3c.tcl
tclsh $(TOP)/tool/mksqlite3c.tcl --linemacros
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
diff --git a/manifest b/manifest
index 248e5f6dc..bcf5b0db0 100644
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sthe\ssessions\sbranch.
-D 2015-09-24T14:26:51.287
+C Merge\srecent\senhancements\sfrom\strunk,\sand\sespecially\sthe\sfix\sfor\nticket\s[1b266395d6bc10].
+D 2015-09-30T14:50:39.955
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
-F Makefile.in 5bda8cc6bbd2be407f4ef679f4f235ba47c7a3c0
+F Makefile.in fdcfdc361f0a3723da9b48b967f259f7aaff3ad5
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
-F Makefile.msc fd760727d07ba21b5f779c6a056a0c6ea7f40979
+F Makefile.msc 2f913bc2229596a31ec1025ce34aef9634164a6a
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F VERSION ccfc4d1576dbfdeece0a4372a2e6a2e37d3e7975
@@ -78,9 +78,9 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts3/fts3.c b04b0c57761fdba2ae562d9d9ba50c7c4a95d9ea
+F ext/fts3/fts3.c e028eb13432f108d2e22cded019fc980700e4e00
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
-F ext/fts3/fts3Int.h 601743955ac43a0e82e6828a931c07bb3b0c95ff
+F ext/fts3/fts3Int.h c84125c666ee54cef6efce6ff64abb0d0e2f4535
F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1
F ext/fts3/fts3_expr.c 71c063da9c2a4167fb54aec089dd5ef33a58c9cb
F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
@@ -96,7 +96,7 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
F ext/fts3/fts3_unicode.c a93f5edc0aff44ef8b06d7cb55b52026541ca145
F ext/fts3/fts3_unicode2.c c3d01968d497bd7001e7dc774ba75b372738c057
-F ext/fts3/fts3_write.c 4f005f78592a1447ca96c8475ef5342ab7dbe02a
+F ext/fts3/fts3_write.c 6f7233a06df17084d5cd968899053731bf953f94
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/fts3/tool/fts3view.c 8e53d0190a7b3443764bbd32ad47be2bd852026d
@@ -112,7 +112,7 @@ F ext/fts5/fts5_buffer.c 64dcaf36a3ebda9e84b7c3b8788887ec325e12a4
F ext/fts5/fts5_config.c 57ee5fe71578cb494574fc0e6e51acb9a22a8695
F ext/fts5/fts5_expr.c 667faaf14a69a5683ac383acdc8d942cf32c3f93
F ext/fts5/fts5_hash.c 4bf4b99708848357b8a2b5819e509eb6d3df9246
-F ext/fts5/fts5_index.c 4fdbc0a321e3a1d73741a623d7aea4db78d6a86d
+F ext/fts5/fts5_index.c c77882ab38d698d5147cef96fa67a2121d77c0b3
F ext/fts5/fts5_main.c 53116cffeb26898832ff7700cc5ebac5fe085d32
F ext/fts5/fts5_storage.c 120f7b143688b5b7710dacbd48cff211609b8059
F ext/fts5/fts5_tcl.c 6da58d6e8f42a93c4486b5ba9b187a7f995dee37
@@ -195,7 +195,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767
F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
-F ext/misc/json1.c 557d6b2d0c3d26625e686a4b4ef8d4a50b8cec94
+F ext/misc/json1.c 263cac0292302b7cf7ecb2e8bd698a50d1aedecc
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
@@ -280,7 +280,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 284e224af00642c0e5e9454c4b07ec5c2d7860d5
+F main.mk f0a074da8e55de2646bc3aca50f13989d4558c1e
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk 0e7f04a8eb90f92259e47d80110e4e98d7ce337a
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@@ -301,30 +301,30 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
F src/backup.c c3a9c4209439b806c44cf30daf466955727bf46c
F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
-F src/btree.c 164583151135a3764672c2c25aa8e4fa06bdb12b
+F src/btree.c a5a653087ae98dd743d12ae0920d5b64c5335960
F src/btree.h 40189aefdc2b830d25c8b58fd7d56538481bfdd7
F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0
-F src/build.c 8a86f4203ac8a9ac0734f242a96f043edffb6018
+F src/build.c 0549b56722f15c146ca21f82a33838365c2031f0
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
F src/date.c fb1c99172017dcc8e237339132c91a21a0788584
F src/dbstat.c e637e7a7ff40ef32132a418c6fdf1cfb63aa27c7
-F src/delete.c d14f86935c7ff120f04532af8332c2ca21a7af0c
-F src/expr.c 3a76afcdac925294c39903b7002ddb9e5fd29863
+F src/delete.c 4545c9f793f27d14a32195f6a0b121913a80f692
+F src/expr.c 36381822e617cb3586d4be1af2cbc3dd5f2b84eb
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
-F src/fkey.c 83e1baba999bed3144ea5a2143fc922edf51135f
+F src/fkey.c 31900763094a3736a5fc887469202eb579fef2d0
F src/func.c ecdd69ec6a1e406f04cc73324be2ebbf6354197f
F src/global.c 508e4087f7b41d688e4762dcf4d4fe28cfbc87f9
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c 7708c63195ba6397325fe64b8442085e73da6851
+F src/insert.c a1e265bbe11d179cb5dc61541e927c6d4f0d04bd
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
F src/lempar.c d344a95d60c24e2f490ee59db9784b1b17439012
F src/loadext.c f0b66d28e377fd6c6d36cc9d92df1ff251ebee44
-F src/main.c 9e2e596c97401a7b99f2f500f5e2c725d53eabe2
+F src/main.c 34206b735954c44defba8f324b613e5d5de89ce3
F src/malloc.c 3a37ce6979a40f499d8cea9e9ab4e8517854d35d
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
@@ -336,8 +336,8 @@ F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495
F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
-F src/mutex_unix.c 8cfa6e83c618d2fcae0fe63f4d2b5bb16b11a97a
-F src/mutex_w32.c 2e025e6642eaf27597403690980f560d1a91f62c
+F src/mutex_unix.c a94b46f3f7beba307dde1b298b0f99f9c3677a93
+F src/mutex_w32.c b483d3e5914b84c82516a6a9919582f12ef3b838
F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7
F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
@@ -352,19 +352,19 @@ F src/parse.y f599aa5e871a493330d567ced93de696f61f48f7
F src/pcache.c 24be750c79272e0ca7b6e007bc94999700f3e5ef
F src/pcache.h 9968603796240cdf83da7e7bef76edf90619cea9
F src/pcache1.c e822007159d53a7ea7aa040d6e28964ddb6de083
-F src/pragma.c d71b813e67bf03f3116b9dd5164fbfd81ec673a2
+F src/pragma.c 234814978bcd35bce6e2874dfb2f5b5e28e7fb38
F src/pragma.h 631a91c8b0e6ca8f051a1d8a4a0da4150e04620a
F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
F src/printf.c 0c4bcdd1c2e2521024f0a69cb5eb334f86b3652a
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 1954a0f01bf65d78d7d559aea3d5c67f33376d91
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
-F src/select.c 36ee14d729e182cd8b6796f980b7ab3fc9bcee72
-F src/shell.c 2b29a6f5c1b431eb0d25196e249d7e69b68d5ef0
-F src/sqlite.h.in 627e991195047aebc49f3c895d458f9ac2805e4a
+F src/select.c e49f4af9748c9e0cc1bf864b4190aa94841c8409
+F src/shell.c f38cfe6a0b971d50158e71880852119bdca89ce9
+F src/sqlite.h.in 8f4deb5874227c7635300fb75105ff6e92131fb5
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 64350bf36833a56ad675e27392a913f417c5c308
-F src/sqliteInt.h 211b8080f46e7cda9f6df9ab590a7aec1b51d999
+F src/sqliteInt.h a6edcbabcd78cfd888f74aaedb2160241a38294f
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
@@ -414,11 +414,11 @@ F src/test_thread.c af391ec03d23486dffbcc250b7e58e073f172af9
F src/test_vfs.c 3b65d42e18b262805716bd96178c81da8f2d9283
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
-F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
+F src/threads.c bbfb74450643cb5372a43ad4f6cffd7e9dfcecb0
F src/tokenize.c 83c6ed569423a3af83a83973b444cf7123be33a6
F src/treeview.c 154f0acc622fa3514de8777dcedf4c8a8802b4ce
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
-F src/update.c 04a06489008ad1d6007e6de3fbe4caed7c6c620b
+F src/update.c 9e102cc3d8732aeb6b977569c8ce1f73fb0031bd
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
@@ -427,18 +427,18 @@ F src/vdbe.h 67151895e779b35475c6c11b16be2ceb839066c8
F src/vdbeInt.h 42fa34502937071aabd3c0596575ba9776547353
F src/vdbeapi.c f5eda36a5c85ef578957ab4311e8d9b1f51a3552
F src/vdbeaux.c 4cbd4cc79dad0e2c2b9996ae018d79a7330110f4
-F src/vdbeblob.c 53ed7f38ab93922038bcfa17aae514dc47752f1e
+F src/vdbeblob.c b400c25ac822af3c507ef84b5cd93c1583a70321
F src/vdbemem.c 28ab8455ac490373798cf2c21def2c1287942551
F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
-F src/vtab.c 2ecfe020c10e0a0c7b078203fdba2fae844744bc
+F src/vtab.c 9a6d8818c8a2477ce547f064701b5e955b25d894
F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
-F src/where.c d5eed2584542e7f3bc78ddef7809a9d76d14d811
+F src/where.c 4c4646675e794ac71e701289edefd7cd81bac844
F src/whereInt.h 7892bb54cf9ca0ae5c7e6094491b94c9286dc647
-F src/wherecode.c 7660e1ad16817a921b099af553f3e1349352d16f
+F src/wherecode.c a32bf1f304f6328e3eefcb82e70bd86836cff343
F src/whereexpr.c 2473e4350e30f9b55d1c6a8f66ca23c689f23f1d
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
@@ -591,7 +591,7 @@ F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
F test/delete.test e1bcdf8926234e27aac24b346ad83d3329ec8b6f
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
-F test/delete4.test d9e7d553a939597b27d205b022d769469f361c1f
+F test/delete4.test 3ac0b8d23689ba764c2e8b78c1b56b8f1b942fa2
F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
@@ -640,7 +640,7 @@ F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7
F test/filectrl.test 7c13f96457435238da99aff7343ad6a3a4885787
F test/filefmt.test cb34663f126cbc2d358af552dcaf5c72769b0146
-F test/fkey1.test de5b287f6a480b36bd51e8debcf48168e26e4ed2
+F test/fkey1.test 13e3d48236a2b9f5c5ebd232eef9b3ab682a8a2c
F test/fkey2.test f3d27ecba480a348c328965d154214719bb158a9
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
@@ -709,7 +709,7 @@ F test/fts3aux2.test 7ae2b2c13aefdf4169279a27a5f51780ce57f6ba
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
-F test/fts3conf.test ee8500c86dd58ec075e8831a1e216a79989436de
+F test/fts3conf.test ff90127b2462c788348c0dd7f6f8c573ef9cb5d9
F test/fts3corrupt.test 2710b77983cc7789295ddbffea52c1d3b7506dbb
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
@@ -755,6 +755,7 @@ F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7
F test/fts4merge4.test d895b1057a7798b67e03455d0fa50e9ea836c47b
F test/fts4noti.test 524807f0c36d49deea7920cdd4cd687408b58849
+F test/fts4onepass.test bfca61f69c6ca74cd71e6dca12a0cdd47192fc24
F test/fts4unicode.test 27378af76394542cf490cf001d8d1505fe55f6a9
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
@@ -771,7 +772,7 @@ F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
F test/fuzzcheck.c b8eb7ee40f6d28548a0b028e0676293522f3427f
F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664
F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973
-F test/fuzzdata3.db 1d6044c33a114007f02b6e6846f1fa232f607bfd
+F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
F test/fuzzdata4.db 1882f0055fb63214d8407ddc7aca9b0b1c59af21
F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
@@ -804,7 +805,7 @@ F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a
F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c
F test/indexedby.test 9c4cd331224e57f79fbf411ae245e6272d415985
-F test/indexexpr1.test 203c83a05accf6f2b748834192f3564321b8c0d8
+F test/indexexpr1.test bbb52b5d5717d9f23853826963b0af5110009366
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
F test/insert.test 38742b5e9601c8f8d76e9b7555f7270288c2d371
@@ -1268,7 +1269,7 @@ F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
-F test/view.test 3930ae94042d702ab15a6a0ef692cfa5c9f9b68b
+F test/view.test 0d8935e64867ae01492347884f5cbb47f056f4c8
F test/vtab1.test 6210e076997f176bedc300a87ad6404651b601dd
F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad
F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e
@@ -1408,7 +1409,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P b7469c44be77358e02892a3abc696f7caa0dcd3b c6ab807b72ddfc1462f61aa91442b6fac04ace8a
-R 1b640a6d419521daa5b2a632ced07132
+P c91065f8edb1e54076791716fc20d3fcfe3070dc e796c0efb6cf17444b53af75046daf7d8fa82f78
+R 0237333bf6dc63bf0c4f4c66d7918366
U drh
-Z b4a9ac9211cc2ca2704bd7cc4c9b5b17
+Z 5f6cedfd7517312f688a69652091b608
diff --git a/manifest.uuid b/manifest.uuid
index 5cc71bdd0..75c74a20e 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-c91065f8edb1e54076791716fc20d3fcfe3070dc \ No newline at end of file
+b2face9aa95ade96a5666c70b6b31064c1ad0977 \ No newline at end of file
diff --git a/src/btree.c b/src/btree.c
index c7d6fabba..1eae0ac5d 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -661,7 +661,7 @@ static int saveCursorPosition(BtCursor *pCur){
pCur->eState = CURSOR_REQUIRESEEK;
}
- invalidateOverflowCache(pCur);
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl|BTCF_AtLast);
return rc;
}
@@ -7624,7 +7624,7 @@ static int balance_nonroot(
** by smaller than the child due to the database header, and so all the
** free space needs to be up front.
*/
- assert( nNew==1 );
+ assert( nNew==1 || CORRUPT_DB );
rc = defragmentPage(apNew[0]);
testcase( rc!=SQLITE_OK );
assert( apNew[0]->nFree ==
diff --git a/src/build.c b/src/build.c
index eee62ee15..ea89e501d 100644
--- a/src/build.c
+++ b/src/build.c
@@ -192,6 +192,8 @@ void sqlite3FinishCoding(Parse *pParse){
db->aDb[iDb].pSchema->iGeneration /* P4 */
);
if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
+ VdbeComment((v,
+ "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite));
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
for(i=0; i<pParse->nVtabLock; i++){
@@ -2082,8 +2084,7 @@ void sqlite3CreateView(
if( pParse->nVar>0 ){
sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
- sqlite3SelectDelete(db, pSelect);
- return;
+ goto create_view_fail;
}
sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
p = pParse->pNewTable;
@@ -3135,7 +3136,7 @@ Index *sqlite3CreateIndex(
/* Analyze the list of expressions that form the terms of the index and
** report any errors. In the common case where the expression is exactly
** a table column, store that column in aiColumn[]. For general expressions,
- ** populate pIndex->aColExpr and store -2 in aiColumn[].
+ ** populate pIndex->aColExpr and store XN_EXPR (-2) in aiColumn[].
**
** TODO: Issue a warning if two or more columns of the index are identical.
** TODO: Issue a warning if the table primary key is used as part of the
@@ -3164,8 +3165,8 @@ Index *sqlite3CreateIndex(
pListItem = &pCopy->a[i];
}
}
- j = -2;
- pIndex->aiColumn[i] = -2;
+ j = XN_EXPR;
+ pIndex->aiColumn[i] = XN_EXPR;
pIndex->uniqNotNull = 0;
}else{
j = pCExpr->iColumn;
@@ -3218,7 +3219,7 @@ Index *sqlite3CreateIndex(
}
assert( i==pIndex->nColumn );
}else{
- pIndex->aiColumn[i] = -1;
+ pIndex->aiColumn[i] = XN_ROWID;
pIndex->azColl[i] = "BINARY";
}
sqlite3DefaultRowEst(pIndex);
diff --git a/src/delete.c b/src/delete.c
index 914b3dfae..9abefda1a 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -414,7 +414,7 @@ void sqlite3DeleteFrom(
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
if( pWInfo==0 ) goto delete_from_cleanup;
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
- assert( IsVirtual(pTab)==0 || eOnePass==ONEPASS_OFF );
+ assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
/* Keep track of the number of rows to be deleted */
@@ -425,7 +425,7 @@ void sqlite3DeleteFrom(
/* Extract the rowid or primary key for the current row */
if( pPk ){
for(i=0; i<nPk; i++){
- assert( pPk->aiColumn[i]>=(-1) );
+ assert( pPk->aiColumn[i]>=0 );
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
pPk->aiColumn[i], iPk+i);
}
@@ -497,7 +497,7 @@ void sqlite3DeleteFrom(
*/
if( eOnePass!=ONEPASS_OFF ){
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
- if( aToOpen[iDataCur-iTabCur] ){
+ if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){
assert( pPk!=0 || pTab->pSelect!=0 );
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
VdbeCoverage(v);
@@ -519,7 +519,11 @@ void sqlite3DeleteFrom(
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, OE_Abort);
+ assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
sqlite3MayAbort(pParse);
+ if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){
+ pParse->isMultiWrite = 0;
+ }
}else
#endif
{
@@ -862,7 +866,7 @@ int sqlite3GenerateIndexKey(
for(j=0; j<nCol; j++){
if( pPrior
&& pPrior->aiColumn[j]==pIdx->aiColumn[j]
- && pPrior->aiColumn[j]>=(-1)
+ && pPrior->aiColumn[j]!=XN_EXPR
){
/* This column was already computed by the previous index */
continue;
diff --git a/src/expr.c b/src/expr.c
index 3141cd9db..b52e814f3 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2443,15 +2443,15 @@ void sqlite3ExprCodeLoadIndexColumn(
int regOut /* Store the index column value in this register */
){
i16 iTabCol = pIdx->aiColumn[iIdxCol];
- if( iTabCol>=(-1) ){
+ if( iTabCol==XN_EXPR ){
+ assert( pIdx->aColExpr );
+ assert( pIdx->aColExpr->nExpr>iIdxCol );
+ pParse->iSelfTab = iTabCur;
+ sqlite3ExprCode(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut);
+ }else{
sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable, iTabCur,
iTabCol, regOut);
- return;
}
- assert( pIdx->aColExpr );
- assert( pIdx->aColExpr->nExpr>iIdxCol );
- pParse->iSelfTab = iTabCur;
- sqlite3ExprCode(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut);
}
/*
diff --git a/src/fkey.c b/src/fkey.c
index a087889f4..b55e2a981 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -252,6 +252,8 @@ int sqlite3FkLocateIndex(
char *zDfltColl; /* Def. collation for column */
char *zIdxCol; /* Name of indexed column */
+ if( iCol<0 ) break; /* No foreign keys against expression indexes */
+
/* If the index uses a collation sequence that is different from
** the default collation sequence for the column, this index is
** unusable. Bail out early in this case. */
@@ -404,6 +406,7 @@ static void fkLookupParent(
for(i=0; i<nCol; i++){
int iChild = aiCol[i]+1+regData;
int iParent = pIdx->aiColumn[i]+1+regData;
+ assert( pIdx->aiColumn[i]>=0 );
assert( aiCol[i]!=pTab->iPKey );
if( pIdx->aiColumn[i]==pTab->iPKey ){
/* The parent key is a composite key that includes the IPK column */
@@ -612,6 +615,7 @@ static void fkScanChildren(
assert( pIdx!=0 );
for(i=0; i<pPk->nKeyCol; i++){
i16 iCol = pIdx->aiColumn[i];
+ assert( iCol>=0 );
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
@@ -931,6 +935,7 @@ void sqlite3FkCheck(
if( aiCol[i]==pTab->iPKey ){
aiCol[i] = -1;
}
+ assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
#ifndef SQLITE_OMIT_AUTHORIZATION
/* Request permission to read the parent key columns. If the
** authorization callback returns SQLITE_IGNORE, behave as if any
@@ -1062,7 +1067,10 @@ u32 sqlite3FkOldmask(
Index *pIdx = 0;
sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
if( pIdx ){
- for(i=0; i<pIdx->nKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
+ for(i=0; i<pIdx->nKeyCol; i++){
+ assert( pIdx->aiColumn[i]>=0 );
+ mask |= COLUMN_MASK(pIdx->aiColumn[i]);
+ }
}
}
}
@@ -1185,6 +1193,7 @@ static Trigger *fkActionTrigger(
iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iFromCol>=0 );
assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKey<pTab->nCol) );
+ assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
tToCol.z = pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName;
tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName;
diff --git a/src/insert.c b/src/insert.c
index 746f58db3..4108c8879 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -90,11 +90,11 @@ const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
i16 x = pIdx->aiColumn[n];
if( x>=0 ){
pIdx->zColAff[n] = pTab->aCol[x].affinity;
- }else if( x==(-1) ){
+ }else if( x==XN_ROWID ){
pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
}else{
char aff;
- assert( x==(-2) );
+ assert( x==XN_EXPR );
assert( pIdx->aColExpr!=0 );
aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
if( aff==0 ) aff = SQLITE_AFF_BLOB;
@@ -260,7 +260,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){
/* This routine is never called during trigger-generation. It is
** only called from the top-level */
assert( pParse->pTriggerTab==0 );
- assert( pParse==sqlite3ParseToplevel(pParse) );
+ assert( sqlite3IsToplevel(pParse) );
assert( v ); /* We failed long ago if this is not so */
for(p = pParse->pAinc; p; p = p->pNext){
@@ -1417,13 +1417,13 @@ void sqlite3GenerateConstraintChecks(
for(i=0; i<pIdx->nColumn; i++){
int iField = pIdx->aiColumn[i];
int x;
- if( iField==(-2) ){
+ if( iField==XN_EXPR ){
pParse->ckBase = regNewData+1;
sqlite3ExprCode(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i);
pParse->ckBase = 0;
VdbeComment((v, "%s column %d", pIdx->zName, i));
}else{
- if( iField==(-1) || iField==pTab->iPKey ){
+ if( iField==XN_ROWID || iField==pTab->iPKey ){
if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
x = regNewData;
regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
@@ -1482,6 +1482,7 @@ void sqlite3GenerateConstraintChecks(
** store it in registers regR..regR+nPk-1 */
if( pIdx!=pPk ){
for(i=0; i<pPk->nKeyCol; i++){
+ assert( pPk->aiColumn[i]>=0 );
x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i);
VdbeComment((v, "%s.%s", pTab->zName,
@@ -1503,6 +1504,7 @@ void sqlite3GenerateConstraintChecks(
for(i=0; i<pPk->nKeyCol; i++){
char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]);
x = pPk->aiColumn[i];
+ assert( x>=0 );
if( i==(pPk->nKeyCol-1) ){
addrJump = addrUniqueOk;
op = OP_Eq;
@@ -1754,7 +1756,7 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){
return 0; /* Different columns indexed */
}
- if( pSrc->aiColumn[i]==(-2) ){
+ if( pSrc->aiColumn[i]==XN_EXPR ){
assert( pSrc->aColExpr!=0 && pDest->aColExpr!=0 );
if( sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr,
pDest->aColExpr->a[i].pExpr, -1)!=0 ){
diff --git a/src/main.c b/src/main.c
index fbb7da437..2aca573b0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2917,6 +2917,13 @@ static int openDatabase(
}
#endif
+#ifdef SQLITE_ENABLE_JSON1
+ if( !db->mallocFailed && rc==SQLITE_OK){
+ extern int sqlite3Json1Init(sqlite3*);
+ rc = sqlite3Json1Init(db);
+ }
+#endif
+
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking
** mode. Doing nothing at all also makes NORMAL the default.
diff --git a/src/mutex_unix.c b/src/mutex_unix.c
index cebb96c90..dbdaa225b 100644
--- a/src/mutex_unix.c
+++ b/src/mutex_unix.c
@@ -81,7 +81,9 @@ static int pthreadMutexNotheld(sqlite3_mutex *p){
#endif
/*
-** Try to provide a memory barrier operation, needed for initialization only.
+** Try to provide a memory barrier operation, needed for initialization
+** and also for the implementation of xShmBarrier in the VFS in cases
+** where SQLite is compiled without mutexes.
*/
void sqlite3MemoryBarrier(void){
#if defined(SQLITE_MEMORY_BARRIER)
diff --git a/src/mutex_w32.c b/src/mutex_w32.c
index 90be07db2..7d28e4def 100644
--- a/src/mutex_w32.c
+++ b/src/mutex_w32.c
@@ -78,7 +78,9 @@ static int winMutexNotheld(sqlite3_mutex *p){
#endif
/*
-** Try to provide a memory barrier operation, needed for initialization only.
+** Try to provide a memory barrier operation, needed for initialization
+** and also for the xShmBarrier method of the VFS in cases when SQLite is
+** compiled without mutexes (SQLITE_THREADSAFE=0).
*/
void sqlite3MemoryBarrier(void){
#if defined(SQLITE_MEMORY_BARRIER)
diff --git a/src/pragma.c b/src/pragma.c
index 2dcad614f..e5e7e54a2 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -1524,8 +1524,8 @@ void sqlite3Pragma(
int kk;
for(kk=0; kk<pIdx->nKeyCol; kk++){
int iCol = pIdx->aiColumn[kk];
- assert( iCol>=0 && iCol<pTab->nCol );
- if( pTab->aCol[iCol].notNull ) continue;
+ assert( iCol!=XN_ROWID && iCol<pTab->nCol );
+ if( iCol>=0 && pTab->aCol[iCol].notNull ) continue;
sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
VdbeCoverage(v);
}
diff --git a/src/select.c b/src/select.c
index a4aaa0eca..8430a00f3 100644
--- a/src/select.c
+++ b/src/select.c
@@ -4221,17 +4221,9 @@ static int selectExpander(Walker *pWalker, Select *p){
*/
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab;
- assert( pFrom->fg.isRecursive==0 || pFrom->pTab );
+ assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
if( pFrom->fg.isRecursive ) continue;
- if( pFrom->pTab!=0 ){
- /* This statement has already been prepared. There is no need
- ** to go further. */
- assert( i==0 );
-#ifndef SQLITE_OMIT_CTE
- selectPopWith(pWalker, p);
-#endif
- return WRC_Prune;
- }
+ assert( pFrom->pTab==0 );
#ifndef SQLITE_OMIT_CTE
if( withExpand(pWalker, pFrom) ) return WRC_Abort;
if( pFrom->pTab ) {} else
@@ -4523,19 +4515,19 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
struct SrcList_item *pFrom;
assert( p->selFlags & SF_Resolved );
- if( (p->selFlags & SF_HasTypeInfo)==0 ){
- p->selFlags |= SF_HasTypeInfo;
- pParse = pWalker->pParse;
- pTabList = p->pSrc;
- for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
- Table *pTab = pFrom->pTab;
- if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){
- /* A sub-query in the FROM clause of a SELECT */
- Select *pSel = pFrom->pSelect;
- if( pSel ){
- while( pSel->pPrior ) pSel = pSel->pPrior;
- selectAddColumnTypeAndCollation(pParse, pTab, pSel);
- }
+ assert( (p->selFlags & SF_HasTypeInfo)==0 );
+ p->selFlags |= SF_HasTypeInfo;
+ pParse = pWalker->pParse;
+ pTabList = p->pSrc;
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ Table *pTab = pFrom->pTab;
+ assert( pTab!=0 );
+ if( (pTab->tabFlags & TF_Ephemeral)!=0 ){
+ /* A sub-query in the FROM clause of a SELECT */
+ Select *pSel = pFrom->pSelect;
+ if( pSel ){
+ while( pSel->pPrior ) pSel = pSel->pPrior;
+ selectAddColumnTypeAndCollation(pParse, pTab, pSel);
}
}
}
@@ -4861,7 +4853,17 @@ int sqlite3Select(
struct SrcList_item *pItem = &pTabList->a[i];
Select *pSub = pItem->pSelect;
int isAggSub;
+ Table *pTab = pItem->pTab;
if( pSub==0 ) continue;
+
+ /* Catch mismatch in the declared columns of a view and the number of
+ ** columns in the SELECT on the RHS */
+ if( pTab->nCol!=pSub->pEList->nExpr ){
+ sqlite3ErrorMsg(pParse, "expected %d columns for '%s' but got %d",
+ pTab->nCol, pTab->zName, pSub->pEList->nExpr);
+ goto select_end;
+ }
+
isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
/* This subquery can be absorbed into its parent. */
diff --git a/src/shell.c b/src/shell.c
index 277473099..ca8e37186 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -2701,6 +2701,22 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
return 0;
}
+/*
+** Print the current sqlite3_errmsg() value to stderr and return 1.
+*/
+static int shellDatabaseError(sqlite3 *db){
+ const char *zErr = sqlite3_errmsg(db);
+ fprintf(stderr, "Error: %s\n", zErr);
+ return 1;
+}
+
+/*
+** Print an out-of-memory message to stderr and return 1.
+*/
+static int shellNomemError(void){
+ fprintf(stderr, "Error: out of memory\n");
+ return 1;
+}
/*
** If an input line begins with "." then invoke this routine to
@@ -3994,13 +4010,17 @@ static int do_meta_command(char *zLine, ShellState *p){
int ii;
open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
- if( rc ) return rc;
+ if( rc ) return shellDatabaseError(p->db);
+
+ /* Create an SQL statement to query for the list of tables in the
+ ** main and all attached databases where the table name matches the
+ ** LIKE pattern bound to variable "?1". */
zSql = sqlite3_mprintf(
"SELECT name FROM sqlite_master"
" WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite_%%'"
" AND name LIKE ?1");
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ while( zSql && sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue;
if( strcmp(zDbName,"temp")==0 ){
@@ -4019,11 +4039,17 @@ static int do_meta_command(char *zLine, ShellState *p){
" AND name LIKE ?1", zSql, zDbName, zDbName);
}
}
- sqlite3_finalize(pStmt);
- zSql = sqlite3_mprintf("%z ORDER BY 1", zSql);
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ rc = sqlite3_finalize(pStmt);
+ if( zSql && rc==SQLITE_OK ){
+ zSql = sqlite3_mprintf("%z ORDER BY 1", zSql);
+ if( zSql ) rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ }
sqlite3_free(zSql);
- if( rc ) return rc;
+ if( !zSql ) return shellNomemError();
+ if( rc ) return shellDatabaseError(p->db);
+
+ /* Run the SQL statement prepared by the above block. Store the results
+ ** as an array of nul-terminated strings in azResult[]. */
nRow = nAlloc = 0;
azResult = 0;
if( nArg>1 ){
@@ -4037,17 +4063,25 @@ static int do_meta_command(char *zLine, ShellState *p){
int n2 = nAlloc*2 + 10;
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
if( azNew==0 ){
- fprintf(stderr, "Error: out of memory\n");
+ rc = shellNomemError();
break;
}
nAlloc = n2;
azResult = azNew;
}
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
- if( azResult[nRow] ) nRow++;
+ if( 0==azResult[nRow] ){
+ rc = shellNomemError();
+ break;
+ }
+ nRow++;
+ }
+ if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
+ rc = shellDatabaseError(p->db);
}
- sqlite3_finalize(pStmt);
- if( nRow>0 ){
+
+ /* Pretty-print the contents of array azResult[] to the output */
+ if( rc==0 && nRow>0 ){
int len, maxlen = 0;
int i, j;
int nPrintCol, nPrintRow;
@@ -4066,6 +4100,7 @@ static int do_meta_command(char *zLine, ShellState *p){
fprintf(p->out, "\n");
}
}
+
for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
sqlite3_free(azResult);
}else
@@ -4900,7 +4935,7 @@ int SQLITE_CDECL main(int argc, char **argv){
}
data.out = stdout;
-#ifdef SQLITE_ENABLE_JSON1
+#ifdef SQLITE_SHELL_JSON1
{
extern int sqlite3_json_init(sqlite3*);
sqlite3_auto_extension((void(*)(void))sqlite3_json_init);
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 471b7f526..8934d729e 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -4360,7 +4360,7 @@ int sqlite3_value_type(sqlite3_value*);
int sqlite3_value_numeric_type(sqlite3_value*);
/*
-** CAPI3REF: Obtaining SQL Values
+** CAPI3REF: Finding The Subtype Of SQL Values
** METHOD: sqlite3_value
**
** The sqlite3_value_subtype(V) function returns the subtype for
@@ -5634,13 +5634,31 @@ struct sqlite3_module {
** ^The estimatedRows value is an estimate of the number of rows that
** will be returned by the strategy.
**
+** The xBestIndex method may optionally populate the idxFlags field with a
+** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
+** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
+** assumes that the strategy may visit at most one row.
+**
+** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
+** SQLite also assumes that if a call to the xUpdate() method is made as
+** part of the same statement to delete or update a virtual table row and the
+** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback
+** any database changes. In other words, if the xUpdate() returns
+** SQLITE_CONSTRAINT, the database contents must be exactly as they were
+** before xUpdate was called. By contrast, if SQLITE_INDEX_SCAN_UNIQUE is not
+** set and xUpdate returns SQLITE_CONSTRAINT, any database changes made by
+** the xUpdate method are automatically rolled back by SQLite.
+**
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
** structure for SQLite version 3.8.2. If a virtual table extension is
** used with an SQLite version earlier than 3.8.2, the results of attempting
** to read or write the estimatedRows field are undefined (but are likely
** to included crashing the application). The estimatedRows field should
** therefore only be used if [sqlite3_libversion_number()] returns a
-** value greater than or equal to 3008002.
+** value greater than or equal to 3008002. Similarly, the idxFlags field
+** was added for version 3.8.12. It may therefore only be used if
+** sqlite3_libversion_number() returns a value greater than or equal to
+** 3008012.
*/
struct sqlite3_index_info {
/* Inputs */
@@ -5668,9 +5686,16 @@ struct sqlite3_index_info {
double estimatedCost; /* Estimated cost of using this index */
/* Fields below are only available in SQLite 3.8.2 and later */
sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ /* Fields below are only available in SQLite 3.8.12 and later */
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
};
/*
+** CAPI3REF: Virtual Table Scan Flags
+*/
+#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
** These macros defined the allowed values for the
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 6b999374b..450eb7cc5 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1925,6 +1925,12 @@ struct Index {
/* Return true if index X is a UNIQUE index */
#define IsUniqueIndex(X) ((X)->onError!=OE_None)
+/* The Index.aiColumn[] values are normally positive integer. But
+** there are some negative values that have special meaning:
+*/
+#define XN_ROWID (-1) /* Indexed column is the rowid */
+#define XN_EXPR (-2) /* Indexed column is an expression */
+
/*
** Each sample stored in the sqlite_stat3 table is represented in memory
** using a structure of this type. See documentation at the top of the
@@ -3516,6 +3522,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
+# define sqlite3IsToplevel(p) ((p)->pToplevel==0)
#else
# define sqlite3TriggersExist(B,C,D,E,F) 0
# define sqlite3DeleteTrigger(A,B)
@@ -3525,6 +3532,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
# define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F)
# define sqlite3TriggerList(X, Y) 0
# define sqlite3ParseToplevel(p) p
+# define sqlite3IsToplevel(p) 1
# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
#endif
diff --git a/src/threads.c b/src/threads.c
index 4ce612227..251b9b763 100644
--- a/src/threads.c
+++ b/src/threads.c
@@ -67,6 +67,10 @@ int sqlite3ThreadCreate(
memset(p, 0, sizeof(*p));
p->xTask = xTask;
p->pIn = pIn;
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ ** function that returns SQLITE_ERROR when passed the argument 200, that
+ ** forces worker threads to run sequentially and deterministically
+ ** for testing purposes. */
if( sqlite3FaultSim(200) ){
rc = 1;
}else{
@@ -151,7 +155,12 @@ int sqlite3ThreadCreate(
*ppThread = 0;
p = sqlite3Malloc(sizeof(*p));
if( p==0 ) return SQLITE_NOMEM;
- if( sqlite3GlobalConfig.bCoreMutex==0 ){
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ ** function that returns SQLITE_ERROR when passed the argument 200, that
+ ** forces worker threads to run sequentially and deterministically
+ ** (via the sqlite3FaultSim() term of the conditional) for testing
+ ** purposes. */
+ if( sqlite3GlobalConfig.bCoreMutex==0 || sqlite3FaultSim(200) ){
memset(p, 0, sizeof(*p));
}else{
p->xTask = xTask;
@@ -179,7 +188,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
assert( ppOut!=0 );
if( NEVER(p==0) ) return SQLITE_NOMEM;
if( p->xTask==0 ){
- assert( p->id==GetCurrentThreadId() );
+ /* assert( p->id==GetCurrentThreadId() ); */
rc = WAIT_OBJECT_0;
assert( p->tid==0 );
}else{
diff --git a/src/update.c b/src/update.c
index aa13e7f7d..fba7762aa 100644
--- a/src/update.c
+++ b/src/update.c
@@ -134,9 +134,9 @@ void sqlite3Update(
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
- int regOldRowid; /* The old rowid */
- int regNewRowid; /* The new rowid */
- int regNew; /* Content of the NEW.* table in triggers */
+ int regOldRowid = 0; /* The old rowid */
+ int regNewRowid = 0; /* The new rowid */
+ int regNew = 0; /* Content of the NEW.* table in triggers */
int regOld = 0; /* Content of OLD.* table in triggers */
int regRowSet = 0; /* Rowset of rows to be updated */
int regKey = 0; /* composite PRIMARY KEY value */
@@ -300,29 +300,20 @@ void sqlite3Update(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, 1, iDb);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- /* Virtual tables must be handled separately */
- if( IsVirtual(pTab) ){
- updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
- pWhere, onError);
- pWhere = 0;
- pTabList = 0;
- goto update_cleanup;
- }
-#endif
-
/* Allocate required registers. */
- regRowSet = ++pParse->nMem;
- regOldRowid = regNewRowid = ++pParse->nMem;
- if( chngPk || pTrigger || hasFK ){
- regOld = pParse->nMem + 1;
+ if( !IsVirtual(pTab) ){
+ regRowSet = ++pParse->nMem;
+ regOldRowid = regNewRowid = ++pParse->nMem;
+ if( chngPk || pTrigger || hasFK ){
+ regOld = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol;
+ }
+ if( chngKey || pTrigger || hasFK ){
+ regNewRowid = ++pParse->nMem;
+ }
+ regNew = pParse->nMem + 1;
pParse->nMem += pTab->nCol;
}
- if( chngKey || pTrigger || hasFK ){
- regNewRowid = ++pParse->nMem;
- }
- regNew = pParse->nMem + 1;
- pParse->nMem += pTab->nCol;
/* Start the view context. */
if( isView ){
@@ -345,6 +336,15 @@ void sqlite3Update(
goto update_cleanup;
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* Virtual tables must be handled separately */
+ if( IsVirtual(pTab) ){
+ updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
+ pWhere, onError);
+ goto update_cleanup;
+ }
+#endif
+
/* Begin the database scan
*/
if( HasRowid(pTab) ){
@@ -384,7 +384,7 @@ void sqlite3Update(
if( pWInfo==0 ) goto update_cleanup;
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
for(i=0; i<nPk; i++){
- assert( pPk->aiColumn[i]>=(-1) );
+ assert( pPk->aiColumn[i]>=0 );
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
iPk+i);
}
@@ -507,7 +507,6 @@ void sqlite3Update(
newmask = sqlite3TriggerColmask(
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
);
- /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
@@ -698,21 +697,23 @@ update_cleanup:
/*
** Generate code for an UPDATE of a virtual table.
**
-** The strategy is that we create an ephemeral table that contains
+** There are two possible strategies - the default and the special
+** "onepass" strategy. Onepass is only used if the virtual table
+** implementation indicates that pWhere may match at most one row.
+**
+** The default strategy is to create an ephemeral table that contains
** for each row to be changed:
**
** (A) The original rowid of that row.
-** (B) The revised rowid for the row. (note1)
+** (B) The revised rowid for the row.
** (C) The content of every column in the row.
**
-** Then we loop over this ephemeral table and for each row in
-** the ephemeral table call VUpdate.
-**
-** When finished, drop the ephemeral table.
+** Then loop through the contents of this ephemeral table executing a
+** VUpdate for each row. When finished, drop the ephemeral table.
**
-** (note1) Actually, if we know in advance that (A) is always the same
-** as (B) we only store (A), then duplicate (A) when pulling
-** it out of the ephemeral table before calling VUpdate.
+** The "onepass" strategy does not use an ephemeral table. Instead, it
+** stores the same values (A, B and C above) in a register array and
+** makes a single invocation of VUpdate.
*/
static void updateVirtualTable(
Parse *pParse, /* The parsing context */
@@ -725,65 +726,95 @@ static void updateVirtualTable(
int onError /* ON CONFLICT strategy */
){
Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */
- ExprList *pEList = 0; /* The result set of the SELECT statement */
- Select *pSelect = 0; /* The SELECT statement */
- Expr *pExpr; /* Temporary expression */
int ephemTab; /* Table holding the result of the SELECT */
int i; /* Loop counter */
- int addr; /* Address of top of loop */
- int iReg; /* First register in set passed to OP_VUpdate */
sqlite3 *db = pParse->db; /* Database connection */
const char *pVTab = (const char*)sqlite3GetVTable(db, pTab);
- SelectDest dest;
-
- /* Construct the SELECT statement that will find the new values for
- ** all updated rows.
- */
- pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_"));
+ WhereInfo *pWInfo;
+ int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */
+ int regArg; /* First register in VUpdate arg array */
+ int regRec; /* Register in which to assemble record */
+ int regRowid; /* Register for ephem table rowid */
+ int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
+ int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
+ int bOnePass; /* True to use onepass strategy */
+ int addr; /* Address of OP_OpenEphemeral */
+
+ /* Allocate nArg registers to martial the arguments to VUpdate. Then
+ ** create and open the ephemeral table in which the records created from
+ ** these arguments will be temporarily stored. */
+ assert( v );
+ ephemTab = pParse->nTab++;
+ addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg);
+ regArg = pParse->nMem + 1;
+ pParse->nMem += nArg;
+ regRec = ++pParse->nMem;
+ regRowid = ++pParse->nMem;
+
+ /* Start scanning the virtual table */
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
+ if( pWInfo==0 ) return;
+
+ /* Populate the argument registers. */
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
if( pRowid ){
- pEList = sqlite3ExprListAppend(pParse, pEList,
- sqlite3ExprDup(db, pRowid, 0));
+ sqlite3ExprCode(pParse, pRowid, regArg+1);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
}
- assert( pTab->iPKey<0 );
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]>=0 ){
- pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
+ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
}else{
- pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName);
+ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
}
- pEList = sqlite3ExprListAppend(pParse, pEList, pExpr);
}
- pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0);
-
- /* Create the ephemeral table into which the update results will
- ** be stored.
- */
- assert( v );
- ephemTab = pParse->nTab++;
- /* fill the ephemeral table
- */
- sqlite3SelectDestInit(&dest, SRT_EphemTab, ephemTab);
- sqlite3Select(pParse, pSelect, &dest);
-
- /* Generate code to scan the ephemeral table and call VUpdate. */
- iReg = ++pParse->nMem;
- pParse->nMem += pTab->nCol+1;
- addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg);
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1);
- for(i=0; i<pTab->nCol; i++){
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i);
+ bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
+
+ if( bOnePass ){
+ /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
+ ** above. Also, if this is a top-level parse (not a trigger), clear the
+ ** multi-write flag so that the VM does not open a statement journal */
+ sqlite3VdbeChangeToNoop(v, addr);
+ if( sqlite3IsToplevel(pParse) ){
+ pParse->isMultiWrite = 0;
+ }
+ }else{
+ /* Create a record from the argument register contents and insert it into
+ ** the ephemeral table. */
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
+ }
+
+
+ if( bOnePass==0 ){
+ /* End the virtual table scan */
+ sqlite3WhereEnd(pWInfo);
+
+ /* Begin scannning through the ephemeral table. */
+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v);
+
+ /* Extract arguments from the current row of the ephemeral table and
+ ** invoke the VUpdate method. */
+ for(i=0; i<nArg; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i, regArg+i);
+ }
}
sqlite3VtabMakeWritable(pParse, pTab);
- sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, nArg, regArg, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse);
- sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
- sqlite3VdbeJumpHere(v, addr);
- sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
- /* Cleanup */
- sqlite3SelectDelete(db, pSelect);
+ /* End of the ephemeral table scan. Or, if using the onepass strategy,
+ ** jump to here if the scan visited zero rows. */
+ if( bOnePass==0 ){
+ sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
+ }else{
+ sqlite3WhereEnd(pWInfo);
+ }
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index ae40a9c09..80608ecec 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -252,7 +252,7 @@ int sqlite3_blob_open(
int j;
for(j=0; j<pIdx->nKeyCol; j++){
/* FIXME: Be smarter about indexes that use expressions */
- if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==(-2) ){
+ if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){
zFault = "indexed";
}
}
diff --git a/src/vtab.c b/src/vtab.c
index 041805ca4..cc293e806 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -937,7 +937,9 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
if( rc==SQLITE_OK ){
rc = pModule->xBegin(pVTab->pVtab);
if( rc==SQLITE_OK ){
+ int iSvpt = db->nStatement + db->nSavepoint;
addToVTrans(db, pVTab);
+ if( iSvpt ) rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, iSvpt-1);
}
}
}
diff --git a/src/where.c b/src/where.c
index 1e13cf0ed..af8e2f35f 100644
--- a/src/where.c
+++ b/src/where.c
@@ -87,6 +87,13 @@ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
*/
int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace && pWInfo->eOnePass!=ONEPASS_OFF ){
+ sqlite3DebugPrintf("%s cursors: %d %d\n",
+ pWInfo->eOnePass==ONEPASS_SINGLE ? "ONEPASS_SINGLE" : "ONEPASS_MULTI",
+ aiCur[0], aiCur[1]);
+ }
+#endif
return pWInfo->eOnePass;
}
@@ -182,13 +189,13 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
while( pScan->iEquiv<=pScan->nEquiv ){
iCur = pScan->aiCur[pScan->iEquiv-1];
iColumn = pScan->aiColumn[pScan->iEquiv-1];
- if( iColumn==(-2) && pScan->pIdxExpr==0 ) return 0;
+ if( iColumn==XN_EXPR && pScan->pIdxExpr==0 ) return 0;
while( (pWC = pScan->pWC)!=0 ){
for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){
if( pTerm->leftCursor==iCur
&& pTerm->u.leftColumn==iColumn
- && (iColumn!=(-2)
- || sqlite3ExprCompare(pTerm->pExpr->pLeft,pScan->pIdxExpr,iCur)==0)
+ && (iColumn!=XN_EXPR
+ || sqlite3ExprCompare(pTerm->pExpr->pLeft,pScan->pIdxExpr,iCur)==0)
&& (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
){
if( (pTerm->eOperator & WO_EQUIV)!=0
@@ -281,7 +288,7 @@ static WhereTerm *whereScanInit(
if( pIdx ){
j = iColumn;
iColumn = pIdx->aiColumn[j];
- if( iColumn==(-2) ) pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
+ if( iColumn==XN_EXPR ) pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
}
if( pIdx && iColumn>=0 ){
pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
@@ -720,7 +727,7 @@ static void constructAutomaticIndex(
}
}
assert( n==nKeyCol );
- pIdx->aiColumn[n] = -1;
+ pIdx->aiColumn[n] = XN_ROWID;
pIdx->azColl[n] = "BINARY";
/* Create the automatic index */
@@ -1159,6 +1166,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
** Return the affinity for a single column of an index.
*/
static char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
+ assert( iCol>=0 && iCol<pIdx->nColumn );
if( !pIdx->zColAff ){
if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
}
@@ -1216,8 +1224,7 @@ static int whereRangeSkipScanEst(
int nLower = -1;
int nUpper = p->nSample+1;
int rc = SQLITE_OK;
- int iCol = p->aiColumn[nEq];
- u8 aff = sqlite3IndexColumnAffinity(db, p, iCol);
+ u8 aff = sqlite3IndexColumnAffinity(db, p, nEq);
CollSeq *pColl;
sqlite3_value *p1 = 0; /* Value extracted from pLower */
@@ -2235,7 +2242,9 @@ static int whereLoopAddBtreeIndex(
int iCol = pProbe->aiColumn[saved_nEq];
pNew->wsFlags |= WHERE_COLUMN_EQ;
assert( saved_nEq==pNew->u.btree.nEq );
- if( iCol==(-1) || (iCol>0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) ){
+ if( iCol==XN_ROWID
+ || (iCol>0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1)
+ ){
if( iCol>=0 && pProbe->uniqNotNull==0 ){
pNew->wsFlags |= WHERE_UNQ_WANTED;
}else{
@@ -2421,18 +2430,25 @@ static int indexMightHelpWithOrderBy(
int iCursor
){
ExprList *pOB;
+ ExprList *aColExpr;
int ii, jj;
if( pIndex->bUnordered ) return 0;
if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
for(ii=0; ii<pOB->nExpr; ii++){
Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
- if( pExpr->op!=TK_COLUMN ) return 0;
- if( pExpr->iTable==iCursor ){
+ if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
}
+ }else if( (aColExpr = pIndex->aColExpr)!=0 ){
+ for(jj=0; jj<pIndex->nKeyCol; jj++){
+ if( pIndex->aiColumn[jj]!=XN_EXPR ) continue;
+ if( sqlite3ExprCompare(pExpr,aColExpr->a[jj].pExpr,iCursor)==0 ){
+ return 1;
+ }
+ }
}
}
return 0;
@@ -2580,7 +2596,7 @@ static int whereLoopAddBtree(
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
&& pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
- && HasRowid(pTab) /* Is not a WITHOUT ROWID table. (FIXME: Why not?) */
+ && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
&& !pSrc->fg.isRecursive /* Not a recursive common table expression. */
){
@@ -2825,6 +2841,7 @@ static int whereLoopAddVirtual(
pIdxInfo->orderByConsumed = 0;
pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
pIdxInfo->estimatedRows = 25;
+ pIdxInfo->idxFlags = 0;
rc = vtabBestIndex(pParse, pTab, pIdxInfo);
if( rc ) goto whereLoopAddVtab_exit;
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
@@ -2870,6 +2887,7 @@ static int whereLoopAddVirtual(
** (2) Multiple outputs from a single IN value will not merge
** together. */
pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE;
}
}
}
@@ -2885,6 +2903,14 @@ static int whereLoopAddVirtual(
pNew->rSetup = 0;
pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
+
+ /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated
+ ** that the scan will visit at most one row. Clear it otherwise. */
+ if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){
+ pNew->wsFlags |= WHERE_ONEROW;
+ }else{
+ pNew->wsFlags &= ~WHERE_ONEROW;
+ }
whereLoopInsert(pBuilder, pNew);
if( pNew->u.vtab.needFree ){
sqlite3_free(pNew->u.vtab.idxStr);
@@ -3206,7 +3232,8 @@ static i8 wherePathSatisfiesOrderBy(
nKeyCol = pIndex->nKeyCol;
nColumn = pIndex->nColumn;
assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
- assert( pIndex->aiColumn[nColumn-1]==(-1) || !HasRowid(pIndex->pTable));
+ assert( pIndex->aiColumn[nColumn-1]==XN_ROWID
+ || !HasRowid(pIndex->pTable));
isOrderDistinct = IsUniqueIndex(pIndex);
}
@@ -3238,7 +3265,7 @@ static i8 wherePathSatisfiesOrderBy(
revIdx = pIndex->aSortOrder[j];
if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
}else{
- iColumn = -1;
+ iColumn = XN_ROWID;
revIdx = 0;
}
@@ -3264,9 +3291,15 @@ static i8 wherePathSatisfiesOrderBy(
testcase( wctrlFlags & WHERE_GROUPBY );
testcase( wctrlFlags & WHERE_DISTINCTBY );
if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
- if( pOBExpr->op!=TK_COLUMN ) continue;
- if( pOBExpr->iTable!=iCur ) continue;
- if( pOBExpr->iColumn!=iColumn ) continue;
+ if( iColumn>=(-1) ){
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ if( pOBExpr->iColumn!=iColumn ) continue;
+ }else{
+ if( sqlite3ExprCompare(pOBExpr,pIndex->aColExpr->a[j].pExpr,iCur) ){
+ continue;
+ }
+ }
if( iColumn>=0 ){
pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
if( !pColl ) pColl = db->pDfltColl;
@@ -4097,7 +4130,8 @@ WhereInfo *sqlite3WhereBegin(
}
/* Construct the WhereLoop objects */
- WHERETRACE(0xffff,("*** Optimizer Start ***\n"));
+ WHERETRACE(0xffff,("*** Optimizer Start *** (wctrlFlags: 0x%x)\n",
+ wctrlFlags));
#if defined(WHERETRACE_ENABLED)
if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
int i;
@@ -4516,7 +4550,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
}else if( pLoop->wsFlags & WHERE_MULTI_OR ){
pIdx = pLevel->u.pCovidx;
}
- if( pIdx && !db->mallocFailed ){
+ if( pIdx
+ && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable))
+ && !db->mallocFailed
+ ){
last = sqlite3VdbeCurrentAddr(v);
k = pLevel->addrBody;
pOp = sqlite3VdbeGetOp(v, k);
@@ -4528,6 +4565,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
if( !HasRowid(pTab) ){
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
x = pPk->aiColumn[x];
+ assert( x>=0 );
}
x = sqlite3ColumnOfIndex(pIdx, x);
if( x>=0 ){
diff --git a/src/wherecode.c b/src/wherecode.c
index 77b8be4be..a6c45f032 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -46,8 +46,8 @@ static void explainAppendTerm(
*/
static const char *explainIndexColumnName(Index *pIdx, int i){
i = pIdx->aiColumn[i];
- if( i==(-2) ) return "<expr>";
- if( i==(-1) ) return "rowid";
+ if( i==XN_EXPR ) return "<expr>";
+ if( i==XN_ROWID ) return "rowid";
return pIdx->pTable->aCol[i].zName;
}
@@ -514,7 +514,7 @@ static int codeAllEqualityTerms(
sqlite3VdbeJumpHere(v, j);
for(j=0; j<nSkip; j++){
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j);
- testcase( pIdx->aiColumn[j]==(-2) );
+ testcase( pIdx->aiColumn[j]==XN_EXPR );
VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
}
}
@@ -700,8 +700,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
disableTerm(pLevel, pLoop->aLTerm[j]);
}
}
- pLevel->op = OP_VNext;
pLevel->p1 = iCur;
+ pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext;
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
sqlite3ExprCachePop(pParse);
diff --git a/test/delete4.test b/test/delete4.test
index 7334bf02e..f3598a949 100644
--- a/test/delete4.test
+++ b/test/delete4.test
@@ -98,5 +98,48 @@ do_execsql_test 3.4 {
PRAGMA integrity_check;
} {ok}
+# Between 2015-09-14 and 2015-09-28, the following test cases would result
+# in corruption (wrong # of entries in index) due to a bug in the ONEPASS
+# optimization.
+#
+do_execsql_test 4.1 {
+ DROP TABLE IF EXISTS t4;
+ CREATE TABLE t4(col0, col1);
+ INSERT INTO "t4" VALUES(14, 'abcde');
+ CREATE INDEX idx_t4_0 ON t4 (col1, col0);
+ CREATE INDEX idx_t4_3 ON t4 (col0);
+ DELETE FROM t4 WHERE col0=69 OR col0>7;
+ PRAGMA integrity_check;
+} {ok}
+do_execsql_test 4.2 {
+ DROP TABLE IF EXISTS t4;
+ CREATE TABLE t4(col0, col1);
+ INSERT INTO "t4" VALUES(14, 'abcde');
+ CREATE INDEX idx_t4_3 ON t4 (col0);
+ CREATE INDEX idx_t4_0 ON t4 (col1, col0);
+ DELETE FROM t4 WHERE col0=69 OR col0>7;
+ PRAGMA integrity_check;
+} {ok}
+do_execsql_test 4.11 {
+ DROP TABLE IF EXISTS t4;
+ CREATE TABLE t4(col0, col1, pk PRIMARY KEY) WITHOUT ROWID;
+ INSERT INTO t4 VALUES(14, 'abcde','xyzzy');
+ CREATE INDEX idx_t4_0 ON t4 (col1, col0);
+ CREATE INDEX idx_t4_3 ON t4 (col0);
+ DELETE FROM t4 WHERE col0=69 OR col0>7;
+ PRAGMA integrity_check;
+} {ok}
+do_execsql_test 4.12 {
+ DROP TABLE IF EXISTS t4;
+ CREATE TABLE t4(col0, col1, pk PRIMARY KEY) WITHOUT ROWID;
+ INSERT INTO t4 VALUES(14, 'abcde','xyzzy');
+ CREATE INDEX idx_t4_3 ON t4 (col0);
+ CREATE INDEX idx_t4_0 ON t4 (col1, col0);
+ DELETE FROM t4 WHERE col0=69 OR col0>7;
+ PRAGMA integrity_check;
+} {ok}
+
+
+
finish_test
diff --git a/test/fkey1.test b/test/fkey1.test
index 0bd4939eb..e10781ac5 100644
--- a/test/fkey1.test
+++ b/test/fkey1.test
@@ -151,4 +151,38 @@ do_execsql_test fkey1-4.2 {
PRAGMA table_info="""1";
} {0 {"2} TEXT 0 {} 1 1 {"3} TEXT 0 {} 0}
+#-------------------------------------------------------------------------
+#
+do_execsql_test fkey1-5.1 {
+ CREATE TABLE t11(
+ x INTEGER PRIMARY KEY,
+ parent REFERENCES t11 ON DELETE CASCADE
+ );
+ INSERT INTO t11 VALUES (1, NULL), (2, 1), (3, 2);
+} {}
+
+# The REPLACE part of this statement deletes the row (2, 1). Then the
+# DELETE CASCADE caused by deleting that row removes the (3, 2) row. Which
+# would have been the parent of the new row being inserted. Causing an
+# FK violation.
+#
+do_catchsql_test fkey1-5.2 {
+ INSERT OR REPLACE INTO t11 VALUES (2, 3);
+} {1 {FOREIGN KEY constraint failed}}
+
+# A similar test to the above.
+do_execsql_test fkey1-5.3 {
+ CREATE TABLE Foo (
+ Id INTEGER PRIMARY KEY,
+ ParentId INTEGER REFERENCES Foo(Id) ON DELETE CASCADE, C1
+ );
+ INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (1, null, 'A');
+ INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 1, 'A-2-1');
+ INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (3, 2, 'A-3-2');
+ INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (4, 3, 'A-4-3');
+}
+do_catchsql_test fkey1-5.4 {
+ INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 3, 'A-2-3');
+} {1 {FOREIGN KEY constraint failed}}
+
finish_test
diff --git a/test/fts3conf.test b/test/fts3conf.test
index e91efbefb..e157c239e 100644
--- a/test/fts3conf.test
+++ b/test/fts3conf.test
@@ -86,11 +86,11 @@ foreach {tn sql uses constraint data} [subst {
9 "INSERT OR IGNORE $T2" 1 0 {{a b c d} {e f g h} {i j k l} z}
10 "INSERT OR REPLACE $T2" 1 0 {{a b c d} y {i j k l} z}
- 11 "UPDATE OR ROLLBACK $T3" 1 1 {{a b c d} {e f g h}}
- 12 "UPDATE OR ABORT $T3" 1 1 {{a b c d} {e f g h} {i j k l}}
- 13 "UPDATE OR FAIL $T3" 1 1 {{a b c d} {e f g h} {i j k l}}
- 14 "UPDATE OR IGNORE $T3" 1 0 {{a b c d} {e f g h} {i j k l}}
- 15 "UPDATE OR REPLACE $T3" 1 0 {{a b c d} {i j k l}}
+ 11 "UPDATE OR ROLLBACK $T3" 0 1 {{a b c d} {e f g h}}
+ 12 "UPDATE OR ABORT $T3" 0 1 {{a b c d} {e f g h} {i j k l}}
+ 13 "UPDATE OR FAIL $T3" 0 1 {{a b c d} {e f g h} {i j k l}}
+ 14 "UPDATE OR IGNORE $T3" 0 0 {{a b c d} {e f g h} {i j k l}}
+ 15 "UPDATE OR REPLACE $T3" 0 0 {{a b c d} {i j k l}}
16 "UPDATE OR ROLLBACK $T4" 1 1 {{a b c d} {e f g h}}
17 "UPDATE OR ABORT $T4" 1 1 {{a b c d} {e f g h} {i j k l}}
@@ -178,4 +178,38 @@ do_execsql_test 3.8 {
SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one'
} {X'0200000002000000'}
+#-------------------------------------------------------------------------
+# Test that the xSavepoint is invoked correctly if the first write
+# operation within a transaction is to a virtual table.
+#
+do_catchsql_test 4.1.1 {
+ CREATE VIRTUAL TABLE t0 USING fts4;
+ BEGIN;
+ INSERT INTO t0(rowid, content) SELECT
+ 1, 'abc' UNION ALL SELECT
+ 2, 'def' UNION ALL SELECT
+ 1, 'ghi';
+} {1 {constraint failed}}
+do_execsql_test 4.1.2 {
+ COMMIT;
+}
+do_execsql_test 4.1.3 {
+ SELECT * FROM t0 WHERE t0 MATCH 'abc';
+ INSERT INTO t0(t0) VALUES('integrity-check');
+} {}
+
+do_execsql_test 4.2.1 {
+ CREATE VIRTUAL TABLE t01 USING fts4;
+ BEGIN;
+ SAVEPOINT abc;
+ INSERT INTO t01 VALUES('a b c');
+ ROLLBACK TO abc;
+ COMMIT;
+}
+do_execsql_test 4.2.2 {
+ SELECT * FROM t01 WHERE t01 MATCH 'b';
+ INSERT INTO t01(t01) VALUES('integrity-check');
+} {}
+
finish_test
+
diff --git a/test/fts4onepass.test b/test/fts4onepass.test
new file mode 100644
index 000000000..d290980dc
--- /dev/null
+++ b/test/fts4onepass.test
@@ -0,0 +1,147 @@
+# 2015 Sep 27
+#
+# 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
+source $testdir/fts3_common.tcl
+set ::testprefix fts4onepass
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE ft USING fts3;
+ INSERT INTO ft(rowid, content) VALUES(1, '1 2 3');
+ INSERT INTO ft(rowid, content) VALUES(2, '4 5 6');
+ INSERT INTO ft(rowid, content) VALUES(3, '7 8 9');
+}
+
+#-------------------------------------------------------------------------
+# Check that UPDATE and DELETE statements that feature "WHERE rowid=?" or
+# or "WHERE docid=?" clauses do not use statement journals. But that other
+# DELETE and UPDATE statements do.
+#
+# Note: "MATCH ? AND docid=?" does use a statement journal.
+#
+foreach {tn sql uses} {
+ 1.1 { DELETE FROM ft } 1
+ 1.2 { DELETE FROM ft WHERE docid=? } 0
+ 1.3 { DELETE FROM ft WHERE rowid=? } 0
+ 1.4 { DELETE FROM ft WHERE ft MATCH '1' } 1
+ 1.5 { DELETE FROM ft WHERE ft MATCH '1' AND docid=? } 1
+ 1.6 { DELETE FROM ft WHERE ft MATCH '1' AND rowid=? } 1
+
+ 2.1 { UPDATE ft SET content='a b c' } 1
+ 2.2 { UPDATE ft SET content='a b c' WHERE docid=? } 0
+ 2.3 { UPDATE ft SET content='a b c' WHERE rowid=? } 0
+ 2.4 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' } 1
+ 2.5 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND docid=? } 1
+ 2.6 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND rowid=? } 1
+} {
+ do_test 1.$tn { sql_uses_stmt db $sql } $uses
+}
+
+#-------------------------------------------------------------------------
+# Check that putting a "DELETE/UPDATE ... WHERE rowid=?" statement in a
+# trigger program does not prevent the VM from using a statement
+# transaction. Even if the calling statement cannot hit a constraint.
+#
+do_execsql_test 2.0 {
+ CREATE TABLE t1(x);
+
+ CREATE TRIGGER t1_ai AFTER INSERT ON t1 BEGIN
+ DELETE FROM ft WHERE rowid=new.x;
+ END;
+
+ CREATE TRIGGER t1_ad AFTER DELETE ON t1 BEGIN
+ UPDATE ft SET content = 'a b c' WHERE rowid=old.x;
+ END;
+
+ CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 BEGIN
+ DELETE FROM ft WHERE rowid=old.x;
+ END;
+}
+
+foreach {tn sql uses} {
+ 1 { INSERT INTO t1 VALUES(1) } 1
+ 2 { DELETE FROM t1 WHERE x=4 } 1
+ 3 { UPDATE t1 SET x=10 WHERE x=11 } 1
+} {
+ do_test 2.$tn { sql_uses_stmt db $sql } $uses
+}
+
+#-------------------------------------------------------------------------
+# Test that an "UPDATE ... WHERE rowid=?" works and does not corrupt the
+# index when it strikes a constraint. Both inside and outside a
+# transaction.
+#
+foreach {tn tcl1 tcl2} {
+ 1 {} {}
+
+ 2 {
+ execsql BEGIN
+ } {
+ if {[sqlite3_get_autocommit db]==1} { error "transaction rolled back!" }
+ execsql COMMIT
+ }
+} {
+
+ do_execsql_test 3.$tn.0 {
+ DROP TABLE IF EXISTS ft2;
+ CREATE VIRTUAL TABLE ft2 USING fts4;
+ INSERT INTO ft2(rowid, content) VALUES(1, 'a b c');
+ INSERT INTO ft2(rowid, content) VALUES(2, 'a b d');
+ INSERT INTO ft2(rowid, content) VALUES(3, 'a b e');
+ }
+
+ eval $tcl1
+ foreach {tn2 sql content} {
+ 1 { UPDATE ft2 SET docid=2 WHERE docid=1 }
+ { 1 {a b c} 2 {a b d} 3 {a b e} }
+
+ 2 {
+ INSERT INTO ft2(rowid, content) VALUES(4, 'a b f');
+ UPDATE ft2 SET docid=5 WHERE docid=4;
+ UPDATE ft2 SET docid=3 WHERE docid=5;
+ } { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
+
+ 3 {
+ UPDATE ft2 SET docid=3 WHERE docid=4; -- matches 0 rows
+ UPDATE ft2 SET docid=2 WHERE docid=3;
+ } { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
+
+ 4 {
+ INSERT INTO ft2(rowid, content) VALUES(4, 'a b g');
+ UPDATE ft2 SET docid=-1 WHERE docid=4;
+ UPDATE ft2 SET docid=3 WHERE docid=-1;
+ } {-1 {a b g} 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
+
+ 5 {
+ DELETE FROM ft2 WHERE rowid=451;
+ DELETE FROM ft2 WHERE rowid=-1;
+ UPDATE ft2 SET docid = 2 WHERE docid = 1;
+ } {1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
+ } {
+ do_catchsql_test 3.$tn.$tn2.a $sql {1 {constraint failed}}
+ do_execsql_test 3.$tn.$tn2.b { SELECT rowid, content FROM ft2 } $content
+ do_execsql_test 3.$tn.$tn2.c {
+ INSERT INTO ft2(ft2) VALUES('integrity-check');
+ }
+ }
+ eval $tcl2
+}
+
+finish_test
+
diff --git a/test/fuzzdata3.db b/test/fuzzdata3.db
index 4ca7a2357..0a41fd6e3 100644
--- a/test/fuzzdata3.db
+++ b/test/fuzzdata3.db
Binary files differ
diff --git a/test/indexexpr1.test b/test/indexexpr1.test
index 1945059f2..89bea1877 100644
--- a/test/indexexpr1.test
+++ b/test/indexexpr1.test
@@ -88,6 +88,23 @@ do_execsql_test indexexpr1-160eqp {
WHERE substr(a,27,3)=='ord' AND d>=29;
} {/USING INDEX t1a2/}
+# ORDER BY using an indexed expression
+#
+do_execsql_test indexexpr1-170 {
+ CREATE INDEX t1alen ON t1(length(a));
+ SELECT length(a) FROM t1 ORDER BY length(a);
+} {20 25 27 29 38 52}
+do_execsql_test indexexpr1-170eqp {
+ EXPLAIN QUERY PLAN
+ SELECT length(a) FROM t1 ORDER BY length(a);
+} {/SCAN TABLE t1 USING INDEX t1alen/}
+do_execsql_test indexexpr1-171 {
+ SELECT length(a) FROM t1 ORDER BY length(a) DESC;
+} {52 38 29 27 25 20}
+do_execsql_test indexexpr1-171eqp {
+ EXPLAIN QUERY PLAN
+ SELECT length(a) FROM t1 ORDER BY length(a) DESC;
+} {/SCAN TABLE t1 USING INDEX t1alen/}
do_execsql_test indexexpr1-200 {
DROP TABLE t1;
@@ -193,7 +210,8 @@ do_execsql_test indexexpr1-400 {
SELECT x, printf('ab%04xyz',x), random() FROM c;
CREATE UNIQUE INDEX t3abc ON t3(CAST(a AS text), b, substr(c,1,3));
SELECT a FROM t3 WHERE CAST(a AS text)<='10' ORDER BY +a;
-} {1 10}
+ PRAGMA integrity_check;
+} {1 10 ok}
do_catchsql_test indexexpr1-410 {
INSERT INTO t3 SELECT * FROM t3 WHERE rowid=10;
} {1 {UNIQUE constraint failed: index 't3abc'}}
@@ -255,4 +273,39 @@ do_execsql_test indexexpr1-710 {
ORDER BY +a, +x;
} {1 1 | 2 2 |}
+# Collating sequences on indexes of expressions
+#
+do_execsql_test indexexpr1-800 {
+ DROP TABLE IF EXISTS t8;
+ CREATE TABLE t8(a INTEGER PRIMARY KEY, b TEXT);
+ CREATE UNIQUE INDEX t8bx ON t8(substr(b,2,4) COLLATE nocase);
+ INSERT INTO t8(a,b) VALUES(1,'Alice'),(2,'Bartholemew'),(3,'Cynthia');
+ SELECT * FROM t8 WHERE substr(b,2,4)='ARTH' COLLATE nocase;
+} {2 Bartholemew}
+do_catchsql_test indexexpr1-810 {
+ INSERT INTO t8(a,b) VALUES(4,'BARTHMERE');
+} {1 {UNIQUE constraint failed: index 't8bx'}}
+do_catchsql_test indexexpr1-820 {
+ DROP INDEX t8bx;
+ CREATE UNIQUE INDEX t8bx ON t8(substr(b,2,4) COLLATE rtrim);
+ INSERT INTO t8(a,b) VALUES(4,'BARTHMERE');
+} {0 {}}
+
+# Check that PRAGMA integrity_check works correctly on a
+# UNIQUE index that includes rowid and expression terms.
+#
+do_execsql_test indexexpr1-900 {
+ CREATE TABLE t9(a,b,c,d);
+ CREATE UNIQUE INDEX t9x1 ON t9(c,abs(d),b);
+ INSERT INTO t9(rowid,a,b,c,d) VALUES(1,2,3,4,5);
+ INSERT INTO t9(rowid,a,b,c,d) VALUES(2,NULL,NULL,NULL,NULL);
+ INSERT INTO t9(rowid,a,b,c,d) VALUES(3,NULL,NULL,NULL,NULL);
+ INSERT INTO t9(rowid,a,b,c,d) VALUES(4,5,6,7,8);
+ PRAGMA integrity_check;
+} {ok}
+do_catchsql_test indexexpr1-910 {
+ INSERT INTO t9(a,b,c,d) VALUES(5,6,7,-8);
+} {1 {UNIQUE constraint failed: index 't9x1'}}
+
+
finish_test
diff --git a/test/view.test b/test/view.test
index b39487689..0b8354a38 100644
--- a/test/view.test
+++ b/test/view.test
@@ -161,6 +161,16 @@ do_test view-3.3.3 {
do_catchsql_test view-3.3.4 {
CREATE VIEW v1err(x,y DESC,z) AS SELECT a, b+c, c-b FROM t1;
} {1 {syntax error after column name "y"}}
+do_catchsql_test view-3.3.5 {
+ DROP VIEW IF EXISTS v1err;
+ CREATE VIEW v1err(x,y) AS SELECT a, b+c, c-b FROM t1;
+ SELECT * FROM v1err;
+} {1 {expected 2 columns for 'v1err' but got 3}}
+do_catchsql_test view-3.3.6 {
+ DROP VIEW IF EXISTS v1err;
+ CREATE VIEW v1err(w,x,y,z) AS SELECT a, b+c, c-b FROM t1;
+ SELECT * FROM v1err;
+} {1 {expected 4 columns for 'v1err' but got 3}}
ifcapable compound {
do_test view-3.4 {
@@ -464,6 +474,11 @@ do_test view-12.1 {
CREATE VIEW v12 AS SELECT a FROM t1 WHERE b=?
}
} {1 {parameters are not allowed in views}}
+do_test view-12.2 {
+ catchsql {
+ CREATE VIEW v12(x) AS SELECT a FROM t1 WHERE b=?
+ }
+} {1 {parameters are not allowed in views}}
ifcapable attach {
do_test view-13.1 {