aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordan <dan@noemail.net>2017-04-20 17:35:46 +0000
committerdan <dan@noemail.net>2017-04-20 17:35:46 +0000
commitc42a0056d7817f629d78de5d285e2df189ceb10e (patch)
tree80269bfa40a3837a9b99fe062257f9779217df4a
parentac33c0343f54bca37411b9f4b11115f7a583b241 (diff)
parentdc62daca866006c4afa4c32c405d1a327dc7334f (diff)
downloadsqlite-c42a0056d7817f629d78de5d285e2df189ceb10e.tar.gz
sqlite-c42a0056d7817f629d78de5d285e2df189ceb10e.zip
Merge latest trunk changes into this branch.
FossilOrigin-Name: b1533bc455f52f570c0f4b8aaa0da802757dc89b0e45b9a9b31aa591a44bf7bd
-rw-r--r--ext/expert/sqlite3expert.c10
-rw-r--r--ext/fts3/fts3.c280
-rw-r--r--ext/misc/anycollseq.c58
-rw-r--r--manifest51
-rw-r--r--manifest.uuid2
-rw-r--r--src/expr.c11
-rw-r--r--src/parse.y34
-rw-r--r--src/pragma.c52
-rw-r--r--src/select.c62
-rw-r--r--src/shell.c50
-rw-r--r--src/treeview.c5
-rw-r--r--src/vdbe.c23
-rw-r--r--src/vdbemem.c4
-rw-r--r--src/where.c2
-rw-r--r--test/fkey5.test35
-rw-r--r--test/fts3aa.test6
-rw-r--r--test/fts3corrupt3.test65
-rw-r--r--test/fts3fault.test12
-rw-r--r--test/fts3fault2.test72
-rw-r--r--test/fts3misc.test228
-rw-r--r--test/fts4langid.test8
-rw-r--r--test/permutations.test2
-rw-r--r--tool/addopcodes.tcl1
-rw-r--r--tool/showwal.c9
24 files changed, 824 insertions, 258 deletions
diff --git a/ext/expert/sqlite3expert.c b/ext/expert/sqlite3expert.c
index 3ee74b571..8cacc9d70 100644
--- a/ext/expert/sqlite3expert.c
+++ b/ext/expert/sqlite3expert.c
@@ -28,9 +28,13 @@ typedef struct IdxStatement IdxStatement;
typedef struct IdxTable IdxTable;
typedef struct IdxWrite IdxWrite;
+/*
+** A temp table name that we assume no user database will actually use.
+** If this assumption proves incorrect triggers on the table with the
+** conflicting name will be ignored.
+*/
#define UNIQUE_TABLE_NAME "t592690916721053953805701627921227776"
-
/*
** A single constraint. Equivalent to either "col = ?" or "col < ?" (or
** any other type of single-ended range constraint on a column).
@@ -238,6 +242,10 @@ static int idxHashAdd(
return 0;
}
+/*
+** If zKey/nKey is present in the hash table, return a pointer to the
+** hash-entry object.
+*/
static IdxHashEntry *idxHashFind(IdxHash *pHash, const char *zKey, int nKey){
int iHash;
IdxHashEntry *pEntry;
diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c
index 6f059bc95..26b0b00bc 100644
--- a/ext/fts3/fts3.c
+++ b/ext/fts3/fts3.c
@@ -372,8 +372,8 @@ int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
}
/*
-** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to a
-** 32-bit integer before it is returned.
+** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to
+** a non-negative 32-bit integer before it is returned.
*/
int sqlite3Fts3GetVarint32(const char *p, int *pi){
u32 a;
@@ -389,7 +389,9 @@ int sqlite3Fts3GetVarint32(const char *p, int *pi){
GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *pi, 3);
GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *pi, 4);
a = (a & 0x0FFFFFFF );
- *pi = (int)(a | ((u32)(*p & 0x0F) << 28));
+ *pi = (int)(a | ((u32)(*p & 0x07) << 28));
+ assert( 0==(a & 0x80000000) );
+ assert( *pi>=0 );
return 5;
}
@@ -1219,65 +1221,66 @@ static int fts3InitVtab(
break;
}
}
- if( iOpt==SizeofArray(aFts4Opt) ){
- sqlite3Fts3ErrMsg(pzErr, "unrecognized parameter: %s", z);
- rc = SQLITE_ERROR;
- }else{
- switch( iOpt ){
- case 0: /* MATCHINFO */
- if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){
- sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo: %s", zVal);
- rc = SQLITE_ERROR;
- }
- bNoDocsize = 1;
- break;
-
- case 1: /* PREFIX */
- sqlite3_free(zPrefix);
- zPrefix = zVal;
- zVal = 0;
- break;
-
- case 2: /* COMPRESS */
- sqlite3_free(zCompress);
- zCompress = zVal;
- zVal = 0;
- break;
-
- case 3: /* UNCOMPRESS */
- sqlite3_free(zUncompress);
- zUncompress = zVal;
- zVal = 0;
- break;
-
- case 4: /* ORDER */
- if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
- && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4))
- ){
- sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal);
- rc = SQLITE_ERROR;
- }
- bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
- break;
-
- case 5: /* CONTENT */
- sqlite3_free(zContent);
- zContent = zVal;
- zVal = 0;
- break;
-
- case 6: /* LANGUAGEID */
- assert( iOpt==6 );
- sqlite3_free(zLanguageid);
- zLanguageid = zVal;
- zVal = 0;
- break;
-
- case 7: /* NOTINDEXED */
- azNotindexed[nNotindexed++] = zVal;
- zVal = 0;
- break;
- }
+ switch( iOpt ){
+ case 0: /* MATCHINFO */
+ if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo: %s", zVal);
+ rc = SQLITE_ERROR;
+ }
+ bNoDocsize = 1;
+ break;
+
+ case 1: /* PREFIX */
+ sqlite3_free(zPrefix);
+ zPrefix = zVal;
+ zVal = 0;
+ break;
+
+ case 2: /* COMPRESS */
+ sqlite3_free(zCompress);
+ zCompress = zVal;
+ zVal = 0;
+ break;
+
+ case 3: /* UNCOMPRESS */
+ sqlite3_free(zUncompress);
+ zUncompress = zVal;
+ zVal = 0;
+ break;
+
+ case 4: /* ORDER */
+ if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
+ && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4))
+ ){
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal);
+ rc = SQLITE_ERROR;
+ }
+ bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
+ break;
+
+ case 5: /* CONTENT */
+ sqlite3_free(zContent);
+ zContent = zVal;
+ zVal = 0;
+ break;
+
+ case 6: /* LANGUAGEID */
+ assert( iOpt==6 );
+ sqlite3_free(zLanguageid);
+ zLanguageid = zVal;
+ zVal = 0;
+ break;
+
+ case 7: /* NOTINDEXED */
+ azNotindexed[nNotindexed++] = zVal;
+ zVal = 0;
+ break;
+
+ default:
+ assert( iOpt==SizeofArray(aFts4Opt) );
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized parameter: %s", z);
+ rc = SQLITE_ERROR;
+ break;
}
sqlite3_free(zVal);
}
@@ -1846,7 +1849,8 @@ static int fts3ScanInteriorNode(
isFirstTerm = 0;
zCsr += fts3GetVarint32(zCsr, &nSuffix);
- if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){
+ assert( nPrefix>=0 && nSuffix>=0 );
+ if( &zCsr[nSuffix]>zEnd ){
rc = FTS_CORRUPT_VTAB;
goto finish_scan;
}
@@ -2656,7 +2660,7 @@ int sqlite3Fts3FirstFilter(
fts3ColumnlistCopy(0, &p);
}
- while( p<pEnd && *p==0x01 ){
+ while( p<pEnd ){
sqlite3_int64 iCol;
p++;
p += sqlite3Fts3GetVarint(p, &iCol);
@@ -3336,33 +3340,38 @@ static int fts3ColumnMethod(
/* The column value supplied by SQLite must be in range. */
assert( iCol>=0 && iCol<=p->nColumn+2 );
- if( iCol==p->nColumn+1 ){
- /* This call is a request for the "docid" column. Since "docid" is an
- ** alias for "rowid", use the xRowid() method to obtain the value.
- */
- sqlite3_result_int64(pCtx, pCsr->iPrevId);
- }else if( iCol==p->nColumn ){
- /* The extra column whose name is the same as the table.
- ** Return a blob which is a pointer to the cursor. */
- sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
- }else if( iCol==p->nColumn+2 && pCsr->pExpr ){
- sqlite3_result_int64(pCtx, pCsr->iLangid);
- }else{
- /* The requested column is either a user column (one that contains
- ** indexed data), or the language-id column. */
- rc = fts3CursorSeek(0, pCsr);
+ switch( iCol-p->nColumn ){
+ case 0:
+ /* The special 'table-name' column */
+ sqlite3_result_blob(pCtx, &pCsr, sizeof(Fts3Cursor*), SQLITE_TRANSIENT);
+ sqlite3_result_subtype(pCtx, SQLITE_BLOB);
+ break;
- if( rc==SQLITE_OK ){
- if( iCol==p->nColumn+2 ){
- int iLangid = 0;
- if( p->zLanguageid ){
- iLangid = sqlite3_column_int(pCsr->pStmt, p->nColumn+1);
- }
- sqlite3_result_int(pCtx, iLangid);
- }else if( sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){
+ case 1:
+ /* The docid column */
+ sqlite3_result_int64(pCtx, pCsr->iPrevId);
+ break;
+
+ case 2:
+ if( pCsr->pExpr ){
+ sqlite3_result_int64(pCtx, pCsr->iLangid);
+ break;
+ }else if( p->zLanguageid==0 ){
+ sqlite3_result_int(pCtx, 0);
+ break;
+ }else{
+ iCol = p->nColumn;
+ /* fall-through */
+ }
+
+ default:
+ /* A user column. Or, if this is a full-table scan, possibly the
+ ** language-id column. Seek the cursor. */
+ rc = fts3CursorSeek(0, pCsr);
+ if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)-1>iCol ){
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
- }
+ break;
}
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
@@ -3442,17 +3451,11 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
static int fts3SetHasStat(Fts3Table *p){
int rc = SQLITE_OK;
if( p->bHasStat==2 ){
- const char *zFmt ="SELECT 1 FROM %Q.sqlite_master WHERE tbl_name='%q_stat'";
- char *zSql = sqlite3_mprintf(zFmt, p->zDb, p->zName);
- if( zSql ){
- sqlite3_stmt *pStmt = 0;
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- if( rc==SQLITE_OK ){
- int bHasStat = (sqlite3_step(pStmt)==SQLITE_ROW);
- rc = sqlite3_finalize(pStmt);
- if( rc==SQLITE_OK ) p->bHasStat = (u8)bHasStat;
- }
- sqlite3_free(zSql);
+ char *zTbl = sqlite3_mprintf("%s_stat", p->zName);
+ if( zTbl ){
+ int res = sqlite3_table_column_metadata(p->db, p->zDb, zTbl, 0,0,0,0,0,0);
+ sqlite3_free(zTbl);
+ p->bHasStat = (res==SQLITE_OK);
}else{
rc = SQLITE_NOMEM;
}
@@ -3559,18 +3562,16 @@ static int fts3FunctionArg(
sqlite3_value *pVal, /* argv[0] passed to function */
Fts3Cursor **ppCsr /* OUT: Store cursor handle here */
){
- Fts3Cursor *pRet;
- if( sqlite3_value_type(pVal)!=SQLITE_BLOB
- || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
- ){
+ int rc = SQLITE_OK;
+ if( sqlite3_value_subtype(pVal)==SQLITE_BLOB ){
+ *ppCsr = *(Fts3Cursor**)sqlite3_value_blob(pVal);
+ }else{
char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
sqlite3_result_error(pContext, zErr, -1);
sqlite3_free(zErr);
- return SQLITE_ERROR;
+ rc = SQLITE_ERROR;
}
- memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *));
- *ppCsr = pRet;
- return SQLITE_OK;
+ return rc;
}
/*
@@ -3957,7 +3958,7 @@ int sqlite3Fts3Init(sqlite3 *db){
#endif
/* Create the virtual table wrapper around the hash-table and overload
- ** the two scalar functions. If this is successful, register the
+ ** the four scalar functions. If this is successful, register the
** module with sqlite.
*/
if( SQLITE_OK==rc
@@ -4540,7 +4541,7 @@ static int fts3EvalIncrPhraseNext(
** one incremental token. In which case the bIncr flag is set. */
assert( p->bIncr==1 );
- if( p->nToken==1 && p->bIncr ){
+ if( p->nToken==1 ){
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
@@ -4773,6 +4774,7 @@ static void fts3EvalTokenCosts(
** the number of overflow pages consumed by a record B bytes in size.
*/
static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
+ int rc = SQLITE_OK;
if( pCsr->nRowAvg==0 ){
/* The average document size, which is required to calculate the cost
** of each doclist, has not yet been determined. Read the required
@@ -4812,11 +4814,10 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz);
assert( pCsr->nRowAvg>0 );
rc = sqlite3_reset(pStmt);
- if( rc!=SQLITE_OK ) return rc;
}
*pnPage = pCsr->nRowAvg;
- return SQLITE_OK;
+ return rc;
}
/*
@@ -5166,7 +5167,8 @@ static void fts3EvalNextRow(
pExpr->iDocid = pLeft->iDocid;
pExpr->bEof = (pLeft->bEof || pRight->bEof);
if( pExpr->eType==FTSQUERY_NEAR && pExpr->bEof ){
- if( pRight->pPhrase && pRight->pPhrase->doclist.aAll ){
+ assert( pRight->eType==FTSQUERY_PHRASE );
+ if( pRight->pPhrase->doclist.aAll ){
Fts3Doclist *pDl = &pRight->pPhrase->doclist;
while( *pRc==SQLITE_OK && pRight->bEof==0 ){
memset(pDl->pList, 0, pDl->nList);
@@ -5195,7 +5197,7 @@ static void fts3EvalNextRow(
if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){
fts3EvalNextRow(pCsr, pLeft, pRc);
- }else if( pLeft->bEof || (pRight->bEof==0 && iCmp>0) ){
+ }else if( pLeft->bEof || iCmp>0 ){
fts3EvalNextRow(pCsr, pRight, pRc);
}else{
fts3EvalNextRow(pCsr, pLeft, pRc);
@@ -5287,7 +5289,6 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
*/
if( *pRc==SQLITE_OK
&& pExpr->eType==FTSQUERY_NEAR
- && pExpr->bEof==0
&& (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
){
Fts3Expr *p;
@@ -5296,42 +5297,39 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
/* Allocate temporary working space. */
for(p=pExpr; p->pLeft; p=p->pLeft){
+ assert( p->pRight->pPhrase->doclist.nList>0 );
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
- if( nTmp==0 ){
+ aTmp = sqlite3_malloc(nTmp*2);
+ if( !aTmp ){
+ *pRc = SQLITE_NOMEM;
res = 0;
}else{
- aTmp = sqlite3_malloc(nTmp*2);
- if( !aTmp ){
- *pRc = SQLITE_NOMEM;
- res = 0;
- }else{
- char *aPoslist = p->pPhrase->doclist.pList;
- int nToken = p->pPhrase->nToken;
+ char *aPoslist = p->pPhrase->doclist.pList;
+ int nToken = p->pPhrase->nToken;
- for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){
- Fts3Phrase *pPhrase = p->pRight->pPhrase;
- int nNear = p->nNear;
- res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
- }
-
- aPoslist = pExpr->pRight->pPhrase->doclist.pList;
- nToken = pExpr->pRight->pPhrase->nToken;
- for(p=pExpr->pLeft; p && res; p=p->pLeft){
- int nNear;
- Fts3Phrase *pPhrase;
- assert( p->pParent && p->pParent->pLeft==p );
- nNear = p->pParent->nNear;
- pPhrase = (
- p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase
- );
- res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
- }
+ for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){
+ Fts3Phrase *pPhrase = p->pRight->pPhrase;
+ int nNear = p->nNear;
+ res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
}
- sqlite3_free(aTmp);
+ aPoslist = pExpr->pRight->pPhrase->doclist.pList;
+ nToken = pExpr->pRight->pPhrase->nToken;
+ for(p=pExpr->pLeft; p && res; p=p->pLeft){
+ int nNear;
+ Fts3Phrase *pPhrase;
+ assert( p->pParent && p->pParent->pLeft==p );
+ nNear = p->pParent->nNear;
+ pPhrase = (
+ p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase
+ );
+ res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
+ }
}
+
+ sqlite3_free(aTmp);
}
return res;
diff --git a/ext/misc/anycollseq.c b/ext/misc/anycollseq.c
new file mode 100644
index 000000000..27b7049d5
--- /dev/null
+++ b/ext/misc/anycollseq.c
@@ -0,0 +1,58 @@
+/*
+** 2017-04-16
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file implements a run-time loadable extension to SQLite that
+** registers a sqlite3_collation_needed() callback to register a fake
+** collating function for any unknown collating sequence. The fake
+** collating function works like BINARY.
+**
+** This extension can be used to load schemas that contain one or more
+** unknown collating sequences.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <string.h>
+
+static int anyCollFunc(
+ void *NotUsed,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ int rc, n;
+ n = nKey1<nKey2 ? nKey1 : nKey2;
+ rc = memcmp(pKey1, pKey2, n);
+ if( rc==0 ) rc = nKey1 - nKey2;
+ return rc;
+}
+
+static void anyCollNeeded(
+ void *NotUsed,
+ sqlite3 *db,
+ int eTextRep,
+ const char *zCollName
+){
+ sqlite3_create_collation(db, zCollName, eTextRep, 0, anyCollFunc);
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_anycollseq_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ rc = sqlite3_collation_needed(db, 0, anyCollNeeded);
+ return rc;
+}
diff --git a/manifest b/manifest
index 81277ab60..66b5295d6 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Avoid\screating\sa\stemp\stable\sin\sthe\suser\sdatabase\sin\sthe\ssqlite3_expert\scode.
-D 2017-04-20T17:03:32.652
+C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
+D 2017-04-20T17:35:46.403
F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 6a8c838220f7c00820e1fc0ac1bccaaa8e5676067e1dbfa1bafa7a4ffecf8ae6
@@ -43,7 +43,7 @@ F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
F ext/expert/README.md 9f15075ec5ad772808eff55ef044c31140fd1146aa0a3c47eafd155e71851b01
F ext/expert/expert.c 33842ef151d84c5f8000f9c7b938998c6b999eaef7ce1f4eeb0df8ffe6739496
F ext/expert/expert1.test 1033e43071b69dc2f4e88fbf03fc7f18846c9865cac14f28c80f581437f09acb
-F ext/expert/sqlite3expert.c fde366d8c1b1970b2c18196ca2e64d01c2106bd9431c371a26e8d5b79f37f90b
+F ext/expert/sqlite3expert.c 4bc1820a70d68b478884a26a2215df8c1f2d44fb40d9cd8c47d2046c8ce0c8bc
F ext/expert/sqlite3expert.h af6354f8ee5c9e025024e63fec3bd640a802afcc3099a44d804752cf0791d811
F ext/expert/test_expert.c b01a5115f9444a9b416582c985138f5dfdb279848ce8b7452be383530be27f01
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
@@ -76,7 +76,7 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts3/fts3.c f0d5de1bc2155ba7cd7c0c1a751779a3a8857fa34d5c12f3b233a23fa2e79ea2
+F ext/fts3/fts3.c 10fc22119e3d91997eb5820d96ff709ca7c61b6f767e09b360b986b897ad74c6
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h eb2502000148e80913b965db3e59f29251266d0a
F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1
@@ -215,6 +215,7 @@ F ext/icu/icu.c 84900472a088a3a172c6c079f58a1d3a1952c332
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/misc/README.md 8e008c8d2b02e09096b31dfba033253ac27c6c06a18aa5826e299fa7601d90b2
F ext/misc/amatch.c 211108e201105e4bb0c076527b8cfd34330fc234
+F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
F ext/misc/carray.c 40c27641010a4dc67e3690bdb7c9d36ca58b3c2d
F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
@@ -360,7 +361,7 @@ F src/ctime.c 47d91a25ad8f199a71a5b1b7b169d6dd0d6e98c5719eca801568798743d1161c
F src/date.c ee676e7694dfadbdd2fde1a258a71be8360ba5ae
F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
F src/delete.c 0d9d5549d42e79ce4d82ff1db1e6c81e36d2f67c
-F src/expr.c f6914d6d06c9a1e488f49cd51a0ef12d8073231e9c1a25c4c821262686cadcbf
+F src/expr.c f10e35dc50be4c8f82eb99bf5d8530229d1d60957cc3c9473ffe584d0444087c
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c db65492ae549c3b548c9ef1f279ce1684f1c473b116e1c56a90878cd5dcf968d
F src/func.c 9d52522cc8ae7f5cdadfe14594262f1618bc1f86083c4cd6da861b4cf5af6174
@@ -396,19 +397,19 @@ F src/os_win.c 2a6c73eef01c51a048cc4ddccd57f981afbec18a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c ff1232b3088a39806035ecfac4fffeb22717d80b
F src/pager.h f2a99646c5533ffe11afa43e9e0bea74054e4efa
-F src/parse.y 48b03113704ee8bd78ee6996d81de7fbee22e105
+F src/parse.y 0513387ce02fea97897d8caef82d45f347818593f24f1bdc48e0c530a8af122d
F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
F src/pcache1.c 1195a21fe28e223e024f900b2011e80df53793f0356a24caace4188b098540dc
-F src/pragma.c 150821702fc90694b46c3432c1402fc970a4c5b8595cb13c21aeb568f9a78fc3
+F src/pragma.c 7fef375edafdb7ae9ba938b992aa726e18bf07b0599cfed040a088a262744b7a
F src/pragma.h 37a1311d0388db480388d7ec09054f7103045eff20d4971f8a433b77f40b9921
F src/prepare.c 7c46b5c7be9e19a1bf87777f0b7f9fb257b5ff9856c46de49f2354acfbeb4c86
F src/printf.c 8757834f1b54dae512fb25eb1acc8e94a0d15dd2290b58f2563f65973265adb2
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 3e518b962d932a997fae373366880fc028c75706
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c 4588dcfb0fa430012247a209ba08e17904dd32ec7690e9cb6c85e0ef012b0518
-F src/shell.c 70f4957b988572315e97c56941fdc81fd35907fee36b7b2e7be5ec4c7e9d065d
+F src/select.c bf8ab605e49717c222136380453cfb7eda564f8e500d5ff6a01341ea59fefe80
+F src/shell.c 21b79c0e1b93f8e35fd7b4087d6ba438326c3d7e285d0dd51dfd741475f858a1
F src/sqlite.h.in 900a07463a87be50b9954817f4c24a0660b4c4ddc1bfe83dedea484c6ac98425
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28
@@ -468,19 +469,19 @@ F src/test_windirent.h 5d67483a55442e31e1bde0f4a230e6e932ad5906
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
-F src/treeview.c b92d57c1ac59f4a3f6b189506921a2b48098f6f4d6afd0b715bc2815ef6af092
+F src/treeview.c 6cf8d7fe9e63fae57dad1bb57f6615e14eac0c527e43d868e805042cae8ed1f7
F src/trigger.c 134b8e7b61317ab7b2a2dd12eb1b9aa2e23ac5bc4a05e63e35b3609b6b30a7c0
F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
F src/util.c ca8440ede81e155d15cff7c101654f60b55a9ae6
F src/vacuum.c 1fe4555cd8c9b263afb85b5b4ee3a4a4181ad569
-F src/vdbe.c 314f0a70ddc29e63f131dfbe6f53c277875d3028cdf5654a709e21cab39fe0c9
+F src/vdbe.c 857fd5fe839b2ce4b999f4c0e17106963f0cb96f5e8ba20ebb22701267c09af2
F src/vdbe.h f7d1456e28875c2dcb964056589b5b7149ab7edf39edeca801596a39bb3d3848
F src/vdbeInt.h c070bc5c8b913bda0ceaa995cd4d939ded5e4fc96cf7c3c1c602d41b871f8ade
F src/vdbeapi.c 5b08d82592bcff4470601fe78aaabebd50837860
F src/vdbeaux.c 526b617ac6b5e167a6bd581e067f1ee1dbcb06e7802cff46b76fb1c02ed7d34e
F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
-F src/vdbemem.c 3122f5a21064198c10ee1b4686937aab27d5395712d9af905b7fa1affc47a453
+F src/vdbemem.c 2c70f8f5de6c71fb99a22c5b83be9fab5c47cdd8e279fa44a8c00cfed06d7e89
F src/vdbesort.c e72fe02a2121386ba767ede8942e9450878b8fc873abf3d1b6824485f092570c
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
F src/vtab.c 007513c2ef52472fcdea6a741683d50662e82790
@@ -488,7 +489,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
F src/walker.c b71a992b413b3a022572eccf29ef4b4890223791
-F src/where.c d22a2ae7f823f5405e5d00c3397204ce1272d53bc3f2fc0c516315f143eb1a24
+F src/where.c a12df4e97aec78ec37e3aa92eea6d889ef3cd9ce5cfe6b682bf1243c743d897c
F src/whereInt.h 2a4b634d63ce488b46d4b0da8f2eaa8f9aeab202bc25ef76f007de5e3fba1f20
F src/wherecode.c 943e32e9dccd0af802e0683ae11071c8bd808364e5908a5fb66758bd404c8681
F src/whereexpr.c e913aaa7b73ffcce66abcea5f197e2c538d48b5df78d0b7bba8ff4d73cc2e745
@@ -713,7 +714,7 @@ F test/fkey1.test ba64806ff9a04eecab2679caad377ae99a5e94e4
F test/fkey2.test 155809016fad6b2a1491facf2ac53a551bc57c2c
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
-F test/fkey5.test 5a373303f201ac03c22ba1ef17a733d3f56e611a
+F test/fkey5.test 19a9b73bafd78323c37fc883dba060191541503d6094f8aea67fe94d33118e20
F test/fkey6.test d078a1e323a740062bed38df32b8a736fd320dc0
F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13
F test/fkey8.test e5372e32cdb4481f121ec3550703eeb7b4e0762c
@@ -757,7 +758,7 @@ F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e
F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
F test/fts3_common.tcl 99cf6659b87c0f74f55963c2aea03b3a7d66ceb0
-F test/fts3aa.test 6c263a6f8845205ee02550981a94c2e8dc1e7058
+F test/fts3aa.test 39b65c11913d277c91d7426c62cfc1d147d1b4e9a48fecd9e38f60d0b5a5f505
F test/fts3ab.test 7f6cf260ae80dda064023df8e8e503e9a412b91f
F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63
F test/fts3ad.test e40570cb6f74f059129ad48bcef3d7cbc20dda49
@@ -782,6 +783,7 @@ F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
F test/fts3conf.test 60317efd562080e198b5bdc9fcd222ce32cf01d7
F test/fts3corrupt.test 2710b77983cc7789295ddbffea52c1d3b7506dbb
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
+F test/fts3corrupt3.test 56e0ee83e90b57f5f3644cb7d1b36a067b7b8b19cdf0dedce45e5e13cf752f65
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
F test/fts3d.test d3e9c8fb75135ada06bf3bab4f9666224965d708
F test/fts3defer.test 0be4440b73a2e651fc1e472066686d6ada4b9963
@@ -794,12 +796,13 @@ F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8
F test/fts3expr4.test c39a15d676b14fc439d9bf845aa7bddcf4a74dc3
F test/fts3expr5.test f9abfffbf5e53d48a33e12a1e8f8ba2c551c9b49
-F test/fts3fault.test da49627b280b210ebc6657f76344c7851f10ce66
-F test/fts3fault2.test f953bb3cf903988172270a9a0aafd5a890b0f98f
+F test/fts3fault.test 268e9589f44f49d6694ef39a293f0e0f80c6230fb01cc6f34325412acceb99ae
+F test/fts3fault2.test 536bbe01fe2946ec24b063a5eee813e8fd90354a6ca0b8f941d299c405edd17e
F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641
F test/fts3join.test 34750f3ce1e29b2749eaf0f1be2fa6301c5d50da
F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6
F test/fts3matchinfo.test ce864e0bd92429df8008f31cf557269ba172482a
+F test/fts3misc.test f481128013b9555babdf3bc04c58ab59d59bebc24b5f780f50342b9ffe05b547
F test/fts3near.test 7e3354d46f155a822b59c0e957fd2a70c1d7e905
F test/fts3offsets.test b85fd382abdc78ebce721d8117bd552dfb75094c
F test/fts3prefix.test fa794eaab0bdae466494947b0b153d7844478ab2
@@ -819,7 +822,7 @@ F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01
F test/fts4growth.test e5390da74619cacc389711bac9349640b32c4f9a
F test/fts4growth2.test 13ad4e76451af6e6906c95cdc725d01b00044269
F test/fts4incr.test 4e353a0bd886ea984e56fce9e77724fc923b8d0d
-F test/fts4langid.test 65a7332c9bc257919e259a304aa8a38c41655b9d
+F test/fts4langid.test 89a107d36710dc3f44dab0861a784ffab967bd1f32c8d700b031748b9d2703c6
F test/fts4lastrowid.test fa5e157955a3121615ef3e16ff5196e96c9e1e64
F test/fts4merge.test d2b39f6b1bd4a9738a13540e2d044cba11c43d47
F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
@@ -1035,7 +1038,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test 9c0da2079fa37e7509957c9efbbdc282dea4ed0e732d19e6f216d53ae431a67d
+F test/permutations.test 1d9e247280c1e656a1f2567a263b83561a29d8c3eca6a349ae939218e82a9cfc
F test/pragma.test 1e94755164a3a3264cd39836de4bebcb7809e5f8
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
@@ -1490,7 +1493,7 @@ F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
F tool/GetTclKit.bat 6afa640edc7810725aec61c3076ac617c4aaf0b7
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
-F tool/addopcodes.tcl 10c889c4a65ec6c5604e4a47306fa77ff57ae189
+F tool/addopcodes.tcl edbd53806bf20e25af2373ad0c091be4385081c1aa1813b916bf093f94ed8380
F tool/build-all-msvc.bat c12328d06c45fec8baada5949e3d5af54bf8c887 x
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
F tool/cg_anno.tcl f95b0006c52cf7f0496b506343415b6ee3cdcdd3 x
@@ -1536,7 +1539,7 @@ F tool/showdb.c e6bc9dba233bf1b57ca0a525a2bba762db4e223de84990739db3f09c46151b1e
F tool/showjournal.c 5bad7ae8784a43d2b270d953060423b8bd480818
F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68
F tool/showstat4.c b14159aa062f661b394ba37b6b7b94bfb8012ab9
-F tool/showwal.c ec79959834f7b21f1e0a2aa52bb7c056d2203977
+F tool/showwal.c ad9d768f96ca6199ad3a8c9562d679680bd032dd01204ea3e5ea6fb931d81847
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/spaceanal.tcl ab7d9bf68062907282a64b3e12ccbfad47193c5a
F tool/speed-check.sh 9630ba0468b609c52f48309243d4eb6e9c34deda
@@ -1579,7 +1582,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 4577fea5cd9d91ea241e9be82797ca1a4447f536e1e4b78a4a569aeb52e78fcb 8e57c31340dd9ffc457da63c5996fb1b573f8154f864ec2b52c15f399906ac8b
-R 73307a63b82b2606f65f28fc989fd3ae
+P 4e366996434e63f06cc451d2011f1f1464360f03430b8260e48f78a612b4e9d6 6b21d0fdebdccfaf63590d9ca9a279c22b8baec07c1a669b9f617f25bd857384
+R 3ff876810132a5a27eb9c4a943446c06
U dan
-Z a6caf6a2d1f5541a0691d13e2a47ea26
+Z 9bcc0d98d05bcef127f4de36c5b9d364
diff --git a/manifest.uuid b/manifest.uuid
index 7dc88187b..10e306f44 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-4e366996434e63f06cc451d2011f1f1464360f03430b8260e48f78a612b4e9d6 \ No newline at end of file
+b1533bc455f52f570c0f4b8aaa0da802757dc89b0e45b9a9b31aa591a44bf7bd \ No newline at end of file
diff --git a/src/expr.c b/src/expr.c
index ad17c5f22..873911633 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -3889,6 +3889,17 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
break;
}
+ case TK_IF_NULL_ROW: {
+ int addrINR;
+ addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable);
+ sqlite3ExprCachePush(pParse);
+ inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
+ sqlite3ExprCachePop(pParse);
+ sqlite3VdbeJumpHere(v, addrINR);
+ sqlite3VdbeChangeP3(v, addrINR, inReg);
+ break;
+ }
+
/*
** Form A:
** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
diff --git a/src/parse.y b/src/parse.y
index ef9d3dd0e..522c80548 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -193,6 +193,23 @@ columnlist ::= columnlist COMMA columnname carglist.
columnlist ::= columnname carglist.
columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
+// The following directive causes tokens ABORT, AFTER, ASC, etc. to
+// fallback to ID if they will not parse as their original value.
+// This obviates the need for the "id" nonterminal.
+//
+%fallback ID
+ ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW
+ CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
+ IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
+ QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW
+ ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
+%ifdef SQLITE_OMIT_COMPOUND_SELECT
+ EXCEPT INTERSECT UNION
+%endif SQLITE_OMIT_COMPOUND_SELECT
+ REINDEX RENAME CTIME_KW IF
+ .
+%wildcard ANY.
+
// Define operator precedence early so that this is the first occurrence
// of the operator tokens in the grammer. Keeping the operators together
// causes them to be assigned integer values that are close together,
@@ -222,23 +239,6 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
//
%token_class id ID|INDEXED.
-// The following directive causes tokens ABORT, AFTER, ASC, etc. to
-// fallback to ID if they will not parse as their original value.
-// This obviates the need for the "id" nonterminal.
-//
-%fallback ID
- ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW
- CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
- IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
- QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW
- ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
-%ifdef SQLITE_OMIT_COMPOUND_SELECT
- EXCEPT INTERSECT UNION
-%endif SQLITE_OMIT_COMPOUND_SELECT
- REINDEX RENAME CTIME_KW IF
- .
-%wildcard ANY.
-
// And "ids" is an identifer-or-string.
//
diff --git a/src/pragma.c b/src/pragma.c
index 28538f43d..fb7a40628 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -1330,33 +1330,37 @@ void sqlite3Pragma(
assert( x==0 );
}
addrOk = sqlite3VdbeMakeLabel(v);
- if( pParent && pIdx==0 ){
- int iKey = pFK->aCol[0].iFrom;
- assert( iKey>=0 && iKey<pTab->nCol );
- if( iKey!=pTab->iPKey ){
- sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
- sqlite3ColumnDefault(v, pTab, iKey, regRow);
- sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); VdbeCoverage(v);
- }else{
- sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
- }
- sqlite3VdbeAddOp3(v, OP_SeekRowid, i, 0, regRow); VdbeCoverage(v);
+
+ /* Generate code to read the child key values into registers
+ ** regRow..regRow+n. If any of the child key values are NULL, this
+ ** row cannot cause an FK violation. Jump directly to addrOk in
+ ** this case. */
+ for(j=0; j<pFK->nCol; j++){
+ int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom;
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v);
+ }
+
+ /* Generate code to query the parent index for a matching parent
+ ** key. If a match is found, jump to addrOk. */
+ if( pIdx ){
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey,
+ sqlite3IndexAffinityStr(db,pIdx), pFK->nCol);
+ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
+ VdbeCoverage(v);
+ }else if( pParent ){
+ int jmp = sqlite3VdbeCurrentAddr(v)+2;
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, i, jmp, regRow); VdbeCoverage(v);
sqlite3VdbeGoto(v, addrOk);
- sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
+ assert( pFK->nCol==1 );
+ }
+
+ /* Generate code to report an FK violation to the caller. */
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
}else{
- for(j=0; j<pFK->nCol; j++){
- sqlite3ExprCodeGetColumnOfTable(v, pTab, 0,
- aiCols ? aiCols[j] : pFK->aCol[j].iFrom, regRow+j);
- sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v);
- }
- if( pParent ){
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey,
- sqlite3IndexAffinityStr(db,pIdx), pFK->nCol);
- sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
- VdbeCoverage(v);
- }
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regResult+1);
}
- sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
sqlite3VdbeMultiLoad(v, regResult+2, "si", pFK->zTo, i-1);
sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4);
sqlite3VdbeResolveLabel(v, addrOk);
diff --git a/src/select.c b/src/select.c
index d7ba1f806..3def13618 100644
--- a/src/select.c
+++ b/src/select.c
@@ -3154,6 +3154,8 @@ static int multiSelectOrderBy(
typedef struct SubstContext {
Parse *pParse; /* The parsing context */
int iTable; /* Replace references to this table */
+ int iNewTable; /* New table number */
+ int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
ExprList *pEList; /* Replacement expressions */
} SubstContext;
@@ -3179,18 +3181,29 @@ static Expr *substExpr(
Expr *pExpr /* Expr in which substitution occurs */
){
if( pExpr==0 ) return 0;
+ if( ExprHasProperty(pExpr, EP_FromJoin) && pExpr->iRightJoinTable==pSubst->iTable ){
+ pExpr->iRightJoinTable = pSubst->iNewTable;
+ }
if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){
if( pExpr->iColumn<0 ){
pExpr->op = TK_NULL;
}else{
Expr *pNew;
Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
+ Expr ifNullRow;
assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr );
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
if( sqlite3ExprIsVector(pCopy) ){
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
}else{
sqlite3 *db = pSubst->pParse->db;
+ if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
+ memset(&ifNullRow, 0, sizeof(ifNullRow));
+ ifNullRow.op = TK_IF_NULL_ROW;
+ ifNullRow.pLeft = pCopy;
+ ifNullRow.iTable = pSubst->iNewTable;
+ pCopy = &ifNullRow;
+ }
pNew = sqlite3ExprDup(db, pCopy, 0);
if( pNew && (pExpr->flags & EP_FromJoin) ){
pNew->iRightJoinTable = pExpr->iRightJoinTable;
@@ -3284,8 +3297,8 @@ static void substSelect(
** FROM-clause subquery that is a candidate for flattening. (2b is
** due to ticket [2f7170d73bf9abf80] from 2015-02-09.)
**
-** (3) The subquery is not the right operand of a left outer join
-** (Originally ticket #306. Strengthened by ticket #3300)
+** (3) The subquery is not the right operand of a LEFT JOIN
+** or the subquery is not itself a join.
**
** (4) The subquery is not DISTINCT.
**
@@ -3297,7 +3310,7 @@ static void substSelect(
** DISTINCT.
**
** (7) The subquery has a FROM clause. TODO: For subqueries without
-** A FROM clause, consider adding a FROM close with the special
+** A FROM clause, consider adding a FROM clause with the special
** table sqlite_once that consists of a single row containing a
** single NULL.
**
@@ -3403,6 +3416,8 @@ static int flattenSubquery(
SrcList *pSubSrc; /* The FROM clause of the subquery */
ExprList *pList; /* The result set of the outer query */
int iParent; /* VDBE cursor number of the pSub result set temp table */
+ int iNewParent = -1;/* Replacement table for iParent */
+ int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
int i; /* Loop counter */
Expr *pWhere; /* The WHERE clause */
struct SrcList_item *pSubitem; /* The subquery */
@@ -3429,7 +3444,7 @@ static int flattenSubquery(
return 0; /* Restriction (2b) */
}
}
-
+
pSubSrc = pSub->pSrc;
assert( pSubSrc );
/* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants,
@@ -3467,10 +3482,9 @@ static int flattenSubquery(
return 0; /* Restriction (23) */
}
- /* OBSOLETE COMMENT 1:
- ** Restriction 3: If the subquery is a join, make sure the subquery is
- ** not used as the right operand of an outer join. Examples of why this
- ** is not allowed:
+ /*
+ ** If the subquery is the right operand of a LEFT JOIN, then the
+ ** subquery may not be a join itself. Example of why this is not allowed:
**
** t1 LEFT OUTER JOIN (t2 JOIN t3)
**
@@ -3480,27 +3494,13 @@ static int flattenSubquery(
**
** which is not at all the same thing.
**
- ** OBSOLETE COMMENT 2:
- ** Restriction 12: If the subquery is the right operand of a left outer
- ** join, make sure the subquery has no WHERE clause.
- ** An examples of why this is not allowed:
- **
- ** t1 LEFT OUTER JOIN (SELECT * FROM t2 WHERE t2.x>0)
- **
- ** If we flatten the above, we would get
- **
- ** (t1 LEFT OUTER JOIN t2) WHERE t2.x>0
- **
- ** But the t2.x>0 test will always fail on a NULL row of t2, which
- ** effectively converts the OUTER JOIN into an INNER JOIN.
- **
- ** THIS OVERRIDES OBSOLETE COMMENTS 1 AND 2 ABOVE:
- ** Ticket #3300 shows that flattening the right term of a LEFT JOIN
- ** is fraught with danger. Best to avoid the whole thing. If the
- ** subquery is the right term of a LEFT JOIN, then do not flatten.
+ ** See also tickets #306, #350, and #3300.
*/
if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
- return 0;
+ isLeftJoin = 1;
+ if( pSubSrc->nSrc>1 ){
+ return 0; /* Restriction (3) */
+ }
}
/* Restriction 17: If the sub-query is a compound SELECT, then it must
@@ -3709,6 +3709,7 @@ static int flattenSubquery(
sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing);
assert( pSrc->a[i+iFrom].fg.isTabFunc==0 );
pSrc->a[i+iFrom] = pSubSrc->a[i];
+ iNewParent = pSubSrc->a[i].iCursor;
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSrc->a[iFrom].fg.jointype = jointype;
@@ -3754,6 +3755,9 @@ static int flattenSubquery(
pSub->pOrderBy = 0;
}
pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
+ if( isLeftJoin ){
+ setJoinExpr(pWhere, iNewParent);
+ }
if( subqueryIsAgg ){
assert( pParent->pHaving==0 );
pParent->pHaving = pParent->pWhere;
@@ -3770,6 +3774,8 @@ static int flattenSubquery(
SubstContext x;
x.pParse = pParse;
x.iTable = iParent;
+ x.iNewTable = iNewParent;
+ x.isLeftJoin = isLeftJoin;
x.pEList = pSub->pEList;
substSelect(&x, pParent, 0);
}
@@ -3878,6 +3884,8 @@ static int pushDownWhereTerms(
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
x.pParse = pParse;
x.iTable = iCursor;
+ x.iNewTable = iCursor;
+ x.isLeftJoin = 0;
x.pEList = pSubq->pEList;
pNew = substExpr(&x, pNew);
pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew);
diff --git a/src/shell.c b/src/shell.c
index 8341d828c..15c88061c 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -427,6 +427,36 @@ static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
}
#endif
+/*
+** Output string zUtf to stream pOut as w characters. If w is negative,
+** then right-justify the text. W is the width in UTF-8 characters, not
+** in bytes. This is different from the %*.*s specification in printf
+** since with %*.*s the width is measured in bytes, not characters.
+*/
+static void utf8_width_print(FILE *pOut, int w, const char *zUtf){
+ int i;
+ int n;
+ int aw = w<0 ? -w : w;
+ char zBuf[1000];
+ if( aw>sizeof(zBuf)/3 ) aw = sizeof(zBuf)/3;
+ for(i=n=0; zUtf[i]; i++){
+ if( (zUtf[i]&0xc0)!=0x80 ){
+ n++;
+ if( n==aw ){
+ do{ i++; }while( (zUtf[i]&0xc0)==0x80 );
+ break;
+ }
+ }
+ }
+ if( n>=aw ){
+ utf8_printf(pOut, "%.*s", i, zUtf);
+ }else if( w<0 ){
+ utf8_printf(pOut, "%*s%s", aw-n, "", zUtf);
+ }else{
+ utf8_printf(pOut, "%s%*s", zUtf, aw-n, "");
+ }
+}
+
/*
** Determines if a string is a number of not.
@@ -1878,13 +1908,8 @@ static int shell_callback(
p->actualWidth[i] = w;
}
if( showHdr ){
- if( w<0 ){
- utf8_printf(p->out,"%*.*s%s",-w,-w,azCol[i],
- i==nArg-1 ? rowSep : " ");
- }else{
- utf8_printf(p->out,"%-*.*s%s",w,w,azCol[i],
- i==nArg-1 ? rowSep : " ");
- }
+ utf8_width_print(p->out, w, azCol[i]);
+ utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " ");
}
}
if( showHdr ){
@@ -1920,15 +1945,8 @@ static int shell_callback(
}
p->iIndent++;
}
- if( w<0 ){
- utf8_printf(p->out,"%*.*s%s",-w,-w,
- azArg[i] ? azArg[i] : p->nullValue,
- i==nArg-1 ? rowSep : " ");
- }else{
- utf8_printf(p->out,"%-*.*s%s",w,w,
- azArg[i] ? azArg[i] : p->nullValue,
- i==nArg-1 ? rowSep : " ");
- }
+ utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue);
+ utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " ");
}
break;
}
diff --git a/src/treeview.c b/src/treeview.c
index e4f3d781b..fc188256f 100644
--- a/src/treeview.c
+++ b/src/treeview.c
@@ -470,6 +470,11 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0);
break;
}
+ case TK_IF_NULL_ROW: {
+ sqlite3TreeViewLine(pView, "IF-NULL-ROW %d", pExpr->iTable);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
default: {
sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
break;
diff --git a/src/vdbe.c b/src/vdbe.c
index 4f8657829..6f9d3e18b 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -486,6 +486,7 @@ static void registerTrace(int iReg, Mem *p){
printf("REG[%d] = ", iReg);
memTracePrint(p);
printf("\n");
+ sqlite3VdbeCheckMemInvariants(p);
}
#endif
@@ -1151,7 +1152,7 @@ case OP_Null: { /* out2 */
case OP_SoftNull: {
assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
pOut = &aMem[pOp->p1];
- pOut->flags = (pOut->flags|MEM_Null)&~MEM_Undefined;
+ pOut->flags = (pOut->flags&~(MEM_Undefined|MEM_AffMask))|MEM_Null;
break;
}
@@ -1494,7 +1495,6 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
type2 = numericType(pIn2);
pOut = &aMem[pOp->p3];
flags = pIn1->flags | pIn2->flags;
- if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null;
if( (type1 & type2 & MEM_Int)!=0 ){
iA = pIn1->u.i;
iB = pIn2->u.i;
@@ -1518,6 +1518,8 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
}
pOut->u.i = iB;
MemSetTypeFlag(pOut, MEM_Int);
+ }else if( (flags & MEM_Null)!=0 ){
+ goto arithmetic_result_is_null;
}else{
bIntint = 0;
fp_math:
@@ -2429,6 +2431,23 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
break;
}
+/* Opcode: IfNullRow P1 P2 P3 * *
+** Synopsis: if P1.nullRow then r[P3]=NULL, goto P2
+**
+** Check the cursor P1 to see if it is currently pointing at a NULL row.
+** If it is, then set register P3 to NULL and jump immediately to P2.
+** If P1 is not on a NULL row, then fall through without making any
+** changes.
+*/
+case OP_IfNullRow: { /* jump */
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ if( p->apCsr[pOp->p1]->nullRow ){
+ sqlite3VdbeMemSetNull(aMem + pOp->p3);
+ goto jump_to_p2;
+ }
+ break;
+}
+
/* Opcode: Column P1 P2 P3 P4 P5
** Synopsis: r[P3]=PX
**
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 4f5789376..e95a8d1b9 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -40,6 +40,10 @@ int sqlite3VdbeCheckMemInvariants(Mem *p){
/* Cannot be both MEM_Int and MEM_Real at the same time */
assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) );
+ /* Cannot be both MEM_Null and some other type */
+ assert( (p->flags & MEM_Null)==0 ||
+ (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob))==0 );
+
/* The szMalloc field holds the correct memory allocation size */
assert( p->szMalloc==0
|| p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc) );
diff --git a/src/where.c b/src/where.c
index e32a895c7..91050de90 100644
--- a/src/where.c
+++ b/src/where.c
@@ -5048,6 +5048,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
+ }else if( pOp->opcode==OP_IfNullRow ){
+ pOp->p1 = pLevel->iIdxCur;
}
}
}
diff --git a/test/fkey5.test b/test/fkey5.test
index b9e1fc2ee..e9bb47f89 100644
--- a/test/fkey5.test
+++ b/test/fkey5.test
@@ -388,5 +388,40 @@ do_execsql_test 9.4 {
PRAGMA foreign_key_check(k2);
} {k2 3 s1 0}
+#-------------------------------------------------------------------------
+# Test using a WITHOUT ROWID table as the child table with an INTEGER
+# PRIMARY KEY as the parent key.
+#
+reset_db
+do_execsql_test 10.1 {
+ CREATE TABLE p30 (id INTEGER PRIMARY KEY);
+ CREATE TABLE IF NOT EXISTS c30 (
+ line INTEGER,
+ master REFERENCES p30(id),
+ PRIMARY KEY(master)
+ ) WITHOUT ROWID;
+
+ INSERT INTO p30 (id) VALUES (1);
+ INSERT INTO c30 (master, line) VALUES (1, 999);
+}
+do_execsql_test 10.2 {
+ PRAGMA foreign_key_check;
+}
+do_execsql_test 10.3 {
+ INSERT INTO c30 VALUES(45, 45);
+ PRAGMA foreign_key_check;
+} {c30 {} p30 0}
+
+#-------------------------------------------------------------------------
+# Test "foreign key mismatch" errors.
+#
+reset_db
+do_execsql_test 11.0 {
+ CREATE TABLE tt(y);
+ CREATE TABLE c11(x REFERENCES tt(y));
+}
+do_catchsql_test 11.1 {
+ PRAGMA foreign_key_check;
+} {1 {foreign key mismatch - "c11" referencing "tt"}}
finish_test
diff --git a/test/fts3aa.test b/test/fts3aa.test
index 08d825dd1..10ec273cb 100644
--- a/test/fts3aa.test
+++ b/test/fts3aa.test
@@ -243,5 +243,11 @@ do_execsql_test 8.5 {
SELECT docid FROM t0 WHERE t0 MATCH '"abc abc"';
} {}
+do_execsql_test 9.1 {
+ CREATE VIRTUAL TABLE t9 USING fts4(a, "", '---');
+}
+do_execsql_test 9.2 {
+ CREATE VIRTUAL TABLE t10 USING fts3(<, b, c);
+}
finish_test
diff --git a/test/fts3corrupt3.test b/test/fts3corrupt3.test
new file mode 100644
index 000000000..6c846e9ba
--- /dev/null
+++ b/test/fts3corrupt3.test
@@ -0,0 +1,65 @@
+# 2010 October 27
+#
+# 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.
+#
+#***********************************************************************
+# Test that the FTS3 extension does not crash when it encounters a
+# corrupt data structure on disk.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
+ifcapable !fts3 { finish_test ; return }
+
+set ::testprefix fts3corrupt3
+
+#-------------------------------------------------------------------------
+# Test that fts3 does not choke on an oversized varint.
+#
+do_execsql_test 1.0 {
+ PRAGMA page_size = 512;
+ CREATE VIRTUAL TABLE t1 USING fts3;
+ BEGIN;
+ INSERT INTO t1 VALUES('one');
+ INSERT INTO t1 VALUES('one');
+ INSERT INTO t1 VALUES('one');
+ COMMIT;
+}
+do_execsql_test 1.1 {
+ SELECT quote(root) from t1_segdir;
+} {X'00036F6E6509010200010200010200'}
+do_execsql_test 1.2 {
+ UPDATE t1_segdir SET root = X'00036F6E650EFFFFFFFFFFFFFFFFFFFFFFFF0200';
+}
+do_catchsql_test 1.3 {
+ SELECT rowid FROM t1 WHERE t1 MATCH 'one'
+} {0 -1}
+
+#-------------------------------------------------------------------------
+# Interior node with the prefix or suffix count of an entry set to a
+# negative value.
+#
+set doc1 [string repeat "x " 600]
+set doc2 [string repeat "y " 600]
+set doc3 [string repeat "z " 600]
+
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE t2 USING fts3;
+ BEGIN;
+ INSERT INTO t2 VALUES($doc1);
+ INSERT INTO t2 VALUES($doc2);
+ INSERT INTO t2 VALUES($doc3);
+ COMMIT;
+}
+do_execsql_test 2.1 {
+ SELECT quote(root) from t2_segdir;
+} {X'0101017900017A'}
+
+
+
+finish_test
diff --git a/test/fts3fault.test b/test/fts3fault.test
index 7d9433205..2622e4713 100644
--- a/test/fts3fault.test
+++ b/test/fts3fault.test
@@ -18,8 +18,6 @@ set ::testprefix fts3fault
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
ifcapable !fts3 { finish_test ; return }
-if 0 {
-
# Test error handling in the sqlite3Fts3Init() function. This is the
# function that registers the FTS3 module and various support functions
# with SQLite.
@@ -160,8 +158,6 @@ do_faultsim_test 7.3 -prep {
}
-}
-
proc mit {blob} {
set scan(littleEndian) i*
set scan(bigEndian) I*
@@ -234,4 +230,12 @@ do_faultsim_test 9.1 -prep {
faultsim_test_result {0 {{0 0 20 39 0 0 64 2}}}
}
+do_faultsim_test 10.1 -prep {
+ faultsim_delete_and_reopen
+} -body {
+ execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, languageid=d) }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
finish_test
diff --git a/test/fts3fault2.test b/test/fts3fault2.test
index 030ff73dc..e4f292042 100644
--- a/test/fts3fault2.test
+++ b/test/fts3fault2.test
@@ -174,4 +174,76 @@ do_faultsim_test 6.1 -faults oom* -prep {
faultsim_test_result {0 -1}
}
+#-------------------------------------------------------------------------
+# Inject faults into a query for an N-byte prefix that uses a prefix=N+1
+# index.
+reset_db
+do_execsql_test 7.0 {
+ CREATE VIRTUAL TABLE t7 USING fts4(x,prefix=2);
+ INSERT INTO t7 VALUES('the quick brown fox');
+ INSERT INTO t7 VALUES('jumped over the');
+ INSERT INTO t7 VALUES('lazy dog');
+}
+do_faultsim_test 7.1 -faults oom* -body {
+ execsql { SELECT docid FROM t7 WHERE t7 MATCH 't*' }
+} -test {
+ faultsim_test_result {0 {1 2}}
+}
+
+#-------------------------------------------------------------------------
+# Inject faults into a opening an existing fts3 table that has been
+# upgraded to add an %_stat table.
+#
+reset_db
+do_execsql_test 8.0 {
+ CREATE VIRTUAL TABLE t8 USING fts3;
+ INSERT INTO t8 VALUES('the quick brown fox');
+ INSERT INTO t8 VALUES('jumped over the');
+ INSERT INTO t8 VALUES('lazy dog');
+ INSERT INTO t8(t8) VALUES('automerge=8');
+ SELECT name FROM sqlite_master WHERE name LIKE 't8%';
+} {
+ t8 t8_content t8_segments t8_segdir t8_stat
+}
+faultsim_save_and_close
+
+do_faultsim_test 8.1 -faults oom* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql { INSERT INTO t8 VALUES('one two three') }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+do_faultsim_test 8.2 -faults oom* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql { ALTER TABLE t8 RENAME TO t8ii }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+#-------------------------------------------------------------------------
+reset_db
+set chunkconfig [fts3_configure_incr_load 1 1]
+do_execsql_test 9.0 {
+ PRAGMA page_size = 512;
+ CREATE VIRTUAL TABLE t9 USING fts3;
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50
+ )
+ INSERT INTO t9 SELECT 'one two three' FROM s;
+}
+
+do_faultsim_test 8.2 -faults io* -body {
+ execsql { SELECT count(*) FROM t9 WHERE t9 MATCH '"one two three"' }
+} -test {
+ faultsim_test_result {0 50}
+}
+
+eval fts3_configure_incr_load $chunkconfig
+
+
finish_test
+
+
diff --git a/test/fts3misc.test b/test/fts3misc.test
new file mode 100644
index 000000000..0d003bd32
--- /dev/null
+++ b/test/fts3misc.test
@@ -0,0 +1,228 @@
+# 2017 March 22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing the FTS3 module.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fts3misc
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+#-------------------------------------------------------------------------
+# A self-join.
+#
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE t1 USING fts3(a, b);
+ INSERT INTO t1 VALUES('one', 'i');
+ INSERT INTO t1 VALUES('one', 'ii');
+ INSERT INTO t1 VALUES('two', 'i');
+ INSERT INTO t1 VALUES('two', 'ii');
+}
+
+do_execsql_test 1.1 {
+ SELECT a.a, b.b FROM t1 a, t1 b WHERE a.t1 MATCH 'two' AND b.t1 MATCH 'i'
+} {two i two i two i two i}
+
+#-------------------------------------------------------------------------
+# FTS tables with 128 or more columns.
+#
+proc v1 {v} {
+ set vector [list a b c d e f g h]
+ set res [list]
+ for {set i 0} {$i<8} {incr i} {
+ if {$v & (1 << $i)} { lappend res [lindex $vector $i] }
+ }
+ set res
+}
+proc v2 {v} {
+ set vector [list d e f g h i j k]
+ set res [list]
+ for {set i 0} {$i<8} {incr i} {
+ if {$v & (1 << $i)} { lappend res [lindex $vector $i] }
+ }
+ set res
+}
+db func v1 v1
+db func v2 v2
+
+do_test 2.0 {
+ set cols [list]
+ for {set i 0} {$i<200} {incr i} {
+ lappend cols "c$i"
+ }
+ execsql "CREATE VIRTUAL TABLE t2 USING fts3([join $cols ,])"
+ execsql {
+ WITH data(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM data WHERE i<200
+ )
+ INSERT INTO t2(c198, c199) SELECT v1(i), v2(i) FROM data;
+ }
+} {}
+do_execsql_test 2.1 {
+ SELECT rowid FROM t2 WHERE t2 MATCH '"a b c"'
+} {
+ 7 15 23 31 39 47 55 63 71 79 87 95 103 111
+ 119 127 135 143 151 159 167 175 183 191 199
+}
+do_execsql_test 2.2 {
+ SELECT rowid FROM t2 WHERE t2 MATCH '"g h i"'
+} {
+ 56 57 58 59 60 61 62 63 120 121 122 123 124
+ 125 126 127 184 185 186 187 188 189 190 191
+}
+do_execsql_test 2.3 {
+ SELECT rowid FROM t2 WHERE t2 MATCH '"i h"'
+} {
+}
+do_execsql_test 2.4 {
+ SELECT rowid FROM t2 WHERE t2 MATCH '"f e"'
+} {
+}
+do_execsql_test 2.5 {
+ SELECT rowid FROM t2 WHERE t2 MATCH '"e f"'
+} {
+ 6 7 14 15 22 23 30 31 38 39 46 47 48 49 50 51 52 53 54 55 56
+ 57 58 59 60 61 62 63 70 71 78 79 86 87 94 95 102 103 110
+ 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
+ 134 135 142 143 150 151 158 159 166 167 174 175 176 177 178 179 180
+ 181 182 183 184 185 186 187 188 189 190 191 198 199
+}
+
+#-------------------------------------------------------------------------
+# Range constraints on the docid using non-integer values.
+#
+do_execsql_test 2.6 {
+ SELECT rowid FROM t2 WHERE t2 MATCH 'e' AND rowid BETWEEN NULL AND 45;
+} {}
+do_execsql_test 2.7 {
+ SELECT rowid FROM t2 WHERE t2 MATCH 'e' AND rowid BETWEEN 11.5 AND 48.2;
+} {
+ 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
+ 29 30 31 34 35 38 39 42 43 46 47 48
+}
+do_execsql_test 2.8 {
+ SELECT rowid FROM t2 WHERE t2 MATCH 'e' AND rowid BETWEEN '11.5' AND '48.2';
+} {
+ 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
+ 29 30 31 34 35 38 39 42 43 46 47 48
+}
+
+#-------------------------------------------------------------------------
+# Phrase query tests.
+#
+do_execsql_test 3.1.1 {
+ CREATE VIRTUAL TABLE t3 USING fts3;
+ INSERT INTO t3 VALUES('a b c');
+ INSERT INTO t3 VALUES('d e f');
+ INSERT INTO t3 VALUES('a b d');
+ INSERT INTO t3 VALUES('1 2 3 4 5 6 7 8 9 10 11');
+}
+do_execsql_test 3.1.2 {
+ SELECT * FROM t3 WHERE t3 MATCH '"a b x y"' ORDER BY docid DESC
+}
+do_execsql_test 3.1.3 {
+ SELECT * FROM t3 WHERE t3 MATCH '"a b c" OR "a b x y"' ORDER BY docid DESC
+} {{a b c}}
+do_execsql_test 3.1.4 {
+ SELECT * FROM t3 WHERE t3 MATCH '"a* b* x* a*"'
+}
+do_execsql_test 3.1.5 {
+ SELECT rowid FROM t3 WHERE t3 MATCH '"2 3 4 5 6 7 8 9"'
+} {4}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 4.0 {
+ PRAGMA page_size = 512;
+ CREATE VIRTUAL TABLE t4 USING fts4;
+ WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<8000 )
+ INSERT INTO t4 SELECT 'a b c a b c a b c' FROM s;
+}
+do_execsql_test 4.1 {
+ SELECT count(*) FROM t4 WHERE t4 MATCH '"a b c" OR "c a b"'
+} {8000}
+do_execsql_test 4.2 {
+ SELECT quote(value) from t4_stat where id=0
+} {X'C03EC0B204C0A608'}
+do_execsql_test 4.3 {
+ UPDATE t4_stat SET value = X'C03EC0B204C0A60800' WHERE id=0;
+}
+do_catchsql_test 4.4 {
+ SELECT count(*) FROM t4 WHERE t4 MATCH '"a b c" OR "c a b"'
+} {1 {database disk image is malformed}}
+do_execsql_test 4.5 {
+ UPDATE t4_stat SET value = X'00C03EC0B204C0A608' WHERE id=0;
+}
+do_catchsql_test 4.6 {
+ SELECT count(*) FROM t4 WHERE t4 MATCH '"a b c" OR "c a b"'
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 5.0 {
+ CREATE VIRTUAL TABLE t5 USING fts4;
+ INSERT INTO t5 VALUES('a x x x x b x x x x c');
+ INSERT INTO t5 VALUES('a x x x x b x x x x c');
+ INSERT INTO t5 VALUES('a x x x x b x x x x c');
+}
+do_execsql_test 5.1 {
+ SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/4 b NEAR/4 c'
+} {1 2 3}
+do_execsql_test 5.2 {
+ SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/3 b NEAR/4 c'
+} {}
+do_execsql_test 5.3 {
+ SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/4 b NEAR/3 c'
+} {}
+do_execsql_test 5.4 {
+ SELECT rowid FROM t5 WHERE t5 MATCH 'y NEAR/4 b NEAR/4 c'
+} {}
+do_execsql_test 5.5 {
+ SELECT rowid FROM t5 WHERE t5 MATCH 'x OR a NEAR/3 b NEAR/3 c'
+} {1 2 3}
+do_execsql_test 5.5 {
+ SELECT rowid FROM t5 WHERE t5 MATCH 'x OR y NEAR/3 b NEAR/3 c'
+} {1 2 3}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 6.0 {
+ CREATE VIRTUAL TABLE t6 USING fts4;
+
+ BEGIN;
+ WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000)
+ INSERT INTO t6 SELECT 'x x x x x x x x x x x' FROM s;
+
+ INSERT INTO t6 VALUES('x x x x x x x x x x x A');
+ INSERT INTO t6 VALUES('x x x x x x x x x x x B');
+ INSERT INTO t6 VALUES('x x x x x x x x x x x A');
+ INSERT INTO t6 VALUES('x x x x x x x x x x x B');
+
+ WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000)
+ INSERT INTO t6 SELECT 'x x x x x x x x x x x' FROM s;
+ COMMIT;
+}
+breakpoint
+do_execsql_test 6.1 {
+ SELECT rowid FROM t6 WHERE t6 MATCH 'b OR "x a"'
+} {50001 50002 50003 50004}
+
+
+finish_test
diff --git a/test/fts4langid.test b/test/fts4langid.test
index fdb1876be..9954acd71 100644
--- a/test/fts4langid.test
+++ b/test/fts4langid.test
@@ -14,7 +14,6 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-set ::testprefix fts4content
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
ifcapable !fts3 {
@@ -341,6 +340,13 @@ do_test_query1 3.3.4 {"zero one" OR "one two"} {
or_merge_lists [rowid_list "zero one"] [rowid_list "one two"]
}
+do_execsql_test 3.4 {
+ CREATE TABLE t8c(a, b);
+ CREATE VIRTUAL TABLE t8 USING fts4(content=t8c, languageid=langid);
+ INSERT INTO t8(docid, a, b) VALUES(-1, 'one two three', 'x y z');
+ SELECT docid FROM t8 WHERE t8 MATCH 'one x' AND langid=0
+} {-1}
+
#-------------------------------------------------------------------------
# Test cases 4.*
#
diff --git a/test/permutations.test b/test/permutations.test
index 7e4715515..1153f7dd1 100644
--- a/test/permutations.test
+++ b/test/permutations.test
@@ -266,6 +266,8 @@ test_suite "fts3" -prefix "" -description {
fts4incr.test fts4langid.test fts4lastrowid.test fts4merge2.test
fts4merge4.test fts4merge.test fts4noti.test fts4onepass.test
fts4opt.test fts4unicode.test
+ fts3corrupt3.test
+ fts3misc.test
}
test_suite "fts5" -prefix "" -description {
diff --git a/tool/addopcodes.tcl b/tool/addopcodes.tcl
index a6c58f1a2..308ddcb6b 100644
--- a/tool/addopcodes.tcl
+++ b/tool/addopcodes.tcl
@@ -39,6 +39,7 @@ set extras {
REGISTER
VECTOR
SELECT_COLUMN
+ IF_NULL_ROW
ASTERISK
SPAN
SPACE
diff --git a/tool/showwal.c b/tool/showwal.c
index 33cc21896..b214a288e 100644
--- a/tool/showwal.c
+++ b/tool/showwal.c
@@ -12,6 +12,7 @@
#if !defined(_MSC_VER)
#include <unistd.h>
+#include <sys/types.h>
#else
#include <io.h>
#endif
@@ -579,6 +580,14 @@ int main(int argc, char **argv){
decode_btree_page(a, iStart, hdrSize, zLeft+1);
free(a);
continue;
+#if !defined(_MSC_VER)
+ }else if( zLeft && strcmp(zLeft,"truncate")==0 ){
+ /* Frame number followed by "truncate" truncates the WAL file
+ ** after that frame */
+ off_t newSize = 32 + iStart*(pagesize+24);
+ truncate(argv[1], newSize);
+ continue;
+#endif
}else{
iEnd = iStart;
}