mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-10 01:02:56 +03:00
Merge recent enhancements from trunk, and especially the fix for
ticket [1b266395d6bc10]. FossilOrigin-Name: b2face9aa95ade96a5666c70b6b31064c1ad0977
This commit is contained 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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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++;
|
||||
|
@@ -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
|
||||
|
@@ -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 );
|
||||
|
@@ -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]);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
5
main.mk
5
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
|
||||
|
79
manifest
79
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
|
||||
|
@@ -1 +1 @@
|
||||
c91065f8edb1e54076791716fc20d3fcfe3070dc
|
||||
b2face9aa95ade96a5666c70b6b31064c1ad0977
|
@@ -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 ==
|
||||
|
13
src/build.c
13
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);
|
||||
|
12
src/delete.c
12
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;
|
||||
|
12
src/expr.c
12
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
11
src/fkey.c
11
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;
|
||||
|
||||
|
14
src/insert.c
14
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 ){
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
48
src/select.c
48
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. */
|
||||
|
57
src/shell.c
57
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++;
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( nRow>0 ){
|
||||
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
|
||||
rc = shellDatabaseError(p->db);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
@@ -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,8 +5686,15 @@ 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
|
||||
**
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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{
|
||||
|
189
src/update.c
189
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.
|
||||
** Then loop through the contents of this ephemeral table executing a
|
||||
** VUpdate for each row. When finished, drop the ephemeral table.
|
||||
**
|
||||
** 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;
|
||||
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 */
|
||||
|
||||
/* Construct the SELECT statement that will find the new values for
|
||||
** all updated rows.
|
||||
*/
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_"));
|
||||
if( pRowid ){
|
||||
pEList = sqlite3ExprListAppend(pParse, pEList,
|
||||
sqlite3ExprDup(db, pRowid, 0));
|
||||
}
|
||||
assert( pTab->iPKey<0 );
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]>=0 ){
|
||||
pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
|
||||
}else{
|
||||
pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName);
|
||||
}
|
||||
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.
|
||||
*/
|
||||
/* 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;
|
||||
|
||||
/* fill the ephemeral table
|
||||
*/
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, ephemTab);
|
||||
sqlite3Select(pParse, pSelect, &dest);
|
||||
/* Start scanning the virtual table */
|
||||
pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
|
||||
if( pWInfo==0 ) return;
|
||||
|
||||
/* 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);
|
||||
/* Populate the argument registers. */
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
||||
if( pRowid ){
|
||||
sqlite3ExprCode(pParse, pRowid, regArg+1);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
||||
}
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i);
|
||||
if( aXRef[i]>=0 ){
|
||||
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+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 */
|
||||
|
@@ -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";
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
74
src/where.c
74
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 ){
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
147
test/fts4onepass.test
Normal file
147
test/fts4onepass.test
Normal file
@@ -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
|
||||
|
Binary file not shown.
@@ -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
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user