Index: Makefile.in
==================================================================
--- Makefile.in
+++ Makefile.in
@@ -549,11 +549,18 @@
# Databases containing fuzzer test cases
#
FUZZDATA = \
$(TOP)/test/fuzzdata1.db \
$(TOP)/test/fuzzdata2.db \
- $(TOP)/test/fuzzdata3.db
+ $(TOP)/test/fuzzdata3.db \
+ $(TOP)/test/fuzzdata4.db
+
+# 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_SRC = $(TOP)/ext/misc/json1.c
# Standard options to testfixture
#
TESTOPTS = --verbose=file --output=test-out.txt
@@ -576,23 +583,24 @@
$(LTLINK) -no-undefined -o $@ tclsqlite.lo \
libsqlite3.la @TCL_STUB_LIB_SPEC@ $(TLIBS) \
-rpath "$(TCLLIBDIR)/sqlite3" \
-version-info "8:6:8"
-sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h $(TOP)/ext/misc/json1.c
- $(LTLINK) $(READLINE_FLAGS) -DSQLITE_ENABLE_JSON1 -o $@ \
- $(TOP)/src/shell.c $(TOP)/ext/misc/json1.c libsqlite3.la \
+sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h $(JSON1_DEP)
+ $(LTLINK) $(READLINE_FLAGS) $(JSON1_OPT) -o $@ \
+ $(TOP)/src/shell.c $(JSON1_SRC) libsqlite3.la \
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
-fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
- $(LTLINK) -o $@ $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
+fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP)
+ $(LTLINK) -o $@ $(JSON1_OPT) \
+ $(TOP)/tool/fuzzershell.c $(JSON1_SRC) sqlite3.c $(TLIBS)
-fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
- $(LTLINK) -o $@ $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS)
+fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP)
+ $(LTLINK) -o $@ $(JSON1_OPT) $(TOP)/test/fuzzcheck.c $(JSON1_SRC) sqlite3.c $(TLIBS)
mptester$(TEXE): sqlite3.c $(TOP)/mptest/mptest.c
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
$(TLIBS) -rpath "$(libdir)"
Index: Makefile.msc
==================================================================
--- Makefile.msc
+++ Makefile.msc
@@ -1205,11 +1205,18 @@
# Databases containing fuzzer test cases
#
FUZZDATA = \
$(TOP)\test\fuzzdata1.db \
$(TOP)\test\fuzzdata2.db \
- $(TOP)\test\fuzzdata3.db
+ $(TOP)\test\fuzzdata3.db \
+ $(TOP)\test\fuzzdata4.db
+
+# Extra arguments for including json1 in the build of tools
+#
+JSON1_DEP = sqlite3ext.h $(TOP)\ext\misc\json1.c
+JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE
+JSON1_SRC = $(TOP)\ext\misc\json1.c
# Standard options to testfixture
#
TESTOPTS = --verbose=file --output=test-out.txt
@@ -1222,22 +1229,23 @@
$(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS)
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
$(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
-sqlite3.exe: $(TOP)\src\shell.c $(TOP)\ext\misc\json1.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
- $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c $(TOP)\ext\misc\json1.c \
+sqlite3.exe: $(TOP)\src\shell.c $(JSON1_DEP) $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
+ $(LTLINK) $(SHELL_COMPILE_OPTS) $(JSON1_OPT) $(READLINE_FLAGS) $(TOP)\src\shell.c $(JSON1_SRC) \
/link /pdb:sqlite3sh.pdb $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
sqldiff.exe: $(TOP)\tool\sqldiff.c sqlite3.c sqlite3.h
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c sqlite3.c
-fuzzershell.exe: $(TOP)\tool\fuzzershell.c sqlite3.c sqlite3.h
- $(LTLINK) $(NO_WARN) $(TOP)\tool\fuzzershell.c sqlite3.c
+fuzzershell.exe: $(TOP)\tool\fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP)
+ $(LTLINK) $(NO_WARN) $(JSON1_OPT) \
+ $(TOP)\tool\fuzzershell.c $(JSON1_SRC) sqlite3.c
-fuzzcheck.exe: $(TOP)\test\fuzzcheck.c sqlite3.c sqlite3.h
- $(LTLINK) $(NO_WARN) $(TOP)\test\fuzzcheck.c sqlite3.c
+fuzzcheck.exe: $(TOP)\test\fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP)
+ $(LTLINK) $(NO_WARN) $(JSON1_OPT) $(TOP)\test\fuzzcheck.c $(JSON1_SRC) sqlite3.c
mptester.exe: $(TOP)\mptest\mptest.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
$(LTLINK) $(NO_WARN) $(SHELL_COMPILE_OPTS) $(TOP)\mptest\mptest.c \
/link $(LTLINKOPTS) $(LTLIBPATHS) $(SHELL_LINK_OPTS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
@@ -1272,14 +1280,13 @@
del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL
$(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new
move vdbe.new tsrc\vdbe.c
echo > .target_source
-sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl
+sqlite3.c: .target_source sqlite3ext.h $(TOP)\tool\mksqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
copy tsrc\shell.c .
- copy tsrc\sqlite3ext.h .
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
# Set the source code file to be used by executables and libraries when
@@ -1582,10 +1589,13 @@
move parse.h parse.h.temp
$(NAWK) -f $(TOP)\addopcodes.awk parse.h.temp > parse.h
sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > sqlite3.h
+
+sqlite3ext.h: .target_source
+ copy tsrc\sqlite3ext.h .
mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
$(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) \
$(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS)
Index: ext/fts5/fts5.h
==================================================================
--- ext/fts5/fts5.h
+++ ext/fts5/fts5.h
@@ -382,11 +382,11 @@
** inefficient), it doesn't matter if the user queries for
** 'first + place' or '1st + place', as there are entires in the
** FTS index corresponding to both forms of the first token.
**
**
-** Whether is is parsing document or query text, any call to xToken that
+** Whether it is parsing document or query text, any call to xToken that
** specifies a tflags argument with the FTS5_TOKEN_COLOCATED bit
** is considered to supply a synonym for the previous token. For example,
** when parsing the document "I won first place", a tokenizer that supports
** synonyms would call xToken() 5 times, as follows:
**
Index: ext/fts5/fts5_main.c
==================================================================
--- ext/fts5/fts5_main.c
+++ ext/fts5/fts5_main.c
@@ -1114,10 +1114,11 @@
pCsr->ePlan = FTS5_PLAN_SOURCE;
pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
}else if( pMatch ){
const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
+ if( zExpr==0 ) zExpr = "";
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){
if( zExpr[0]=='*' ){
/* The user has issued a query of the form "MATCH '*...'". This
Index: ext/fts5/test/fts5simple.test
==================================================================
--- ext/fts5/test/fts5simple.test
+++ ext/fts5/test/fts5simple.test
@@ -166,8 +166,24 @@
}
do_execsql_test 5.8 {
SELECT rowid FROM tt WHERE tt MATCH 'a*';
} {1}
+
+#-------------------------------------------------------------------------
+
+reset_db
+do_execsql_test 6.1 {
+ CREATE VIRTUAL TABLE xyz USING fts5(x, y, z);
+ INSERT INTO xyz VALUES('x', 'y', 'z');
+}
+
+do_catchsql_test 6.2 {
+ SELECT * FROM xyz WHERE xyz MATCH ''
+} {1 {fts5: syntax error near ""}}
+do_catchsql_test 6.3 {
+ SELECT * FROM xyz WHERE xyz MATCH NULL
+} {1 {fts5: syntax error near ""}}
+
finish_test
Index: ext/misc/json1.c
==================================================================
--- ext/misc/json1.c
+++ ext/misc/json1.c
@@ -31,10 +31,42 @@
#include
#include
#define UNUSED_PARAM(X) (void)(X)
+/*
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
+** to pass signed char values.
+*/
+#define safe_isdigit(x) isdigit((unsigned char)(x))
+#define safe_isalnum(x) isalnum((unsigned char)(x))
+
+/*
+** Growing our own isspace() routine this way is twice as fast as
+** the library isspace() function, resulting in a 7% overall performance
+** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
+*/
+static const char jsonIsSpace[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define safe_isspace(x) (jsonIsSpace[(unsigned char)x])
+
/* Unsigned integer types */
typedef sqlite3_uint64 u64;
typedef unsigned int u32;
typedef unsigned char u8;
@@ -146,15 +178,13 @@
/* Report an out-of-memory (OOM) condition
*/
static void jsonOom(JsonString *p){
- if( !p->bErr ){
- p->bErr = 1;
- sqlite3_result_error_nomem(p->pCtx);
- jsonReset(p);
- }
+ p->bErr = 1;
+ sqlite3_result_error_nomem(p->pCtx);
+ jsonReset(p);
}
/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success. Return non-zero on an OOM error
*/
@@ -229,16 +259,17 @@
if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
p->zBuf[p->nUsed++] = '"';
for(i=0; inUsed+N+1-i > p->nAlloc) && jsonGrow(p,N+1-i)!=0 ) return;
+ if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
}
p->zBuf[p->nUsed++] = c;
}
p->zBuf[p->nUsed++] = '"';
+ assert( p->nUsednAlloc );
}
/*
** Append a function parameter value to the JSON string under
** construction.
@@ -332,11 +363,12 @@
JsonNode *pNode, /* The node to render */
JsonString *pOut, /* Write JSON here */
sqlite3_value **aReplace /* Replacement values */
){
switch( pNode->eType ){
- case JSON_NULL: {
+ default: {
+ assert( pNode->eType==JSON_NULL );
jsonAppendRaw(pOut, "null", 4);
break;
}
case JSON_TRUE: {
jsonAppendRaw(pOut, "true", 4);
@@ -430,11 +462,12 @@
JsonNode *pNode, /* Node to return */
sqlite3_context *pCtx, /* Return value for this function */
sqlite3_value **aReplace /* Array of replacement values */
){
switch( pNode->eType ){
- case JSON_NULL: {
+ default: {
+ assert( pNode->eType==JSON_NULL );
sqlite3_result_null(pCtx);
break;
}
case JSON_TRUE: {
sqlite3_result_int(pCtx, 1);
@@ -457,14 +490,20 @@
if( pNode->u.zJContent[0]=='-' ){ i = -i; }
sqlite3_result_int64(pCtx, i);
break;
}
case JSON_STRING: {
+#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
+ ** json_insert() and json_replace() and those routines do not
+ ** call jsonReturn() */
if( pNode->jnFlags & JNODE_RAW ){
sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
SQLITE_TRANSIENT);
- }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
+ }else
+#endif
+ assert( (pNode->jnFlags & JNODE_RAW)==0 );
+ if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
/* JSON formatted without any backslash-escapes */
sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
SQLITE_TRANSIENT);
}else{
/* Translate JSON formatted string into raw text */
@@ -530,10 +569,48 @@
jsonReturnJson(pNode, pCtx, aReplace);
break;
}
}
}
+
+/* Forward reference */
+static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
+
+/*
+** A macro to hint to the compiler that a function should not be
+** inlined.
+*/
+#if defined(__GNUC__)
+# define JSON_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER) && _MSC_VER>=1310
+# define JSON_NOINLINE __declspec(noinline)
+#else
+# define JSON_NOINLINE
+#endif
+
+
+static JSON_NOINLINE int jsonParseAddNodeExpand(
+ JsonParse *pParse, /* Append the node to this object */
+ u32 eType, /* Node type */
+ u32 n, /* Content size or sub-node count */
+ const char *zContent /* Content */
+){
+ u32 nNew;
+ JsonNode *pNew;
+ assert( pParse->nNode>=pParse->nAlloc );
+ if( pParse->oom ) return -1;
+ nNew = pParse->nAlloc*2 + 10;
+ pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
+ if( pNew==0 ){
+ pParse->oom = 1;
+ return -1;
+ }
+ pParse->nAlloc = nNew;
+ pParse->aNode = pNew;
+ assert( pParse->nNodenAlloc );
+ return jsonParseAddNode(pParse, eType, n, zContent);
+}
/*
** Create a new JsonNode instance based on the arguments and append that
** instance to the JsonParse. Return the index in pParse->aNode[] of the
** new node, or -1 if a memory allocation fails.
@@ -544,25 +621,11 @@
u32 n, /* Content size or sub-node count */
const char *zContent /* Content */
){
JsonNode *p;
if( pParse->nNode>=pParse->nAlloc ){
- u32 nNew;
- JsonNode *pNew;
- if( pParse->oom ) return -1;
- nNew = pParse->nAlloc*2 + 10;
- if( nNew<=pParse->nNode ){
- pParse->oom = 1;
- return -1;
- }
- pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
- if( pNew==0 ){
- pParse->oom = 1;
- return -1;
- }
- pParse->nAlloc = nNew;
- pParse->aNode = pNew;
+ return jsonParseAddNodeExpand(pParse, eType, n, zContent);
}
p = &pParse->aNode[pParse->nNode];
p->eType = (u8)eType;
p->jnFlags = 0;
p->iVal = 0;
@@ -583,18 +646,17 @@
char c;
u32 j;
int iThis;
int x;
JsonNode *pNode;
- while( isspace(pParse->zJson[i]) ){ i++; }
- if( (c = pParse->zJson[i])==0 ) return 0;
- if( c=='{' ){
+ while( safe_isspace(pParse->zJson[i]) ){ i++; }
+ if( (c = pParse->zJson[i])=='{' ){
/* Parse object */
iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
if( iThis<0 ) return -1;
for(j=i+1;;j++){
- while( isspace(pParse->zJson[j]) ){ j++; }
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
x = jsonParseValue(pParse, j);
if( x<0 ){
if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
return -1;
}
@@ -601,17 +663,17 @@
if( pParse->oom ) return -1;
pNode = &pParse->aNode[pParse->nNode-1];
if( pNode->eType!=JSON_STRING ) return -1;
pNode->jnFlags |= JNODE_LABEL;
j = x;
- while( isspace(pParse->zJson[j]) ){ j++; }
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
if( pParse->zJson[j]!=':' ) return -1;
j++;
x = jsonParseValue(pParse, j);
if( x<0 ) return -1;
j = x;
- while( isspace(pParse->zJson[j]) ){ j++; }
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
c = pParse->zJson[j];
if( c==',' ) continue;
if( c!='}' ) return -1;
break;
}
@@ -620,18 +682,18 @@
}else if( c=='[' ){
/* Parse array */
iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
if( iThis<0 ) return -1;
for(j=i+1;;j++){
- while( isspace(pParse->zJson[j]) ){ j++; }
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
x = jsonParseValue(pParse, j);
if( x<0 ){
if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
return -1;
}
j = x;
- while( isspace(pParse->zJson[j]) ){ j++; }
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
c = pParse->zJson[j];
if( c==',' ) continue;
if( c!=']' ) return -1;
break;
}
@@ -656,21 +718,21 @@
jsonParseAddNode(pParse, JSON_STRING, j+1-i, &pParse->zJson[i]);
if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
return j+1;
}else if( c=='n'
&& strncmp(pParse->zJson+i,"null",4)==0
- && !isalnum(pParse->zJson[i+4]) ){
+ && !safe_isalnum(pParse->zJson[i+4]) ){
jsonParseAddNode(pParse, JSON_NULL, 0, 0);
return i+4;
}else if( c=='t'
&& strncmp(pParse->zJson+i,"true",4)==0
- && !isalnum(pParse->zJson[i+4]) ){
+ && !safe_isalnum(pParse->zJson[i+4]) ){
jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
return i+4;
}else if( c=='f'
&& strncmp(pParse->zJson+i,"false",5)==0
- && !isalnum(pParse->zJson[i+5]) ){
+ && !safe_isalnum(pParse->zJson[i+5]) ){
jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
return i+5;
}else if( c=='-' || (c>='0' && c<='9') ){
/* Parse number */
u8 seenDP = 0;
@@ -705,10 +767,12 @@
return j;
}else if( c=='}' ){
return -2; /* End of {...} */
}else if( c==']' ){
return -3; /* End of [...] */
+ }else if( c==0 ){
+ return 0; /* End of file */
}else{
return -1; /* Syntax error */
}
}
@@ -729,11 +793,11 @@
if( zJson==0 ) return 1;
pParse->zJson = zJson;
i = jsonParseValue(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
- while( isspace(zJson[i]) ) i++;
+ while( safe_isspace(zJson[i]) ) i++;
if( zJson[i] ) i = -1;
}
if( i<=0 ){
if( pCtx!=0 ){
if( pParse->oom ){
@@ -787,10 +851,24 @@
return SQLITE_NOMEM;
}
jsonParseFillInParentage(pParse, 0, 0);
return SQLITE_OK;
}
+
+/*
+** Compare the OBJECT label at pNode against zKey,nKey. Return true on
+** a match.
+*/
+static int jsonLabelCompare(JsonNode *pNode, const char *zKey, int nKey){
+ if( pNode->jnFlags & JNODE_RAW ){
+ if( pNode->n!=nKey ) return 0;
+ return strncmp(pNode->u.zJContent, zKey, nKey)==0;
+ }else{
+ if( pNode->n!=nKey+2 ) return 0;
+ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+ }
+}
/* forward declaration */
static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
/*
@@ -818,11 +896,16 @@
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
for(i=1; zPath[i] && zPath[i]!='"'; i++){}
nKey = i-1;
- if( zPath[i] ) i++;
+ if( zPath[i] ){
+ i++;
+ }else{
+ *pzErr = zPath;
+ return 0;
+ }
}else{
zKey = zPath;
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
}
@@ -831,13 +914,11 @@
return 0;
}
j = 1;
for(;;){
while( j<=pRoot->n ){
- if( pRoot[j].n==nKey+2
- && strncmp(&pRoot[j].u.zJContent[1],zKey,nKey)==0
- ){
+ if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
}
j++;
j += jsonNodeSize(&pRoot[j]);
}
@@ -860,23 +941,23 @@
pRoot->jnFlags |= JNODE_APPEND;
pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
}
return pNode;
}
- }else if( zPath[0]=='[' && isdigit(zPath[1]) ){
+ }else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
if( pRoot->eType!=JSON_ARRAY ) return 0;
i = 0;
- zPath++;
- while( isdigit(zPath[0]) ){
- i = i*10 + zPath[0] - '0';
- zPath++;
+ j = 1;
+ while( safe_isdigit(zPath[j]) ){
+ i = i*10 + zPath[j] - '0';
+ j++;
}
- if( zPath[0]!=']' ){
+ if( zPath[j]!=']' ){
*pzErr = zPath;
return 0;
}
- zPath++;
+ zPath += j + 1;
j = 1;
for(;;){
while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
j += jsonNodeSize(&pRoot[j]);
@@ -900,11 +981,11 @@
pRoot->u.iAppend = iStart - iRoot;
pRoot->jnFlags |= JNODE_APPEND;
}
return pNode;
}
- }else if( zPath[0]!=0 ){
+ }else{
*pzErr = zPath;
}
return 0;
}
@@ -958,30 +1039,30 @@
int *pApnd, /* Append nodes to complete path if not NULL */
sqlite3_context *pCtx /* Report errors here, if not NULL */
){
const char *zErr = 0;
JsonNode *pNode = 0;
+ char *zMsg;
if( zPath==0 ) return 0;
if( zPath[0]!='$' ){
zErr = zPath;
goto lookup_err;
}
zPath++;
pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
- return pNode;
+ if( zErr==0 ) return pNode;
lookup_err:
pParse->nErr++;
- if( zErr!=0 && pCtx!=0 ){
- char *z = jsonPathSyntaxError(zErr);
- if( z ){
- sqlite3_result_error(pCtx, z, -1);
- sqlite3_free(z);
- }else{
- sqlite3_result_error_nomem(pCtx);
- }
+ assert( zErr!=0 && pCtx!=0 );
+ zMsg = jsonPathSyntaxError(zErr);
+ if( zMsg ){
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+ }else{
+ sqlite3_result_error_nomem(pCtx);
}
return 0;
}
@@ -1100,27 +1181,26 @@
sqlite3_value **argv
){
JsonParse x; /* The parse */
sqlite3_int64 n = 0;
u32 i;
+ JsonNode *pNode;
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- if( x.nNode ){
- JsonNode *pNode;
- if( argc==2 ){
- const char *zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(&x, zPath, 0, ctx);
- }else{
- pNode = x.aNode;
- }
- if( pNode==0 ){
- x.nErr = 1;
- }else if( pNode->eType==JSON_ARRAY ){
- assert( (pNode->jnFlags & JNODE_APPEND)==0 );
- for(i=1; i<=pNode->n; n++){
- i += jsonNodeSize(&pNode[i]);
- }
+ assert( x.nNode );
+ if( argc==2 ){
+ const char *zPath = (const char*)sqlite3_value_text(argv[1]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ }else{
+ pNode = x.aNode;
+ }
+ if( pNode==0 ){
+ x.nErr = 1;
+ }else if( pNode->eType==JSON_ARRAY ){
+ assert( (pNode->jnFlags & JNODE_APPEND)==0 );
+ for(i=1; i<=pNode->n; n++){
+ i += jsonNodeSize(&pNode[i]);
}
}
if( x.nErr==0 ) sqlite3_result_int64(ctx, n);
jsonParseReset(&x);
}
@@ -1195,11 +1275,11 @@
jsonInit(&jx, ctx);
jsonAppendChar(&jx, '{');
for(i=0; ijnFlags |= JNODE_REMOVE;
- }
- if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
- jsonReturnJson(x.aNode, ctx, 0);
- }
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i++){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ if( zPath==0 ) goto remove_done;
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) goto remove_done;
+ if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
+ }
+ if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
+ jsonReturnJson(x.aNode, ctx, 0);
}
remove_done:
jsonParseReset(&x);
}
@@ -1267,25 +1346,24 @@
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, "replace");
return;
}
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- if( x.nNode ){
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(&x, zPath, 0, ctx);
- if( x.nErr ) goto replace_err;
- if( pNode ){
- pNode->jnFlags |= (u8)JNODE_REPLACE;
- pNode->iVal = (u8)(i+1);
- }
- }
- if( x.aNode[0].jnFlags & JNODE_REPLACE ){
- sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
- }else{
- jsonReturnJson(x.aNode, ctx, argv);
- }
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i+=2){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) goto replace_err;
+ if( pNode ){
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
+ pNode->iVal = (u8)(i+1);
+ }
+ }
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+ sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
+ }else{
+ jsonReturnJson(x.aNode, ctx, argv);
}
replace_err:
jsonParseReset(&x);
}
@@ -1317,30 +1395,29 @@
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
return;
}
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- if( x.nNode ){
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- bApnd = 0;
- pNode = jsonLookup(&x, zPath, &bApnd, ctx);
- if( x.oom ){
- sqlite3_result_error_nomem(ctx);
- goto jsonSetDone;
- }else if( x.nErr ){
- goto jsonSetDone;
- }else if( pNode && (bApnd || bIsSet) ){
- pNode->jnFlags |= (u8)JNODE_REPLACE;
- pNode->iVal = (u8)(i+1);
- }
- }
- if( x.aNode[0].jnFlags & JNODE_REPLACE ){
- sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
- }else{
- jsonReturnJson(x.aNode, ctx, argv);
- }
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i+=2){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ bApnd = 0;
+ pNode = jsonLookup(&x, zPath, &bApnd, ctx);
+ if( x.oom ){
+ sqlite3_result_error_nomem(ctx);
+ goto jsonSetDone;
+ }else if( x.nErr ){
+ goto jsonSetDone;
+ }else if( pNode && (bApnd || bIsSet) ){
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
+ pNode->iVal = (u8)(i+1);
+ }
+ }
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+ sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
+ }else{
+ jsonReturnJson(x.aNode, ctx, argv);
}
jsonSetDone:
jsonParseReset(&x);
}
@@ -1356,23 +1433,22 @@
int argc,
sqlite3_value **argv
){
JsonParse x; /* The parse */
const char *zPath;
+ JsonNode *pNode;
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- if( x.nNode ){
- JsonNode *pNode;
- if( argc==2 ){
- zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(&x, zPath, 0, ctx);
- }else{
- pNode = x.aNode;
- }
- if( pNode ){
- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
- }
+ assert( x.nNode );
+ if( argc==2 ){
+ zPath = (const char*)sqlite3_value_text(argv[1]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ }else{
+ pNode = x.aNode;
+ }
+ if( pNode ){
+ sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
}
jsonParseReset(&x);
}
/*
@@ -1388,13 +1464,11 @@
){
JsonParse x; /* The parse */
int rc = 0;
UNUSED_PARAM(argc);
- if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0
- && x.nNode>0
- ){
+ if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0 ){
rc = 1;
}
jsonParseReset(&x);
sqlite3_result_int(ctx, rc);
}
@@ -1667,11 +1741,11 @@
const char *zRoot = p->zRoot;
if( zRoot==0 ) zRoot = "$";
sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
break;
}
- default: {
+ case JEACH_JSON: {
assert( i==JEACH_JSON );
sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
break;
}
}
@@ -1743,19 +1817,10 @@
UNUSED_PARAM(argc);
jsonEachCursorReset(p);
if( idxNum==0 ) return SQLITE_OK;
z = (const char*)sqlite3_value_text(argv[0]);
if( z==0 ) return SQLITE_OK;
- if( idxNum&2 ){
- zRoot = (const char*)sqlite3_value_text(argv[1]);
- if( zRoot==0 ) return SQLITE_OK;
- if( zRoot[0]!='$' ){
- sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot);
- return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
- }
- }
n = sqlite3_value_bytes(argv[0]);
p->zJson = sqlite3_malloc64( n+1 );
if( p->zJson==0 ) return SQLITE_NOMEM;
memcpy(p->zJson, z, (size_t)n+1);
if( jsonParse(&p->sParse, 0, p->zJson) ){
@@ -1769,19 +1834,25 @@
return rc;
}else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
jsonEachCursorReset(p);
return SQLITE_NOMEM;
}else{
- JsonNode *pNode;
+ JsonNode *pNode = 0;
if( idxNum==3 ){
const char *zErr = 0;
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
+ if( zRoot==0 ) return SQLITE_OK;
n = sqlite3_value_bytes(argv[1]);
p->zRoot = sqlite3_malloc64( n+1 );
if( p->zRoot==0 ) return SQLITE_NOMEM;
memcpy(p->zRoot, zRoot, (size_t)n+1);
- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
- if( p->sParse.nErr ){
+ if( zRoot[0]!='$' ){
+ zErr = zRoot;
+ }else{
+ pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
+ }
+ if( zErr ){
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}else if( pNode==0 ){
@@ -1794,10 +1865,11 @@
p->eType = pNode->eType;
if( p->eType>=JSON_ARRAY ){
pNode->u.iKey = 0;
p->iEnd = p->i + pNode->n + 1;
if( p->bRecursive ){
+ p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
p->i--;
}
}else{
p->i++;
@@ -1804,11 +1876,11 @@
}
}else{
p->iEnd = p->i+1;
}
}
- return p->sParse.oom ? SQLITE_NOMEM : SQLITE_OK;
+ return SQLITE_OK;
}
/* The methods of the json_each virtual table */
static sqlite3_module jsonEachModule = {
0, /* iVersion */
Index: main.mk
==================================================================
--- main.mk
+++ main.mk
@@ -433,11 +433,18 @@
# Databases containing fuzzer test cases
#
FUZZDATA = \
$(TOP)/test/fuzzdata1.db \
$(TOP)/test/fuzzdata2.db \
- $(TOP)/test/fuzzdata3.db
+ $(TOP)/test/fuzzdata3.db \
+ $(TOP)/test/fuzzdata4.db
+
+# 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_SRC = $(TOP)/ext/misc/json1.c
# Standard options to testfixture
#
TESTOPTS = --verbose=file --output=test-out.txt
@@ -448,27 +455,28 @@
libsqlite3.a: $(LIBOBJ)
$(AR) libsqlite3.a $(LIBOBJ)
$(RANLIB) libsqlite3.a
-sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TOP)/ext/misc/json1.c
- $(TCCX) $(READLINE_FLAGS) -DSQLITE_ENABLE_JSON1 -o sqlite3$(EXE) \
- $(TOP)/src/shell.c $(TOP)/ext/misc/json1.c \
+sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(JSON1_DEP)
+ $(TCCX) $(READLINE_FLAGS) $(JSON1_OPT) -o sqlite3$(EXE) \
+ $(TOP)/src/shell.c $(JSON1_SRC) \
libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
-fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
+fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP)
$(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
- $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) $(THREADLIB)
+ $(JSON1_OPT) $(TOP)/tool/fuzzershell.c $(JSON1_SRC) sqlite3.c \
+ $(TLIBS) $(THREADLIB)
-fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
+fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP)
$(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
- -DSQLITE_ENABLE_MEMSYS5 \
- $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) $(THREADLIB)
+ -DSQLITE_ENABLE_MEMSYS5 $(JSON1_OPT) \
+ $(TOP)/test/fuzzcheck.c $(JSON1_SRC) sqlite3.c $(TLIBS) $(THREADLIB)
mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
$(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
$(TLIBS) $(THREADLIB)
Index: src/backup.c
==================================================================
--- src/backup.c
+++ src/backup.c
@@ -766,10 +766,14 @@
memset(&b, 0, sizeof(b));
b.pSrcDb = pFrom->db;
b.pSrc = pFrom;
b.pDest = pTo;
b.iNext = 1;
+
+#ifdef SQLITE_HAS_CODEC
+ sqlite3PagerAlignReserve(sqlite3BtreePager(pTo), sqlite3BtreePager(pFrom));
+#endif
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
** file. By passing this as the number of pages to copy to
** sqlite3_backup_step(), we can guarantee that the copy finishes
** within a single call (unless an error occurs). The assert() statement
Index: src/btree.c
==================================================================
--- src/btree.c
+++ src/btree.c
@@ -8211,11 +8211,12 @@
}
if( rc==SQLITE_OK ){
if( bSkipnext ){
assert( bPreserve && pCur->iPage==iCellDepth );
- assert( pPage->nCell>0 && iCellIdx<=pPage->nCell );
+ assert( pPage==pCur->apPage[pCur->iPage] );
+ assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell );
pCur->eState = CURSOR_SKIPNEXT;
if( iCellIdx>=pPage->nCell ){
pCur->skipNext = -1;
pCur->aiIdx[iCellDepth] = pPage->nCell-1;
}else{
@@ -8946,10 +8947,14 @@
}
}
#endif
iPage = get4byte(pOvflData);
sqlite3PagerUnref(pOvflPage);
+
+ if( isFreeList && N<(iPage!=0) ){
+ checkAppendMsg(pCheck, "free-page count in header is too small");
+ }
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/*
Index: src/delete.c
==================================================================
--- src/delete.c
+++ src/delete.c
@@ -410,10 +410,11 @@
*/
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) || bComplex || eOnePass!=ONEPASS_OFF );
/* Keep track of the number of rows to be deleted */
if( db->flags & SQLITE_CountRows ){
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
}
Index: src/os_unix.c
==================================================================
--- src/os_unix.c
+++ src/os_unix.c
@@ -5414,11 +5414,12 @@
*/
static void unixShmBarrier(
sqlite3_file *fd /* Database file holding the shared memory */
){
UNUSED_PARAMETER(fd);
- unixEnterMutex();
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ unixEnterMutex(); /* Also mutex, for redundancy */
unixLeaveMutex();
}
/*
** Close a connection to shared-memory. Delete the underlying
Index: src/os_win.c
==================================================================
--- src/os_win.c
+++ src/os_win.c
@@ -3857,12 +3857,12 @@
*/
static void winShmBarrier(
sqlite3_file *fd /* Database holding the shared memory */
){
UNUSED_PARAMETER(fd);
- /* MemoryBarrier(); // does not work -- do not know why not */
- winShmEnterMutex();
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ winShmEnterMutex(); /* Also mutex, for redundancy */
winShmLeaveMutex();
}
/*
** This function is called to obtain a pointer to region iRegion of the
Index: src/pager.c
==================================================================
--- src/pager.c
+++ src/pager.c
@@ -2113,10 +2113,24 @@
}
}
#else
# define pagerReportSize(X) /* No-op if we do not support a codec */
#endif
+
+#ifdef SQLITE_HAS_CODEC
+/*
+** Make sure the number of reserved bits is the same in the destination
+** pager as it is in the source. This comes up when a VACUUM changes the
+** number of reserved bits to the "optimal" amount.
+*/
+void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){
+ if( pDest->nReserve!=pSrc->nReserve ){
+ pDest->nReserve = pSrc->nReserve;
+ pagerReportSize(pDest);
+ }
+}
+#endif
/*
** Read a single page from either the journal file (if isMainJrnl==1) or
** from the sub-journal (if isMainJrnl==0) and playback that page.
** The page begins at offset *pOffset into the file. The *pOffset
Index: src/pager.h
==================================================================
--- src/pager.h
+++ src/pager.h
@@ -116,10 +116,13 @@
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
/* Functions used to configure a Pager object. */
void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
int sqlite3PagerSetPagesize(Pager*, u32*, int);
+#ifdef SQLITE_HAS_CODEC
+void sqlite3PagerAlignReserve(Pager*,Pager*);
+#endif
int sqlite3PagerMaxPageCount(Pager*, int);
void sqlite3PagerSetCachesize(Pager*, int);
void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64);
void sqlite3PagerShrink(Pager*);
void sqlite3PagerSetFlags(Pager*,unsigned);
Index: src/pcache1.c
==================================================================
--- src/pcache1.c
+++ src/pcache1.c
@@ -411,11 +411,11 @@
** this mutex is not held. */
assert( pcache1.separateCache==0 );
assert( pCache->pGroup==&pcache1.grp );
pcache1LeaveMutex(pCache->pGroup);
#endif
- if( benignMalloc ) sqlite3BeginBenignMalloc();
+ if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
pPg = pcache1Alloc(pCache->szPage);
p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
if( !pPg || !p ){
pcache1Free(pPg);
@@ -424,11 +424,11 @@
}
#else
pPg = pcache1Alloc(pCache->szAlloc);
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
#endif
- if( benignMalloc ) sqlite3EndBenignMalloc();
+ if( benignMalloc ){ sqlite3EndBenignMalloc(); }
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
pcache1EnterMutex(pCache->pGroup);
#endif
if( pPg==0 ) return 0;
p->page.pBuf = pPg;
Index: src/resolve.c
==================================================================
--- src/resolve.c
+++ src/resolve.c
@@ -353,12 +353,17 @@
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
/*
** Perhaps the name is a reference to the ROWID
*/
- if( cnt==0 && cntTab==1 && pMatch && sqlite3IsRowid(zCol)
- && VisibleRowid(pMatch->pTab) ){
+ if( cnt==0
+ && cntTab==1
+ && pMatch
+ && (pNC->ncFlags & NC_IdxExpr)==0
+ && sqlite3IsRowid(zCol)
+ && VisibleRowid(pMatch->pTab)
+ ){
cnt = 1;
pExpr->iColumn = -1; /* IMP: R-44911-55124 */
pExpr->affinity = SQLITE_AFF_INTEGER;
}
Index: src/select.c
==================================================================
--- src/select.c
+++ src/select.c
@@ -4265,13 +4265,16 @@
return WRC_Abort;
}
pTab->nRef++;
#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
if( pTab->pSelect || IsVirtual(pTab) ){
- /* We reach here if the named table is a really a view */
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
assert( pFrom->pSelect==0 );
+ if( pFrom->fg.isTabFunc && !IsVirtual(pTab) ){
+ sqlite3ErrorMsg(pParse, "'%s' is not a function", pTab->zName);
+ return WRC_Abort;
+ }
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
sqlite3SelectSetName(pFrom->pSelect, pTab->zName);
sqlite3WalkSelect(pWalker, pFrom->pSelect);
}
#endif
Index: src/sqlite.h.in
==================================================================
--- src/sqlite.h.in
+++ src/sqlite.h.in
@@ -3629,11 +3629,11 @@
** name must be given in UTF-8 even if the original statement
** was prepared from UTF-16 text using [sqlite3_prepare16_v2()].
**
** See also: [sqlite3_bind_blob|sqlite3_bind()],
** [sqlite3_bind_parameter_count()], and
-** [sqlite3_bind_parameter_index()].
+** [sqlite3_bind_parameter_name()].
*/
int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
/*
** CAPI3REF: Reset All Bindings On A Prepared Statement
Index: src/vdbeapi.c
==================================================================
--- src/vdbeapi.c
+++ src/vdbeapi.c
@@ -926,22 +926,23 @@
#if defined(SQLITE_DEBUG) && defined(__GNUC__)
__attribute__((aligned(8)))
#endif
= {
/* .u = */ {0},
- /* .flags = */ MEM_Null,
- /* .enc = */ 0,
- /* .n = */ 0,
- /* .z = */ 0,
- /* .zMalloc = */ 0,
- /* .szMalloc = */ 0,
- /* .iPadding1 = */ 0,
- /* .db = */ 0,
- /* .xDel = */ 0,
+ /* .flags = */ (u16)MEM_Null,
+ /* .enc = */ (u8)0,
+ /* .eSubtype = */ (u8)0,
+ /* .n = */ (int)0,
+ /* .z = */ (char*)0,
+ /* .zMalloc = */ (char*)0,
+ /* .szMalloc = */ (int)0,
+ /* .uTemp = */ (u32)0,
+ /* .db = */ (sqlite3*)0,
+ /* .xDel = */ (void(*)(void*))0,
#ifdef SQLITE_DEBUG
- /* .pScopyFrom = */ 0,
- /* .pFiller = */ 0,
+ /* .pScopyFrom = */ (Mem*)0,
+ /* .pFiller = */ (void*)0,
#endif
};
return &nullMem;
}
Index: src/where.c
==================================================================
--- src/where.c
+++ src/where.c
@@ -180,11 +180,11 @@
int k = pScan->k; /* Where to start scanning */
while( pScan->iEquiv<=pScan->nEquiv ){
iCur = pScan->aiCur[pScan->iEquiv-1];
iColumn = pScan->aiColumn[pScan->iEquiv-1];
- assert( iColumn!=(-2) || pScan->pIdxExpr!=0 );
+ if( iColumn==(-2) && pScan->pIdxExpr==0 ) return 0;
while( (pWC = pScan->pWC)!=0 ){
for(pTerm=pWC->a+k; knTerm; k++, pTerm++){
if( pTerm->leftCursor==iCur
&& pTerm->u.leftColumn==iColumn
&& (iColumn!=(-2)
@@ -191,14 +191,13 @@
|| sqlite3ExprCompare(pTerm->pExpr->pLeft,pScan->pIdxExpr,iCur)==0)
&& (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
){
if( (pTerm->eOperator & WO_EQUIV)!=0
&& pScan->nEquivaiCur)
+ && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN
){
int j;
- pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
- assert( pX->op==TK_COLUMN );
for(j=0; jnEquiv; j++){
if( pScan->aiCur[j]==pX->iTable
&& pScan->aiColumn[j]==pX->iColumn ){
break;
}
Index: src/wherecode.c
==================================================================
--- src/wherecode.c
+++ src/wherecode.c
@@ -63,11 +63,11 @@
** is run and there is an index on (a, b), then this function returns a
** string similar to:
**
** "a=? AND b>?"
*/
-static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){
+static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
Index *pIndex = pLoop->u.btree.pIndex;
u16 nEq = pLoop->u.btree.nEq;
u16 nSkip = pLoop->nSkip;
int i, j;
@@ -164,11 +164,11 @@
zFmt = "INDEX %s";
}
if( zFmt ){
sqlite3StrAccumAppend(&str, " USING ", 7);
sqlite3XPrintf(&str, 0, zFmt, pIdx->zName);
- explainIndexRange(&str, pLoop, pItem->pTab);
+ explainIndexRange(&str, pLoop);
}
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
const char *zRangeOp;
if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
zRangeOp = "=";
@@ -512,12 +512,12 @@
VdbeCoverageIf(v, bRev==0);
VdbeCoverageIf(v, bRev!=0);
sqlite3VdbeJumpHere(v, j);
for(j=0; jaiColumn[j]>=0 );
- VdbeComment((v, "%s", pIdx->pTable->aCol[pIdx->aiColumn[j]].zName));
+ testcase( pIdx->aiColumn[j]==(-2) );
+ VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
}
}
/* Evaluate the equality constraints
*/
Index: test/corrupt2.test
==================================================================
--- test/corrupt2.test
+++ test/corrupt2.test
@@ -15,10 +15,11 @@
#
# $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix corrupt2
# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec
@@ -555,7 +556,54 @@
do_test corrupt2-13.3 {
catchsql { DELETE FROM t1 WHERE rowid < 30; }
} {1 {database disk image is malformed}}
}
}
+
+#-------------------------------------------------------------------------
+# Test that PRAGMA integrity_check detects cases where the freelist-count
+# header field is smaller than the actual number of pages on the freelist.
+#
+
+reset_db
+do_execsql_test 14.0 {
+ PRAGMA auto_vacuum = 0;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(randomblob(3500));
+ DELETE FROM t1;
+}
+
+do_execsql_test 14.1 {
+ PRAGMA integrity_check;
+ PRAGMA freelist_count;
+} {ok 3}
+
+# There are now 3 free pages. Modify the header-field so that it
+# (incorrectly) says that just 2 are free.
+do_test 14.2 {
+ db close
+ hexio_write test.db 36 [hexio_render_int32 2]
+ sqlite3 db test.db
+ execsql { PRAGMA freelist_count }
+} {2}
+
+do_execsql_test 14.3 {
+ PRAGMA integrity_check;
+} {{*** in database main ***
+Main freelist: free-page count in header is too small}}
+
+# Use 2 of the free pages on the free-list.
+#
+do_execsql_test 14.4 {
+ INSERT INTO t1 VALUES(randomblob(2500));
+ PRAGMA freelist_count;
+} {0}
+
+do_execsql_test 14.5 {
+ PRAGMA integrity_check;
+} {{*** in database main ***
+Page 3 is never used}}
+
+
+finish_test
finish_test
Index: test/corruptC.test
==================================================================
--- test/corruptC.test
+++ test/corruptC.test
@@ -186,12 +186,16 @@
hexio_write test.db 3074 [format %02x 0xa0]
sqlite3 db test.db
catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;}
} {1 {database disk image is malformed}}
+
# corruption (seed 179069)
+# Obsolete. With single-pass DELETE the corruption in the
+# main database is not detected.
+if 0 {
do_test corruptC-2.8 {
db close
forcecopy test.bu test.db
# insert corrupt byte(s)
@@ -202,10 +206,11 @@
hexio_write test.db 2139 [format %02x 0x55]
sqlite3 db test.db
catchsql {BEGIN; DELETE FROM t1 WHERE x>13; ROLLBACK;}
} {1 {database disk image is malformed}}
+}
# corruption (seed 170434)
#
# UPDATE: Prior to 3.8.2, this used to return SQLITE_CORRUPT. It no longer
# does. That is Ok, the point of these tests is to verify that no buffer
Index: test/fuzzcheck.c
==================================================================
--- test/fuzzcheck.c
+++ test/fuzzcheck.c
@@ -293,10 +293,42 @@
}else{
sqlite3_free(pBuf);
}
fclose(in);
}
+
+/*
+** Implementation of the "writefile(X,Y)" SQL function. The argument Y
+** is written into file X. The number of bytes written is returned. Or
+** NULL is returned if something goes wrong, such as being unable to open
+** file X for writing.
+*/
+static void writefileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ FILE *out;
+ const char *z;
+ sqlite3_int64 rc;
+ const char *zFile;
+
+ (void)argc;
+ zFile = (const char*)sqlite3_value_text(argv[0]);
+ if( zFile==0 ) return;
+ out = fopen(zFile, "wb");
+ if( out==0 ) return;
+ z = (const char*)sqlite3_value_blob(argv[1]);
+ if( z==0 ){
+ rc = 0;
+ }else{
+ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
+ }
+ fclose(out);
+ sqlite3_result_int64(context, rc);
+}
+
/*
** Load a list of Blob objects from the database
*/
static void blobListLoadFromDb(
@@ -749,10 +781,12 @@
"Read databases and SQL scripts from SOURCE-DB and execute each script against\n"
"each database, checking for crashes and memory leaks.\n"
"Options:\n"
" --cell-size-check Set the PRAGMA cell_size_check=ON\n"
" --dbid N Use only the database where dbid=N\n"
+" --export-db DIR Write databases to files(s) in DIR. Works with --dbid\n"
+" --export-sql DIR Write SQL to file(s) in DIR. Also works with --sqlid\n"
" --help Show this help text\n"
" -q Reduced output\n"
" --quiet Reduced output\n"
" --limit-mem N Limit memory used by test SQLite instance to N bytes\n"
" --limit-vdbe Panic if an sync SQL runs for more than 100,000 cycles\n"
@@ -761,11 +795,11 @@
" -m TEXT Add a description to the database\n"
" --native-vfs Use the native VFS for initially empty database files\n"
" --rebuild Rebuild and vacuum the database file\n"
" --result-trace Show the results of each SQL command\n"
" --sqlid N Use only SQL where sqlid=N\n"
-" --timeline N Abort if any single test case needs more than N seconds\n"
+" --timeout N Abort if any single test case needs more than N seconds\n"
" -v Increased output\n"
" --verbose Increased output\n"
);
}
@@ -797,10 +831,12 @@
const char *zFailCode = 0; /* Value of the TEST_FAILURE environment variable */
int cellSzCkFlag = 0; /* --cell-size-check */
int sqlFuzz = 0; /* True for SQL fuzz testing. False for DB fuzz */
int iTimeout = 120; /* Default 120-second timeout */
int nMem = 0; /* Memory limit */
+ char *zExpDb = 0; /* Write Databases to files in this directory */
+ char *zExpSql = 0; /* Write SQL to files in this directory */
iBegin = timeOfDay();
#ifdef __unix__
signal(SIGALRM, timeoutHandler);
#endif
@@ -815,10 +851,18 @@
cellSzCkFlag = 1;
}else
if( strcmp(z,"dbid")==0 ){
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
onlyDbid = integerValue(argv[++i]);
+ }else
+ if( strcmp(z,"export-db")==0 ){
+ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
+ zExpDb = argv[++i];
+ }else
+ if( strcmp(z,"export-sql")==0 ){
+ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
+ zExpSql = argv[++i];
}else
if( strcmp(z,"help")==0 ){
showHelp();
return 0;
}else
@@ -941,10 +985,54 @@
if( rc ) fatalError("cannot commit the transaction: %s", sqlite3_errmsg(db));
rebuild_database(db);
sqlite3_close(db);
return 0;
}
+ if( zExpDb!=0 || zExpSql!=0 ){
+ sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
+ writefileFunc, 0, 0);
+ if( zExpDb!=0 ){
+ const char *zExDb =
+ "SELECT writefile(printf('%s/db%06d.db',?1,dbid),dbcontent),"
+ " dbid, printf('%s/db%06d.db',?1,dbid), length(dbcontent)"
+ " FROM db WHERE ?2<0 OR dbid=?2;";
+ rc = sqlite3_prepare_v2(db, zExDb, -1, &pStmt, 0);
+ if( rc ) fatalError("cannot prepare statement [%s]: %s",
+ zExDb, sqlite3_errmsg(db));
+ sqlite3_bind_text64(pStmt, 1, zExpDb, strlen(zExpDb),
+ SQLITE_STATIC, SQLITE_UTF8);
+ sqlite3_bind_int(pStmt, 2, onlyDbid);
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ printf("write db-%d (%d bytes) into %s\n",
+ sqlite3_column_int(pStmt,1),
+ sqlite3_column_int(pStmt,3),
+ sqlite3_column_text(pStmt,2));
+ }
+ sqlite3_finalize(pStmt);
+ }
+ if( zExpSql!=0 ){
+ const char *zExSql =
+ "SELECT writefile(printf('%s/sql%06d.txt',?1,sqlid),sqltext),"
+ " sqlid, printf('%s/sql%06d.txt',?1,sqlid), length(sqltext)"
+ " FROM xsql WHERE ?2<0 OR sqlid=?2;";
+ rc = sqlite3_prepare_v2(db, zExSql, -1, &pStmt, 0);
+ if( rc ) fatalError("cannot prepare statement [%s]: %s",
+ zExSql, sqlite3_errmsg(db));
+ sqlite3_bind_text64(pStmt, 1, zExpSql, strlen(zExpSql),
+ SQLITE_STATIC, SQLITE_UTF8);
+ sqlite3_bind_int(pStmt, 2, onlySqlid);
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ printf("write sql-%d (%d bytes) into %s\n",
+ sqlite3_column_int(pStmt,1),
+ sqlite3_column_int(pStmt,3),
+ sqlite3_column_text(pStmt,2));
+ }
+ sqlite3_finalize(pStmt);
+ }
+ sqlite3_close(db);
+ return 0;
+ }
/* Load all SQL script content and all initial database images from the
** source db
*/
blobListLoadFromDb(db, "SELECT sqlid, sqltext FROM xsql", onlySqlid,
@@ -1037,10 +1125,16 @@
openFlags |= SQLITE_OPEN_MEMORY;
zVfs = 0;
}
rc = sqlite3_open_v2("main.db", &db, openFlags, zVfs);
if( rc ) fatalError("cannot open inmem database");
+#ifdef SQLITE_ENABLE_JSON1
+ {
+ extern int sqlite3_json_init(sqlite3*);
+ sqlite3_json_init(db);
+ }
+#endif
if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags);
setAlarm(iTimeout);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( sqlFuzz || vdbeLimitFlag ){
sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag);
ADDED test/fuzzdata4.db
Index: test/fuzzdata4.db
==================================================================
--- /dev/null
+++ test/fuzzdata4.db
cannot compute difference between binary files
Index: test/indexexpr1.test
==================================================================
--- test/indexexpr1.test
+++ test/indexexpr1.test
@@ -216,7 +216,43 @@
do_execsql_test indexexpr1-510eqp {
EXPLAIN QUERY PLAN
SELECT substr(a,4,3) AS k FROM cnt, t5 WHERE k=printf('%03d',x);
} {/USING INDEX t5ax/}
+# Skip-scan on an indexed expression
+#
+do_execsql_test indexexpr1-600 {
+ DROP TABLE IF EXISTS t4;
+ CREATE TABLE t4(a,b,c,d,e,f,g,h,i);
+ CREATE INDEX t4all ON t4(a,b,cx;
} {}
-do_execsql_test json1-4.6 {
+do_execsql_test json101-4.6 {
SELECT x FROM j1 WHERE json_replace(x)<>x;
} {}
-do_execsql_test json1-4.7 {
+do_execsql_test json101-4.7 {
SELECT x FROM j1 WHERE json_set(x)<>x;
} {}
-do_execsql_test json1-4.8 {
+do_execsql_test json101-4.8 {
SELECT x FROM j1 WHERE json_insert(x)<>x;
} {}
# json_extract(JSON,'$') will return objects and arrays without change.
#
@@ -300,8 +305,20 @@
SELECT j2.rowid, jx.rowid, fullkey, path, key
FROM j2, json_tree(j2.json) AS jx
WHERE jx.value<>jx.atom AND type NOT IN ('array','object');
} {}
+do_execsql_test json-6.1 {
+ SELECT json_valid('{"a":55,"b":72,}');
+} {0}
+do_execsql_test json-6.2 {
+ SELECT json_valid('{"a":55,"b":72}');
+} {1}
+do_execsql_test json-6.3 {
+ SELECT json_valid('["a",55,"b",72,]');
+} {0}
+do_execsql_test json-6.4 {
+ SELECT json_valid('["a",55,"b",72]');
+} {1}
finish_test
Index: test/json102.test
==================================================================
--- test/json102.test
+++ test/json102.test
@@ -275,7 +275,23 @@
FROM big, json_tree(big.json)
WHERE json_tree.key='uuid'
AND json_tree.value='6fa5181e-5721-11e5-a04e-57f3d7b32808';
} {123}
} ;# end ifcapable vtab
+
+#-------------------------------------------------------------------------
+# Test that json_valid() correctly identifies non-ascii range
+# characters as non-whitespace.
+#
+do_execsql_test json102-1201 { SELECT json_valid(char(32) || '"xyz"') } 1
+do_execsql_test json102-1202 { SELECT json_valid(char(200) || '"xyz"') } 0
+
+# Off-by-one error in jsonAppendString()
+#
+for {set i 0} {$i<100} {incr i} {
+ set str abcdef[string repeat \" [expr {$i+50}]]uvwxyz
+ do_test json102-[format %d [expr {$i+1300}]] {
+ db eval {SELECT json_extract(json_array($::str),'$[0]')==$::str}
+ } {1}
+}
finish_test
Index: test/orderby9.test
==================================================================
--- test/orderby9.test
+++ test/orderby9.test
@@ -25,28 +25,38 @@
CREATE TABLE t1(x);
WITH RECURSIVE
c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
INSERT INTO t1 SELECT x FROM c;
}
+
+# Some versions of TCL are unable to [lsort -int] for
+# 64-bit integers. So we write our own comparison
+# routine.
+proc bigintcompare {a b} {
+ set x [expr {$a-$b}]
+ if {$x<0} {return -1}
+ if {$x>0} {return +1}
+ return 0
+}
do_test 1.0 {
set l1 {}
# If random() is only evaluated once and then reused for each row, then
# the output should appear in sorted order. If random() is evaluated
# separately for the result set and the ORDER BY clause, then the output
# order will be random.
db eval {SELECT random() AS y FROM t1 ORDER BY 1;} {lappend l1 $y}
- expr {$l1==[lsort -int $l1]}
+ expr {$l1==[lsort -command bigintcompare $l1]}
} {1}
do_test 1.1 {
set l1 {}
db eval {SELECT random() AS y FROM t1 ORDER BY random();} {lappend l1 $y}
- expr {$l1==[lsort -int $l1]}
+ expr {$l1==[lsort -command bigintcompare $l1]}
} {1}
do_test 1.2 {
set l1 {}
db eval {SELECT random() AS y FROM t1 ORDER BY +random();} {lappend l1 $y}
- expr {$l1==[lsort -int $l1]}
+ expr {$l1==[lsort -command bigintcompare $l1]}
} {0}
finish_test
Index: test/releasetest.tcl
==================================================================
--- test/releasetest.tcl
+++ test/releasetest.tcl
@@ -295,10 +295,11 @@
puts $::LOG [lindex $args 1]
} else {
puts [lindex $args 0]
puts $::LOG [lindex $args 0]
}
+ flush $::LOG
}
puts $LOG "$argv0 $argv"
set tm0 [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S} -gmt 1]
puts $LOG "start-time: $tm0 UTC"
Index: test/spellfix2.test
==================================================================
--- test/spellfix2.test
+++ test/spellfix2.test
@@ -27,36 +27,39 @@
INSERT INTO demo(word) VALUES ('amsterdamlaan');
}
do_execsql_test 1.1 {
SELECT word, distance, matchlen FROM demo
- WHERE word MATCH 'amstedam*' AND top=3;
+ WHERE word MATCH 'amstedam*' AND top=3
+ ORDER BY +word;
} {
amsterdam 100 9
- amsterdammetje 100 9
amsterdamania 100 9
+ amsterdammetje 100 9
}
do_execsql_test 1.2 {
SELECT word, distance, matchlen FROM demo WHERE
- word MATCH 'amstedam*' AND top=3 AND distance <= 100;
+ word MATCH 'amstedam*' AND top=3 AND distance <= 100
+ ORDER BY +word;
} {
amsterdam 100 9
- amsterdammetje 100 9
amsterdamania 100 9
+ amsterdammetje 100 9
}
do_execsql_test 1.3 {
SELECT word, distance, matchlen FROM demo WHERE
- word MATCH 'amstedam*' AND distance <= 100;
+ word MATCH 'amstedam*' AND distance <= 100
+ ORDER BY +word;
} {
amsterdam 100 9
- amsterdammetje 100 9
amsterdamania 100 9
- amsterdamweg 100 9
+ amsterdamlaan 100 9
+ amsterdammetje 100 9
amsterdamsestraat 100 9
- amsterdamlaan 100 9
+ amsterdamweg 100 9
}
do_test 1.4 {
foreach l {a b c d e f g h i j k l m n o p q r s t u v w x y z} {
execsql { INSERT INTO demo(word) VALUES ('amsterdam' || $l) }
@@ -109,6 +112,5 @@
amsterdamc 100 9 amsterdamg 100 9
}
finish_test
-
Index: test/sqllimits1.test
==================================================================
--- test/sqllimits1.test
+++ test/sqllimits1.test
@@ -641,22 +641,26 @@
# Columns in a view definition:
set cols [list]
for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
lappend cols "c$i"
}
- catchsql "CREATE VIEW v1 AS SELECT [join $cols ,] FROM t1;"
+ execsql "CREATE VIEW v1 AS SELECT [join $cols ,] FROM t1;"
+ catchsql {SELECT * FROM v1}
} {1 {too many columns in result set}}
do_test sqllimits1-8.9 {
# Columns in a view definition (testing * expansion):
set cols [list]
for {set i 0} {$i < $SQLITE_LIMIT_COLUMN} {incr i} {
lappend cols "c$i"
}
+ execsql {DROP VIEW IF EXISTS v1}
catchsql "CREATE TABLE t2([join $cols ,])"
catchsql "CREATE VIEW v1 AS SELECT *, c1 AS o FROM t2;"
+ catchsql "SELECT * FROM v1"
} {1 {too many columns in result set}}
+
do_test sqllimits1-8.10 {
# ORDER BY columns
set cols [list]
for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
lappend cols c
Index: test/tabfunc01.test
==================================================================
--- test/tabfunc01.test
+++ test/tabfunc01.test
@@ -52,10 +52,25 @@
SELECT rowid, * FROM generate_series(0,32,5) ORDER BY value DESC;
} {1 30 2 25 3 20 4 15 5 10 6 5 7 0}
do_execsql_test tabfunc01-1.10 {
SELECT rowid, * FROM generate_series(0,32,5) ORDER BY +value DESC;
} {7 30 6 25 5 20 4 15 3 10 2 5 1 0}
+
+do_execsql_test tabfunc01-1.20 {
+ CREATE VIEW v1(a,b) AS VALUES(1,2),(3,4);
+ SELECT * FROM v1;
+} {1 2 3 4}
+do_catchsql_test tabfunc01-1.21 {
+ SELECT * FROM v1(55);
+} {1 {'v1' is not a function}}
+do_execsql_test tabfunc01-1.22 {
+ CREATE VIEW v2(x) AS SELECT value FROM generate_series(1,5);
+ SELECT * FROM v2;
+} {1 2 3 4 5}
+do_catchsql_test tabfunc01-1.23 {
+ SELECT * FROM v2(55);
+} {1 {'v2' is not a function}}
do_execsql_test tabfunc01-2.1 {
CREATE TABLE t1(x);
INSERT INTO t1(x) VALUES(2),(3);
SELECT *, '|' FROM t1, generate_series(1,x) ORDER BY 1, 2
Index: test/unique2.test
==================================================================
--- test/unique2.test
+++ test/unique2.test
@@ -72,7 +72,15 @@
} {}
do_test $id.2 {
catchsql {CREATE UNIQUE INDEX t1yz ON t1(y,z)}
} {1 {UNIQUE constraint failed: t1.y, t1.z}}
}
+
+do_catchsql_test 13.1 {
+ CREATE TABLE err1(a,b,c,UNIQUE(rowid));
+} {1 {no such column: rowid}}
+do_catchsql_test 13.2 {
+ CREATE TABLE err1(a,b,c,PRIMARY KEY(rowid));
+} {1 {no such column: rowid}}
+
finish_test
Index: test/wal3.test
==================================================================
--- test/wal3.test
+++ test/wal3.test
@@ -33,14 +33,10 @@
# When a rollback or savepoint rollback occurs, the client may remove
# elements from one of the hash tables in the wal-index. This block
# of test cases tests that nothing appears to go wrong when this is
# done.
#
-set ans 4056
-if {[info exists G(perm:name)] && $G(perm:name)=="memsubsys1"} {
- set ans 4251
-}
do_test wal3-1.0 {
execsql {
PRAGMA cache_size = 2000;
PRAGMA page_size = 1024;
PRAGMA auto_vacuum = off;
@@ -63,12 +59,16 @@
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2048 */
INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 1970; /* 4018 */
COMMIT;
PRAGMA cache_size = 10;
}
- wal_frame_count test.db-wal 1024
-} $ans
+ set x [wal_frame_count test.db-wal 1024]
+ if {$::G(perm:name)=="memsubsys1"} {
+ if {$x==4251 || $x==4290} {set x 4056}
+ }
+ set x
+} 4056
for {set i 1} {$i < 50} {incr i} {
do_test wal3-1.$i.1 {
set str [a_string 800]
Index: test/without_rowid6.test
==================================================================
--- test/without_rowid6.test
+++ test/without_rowid6.test
@@ -110,8 +110,12 @@
SELECT a FROM t1 WHERE b>3 ORDER BY b;
} {/SEARCH TABLE t1 USING PRIMARY KEY .b>../}
do_execsql_test without_rowid6-520 {
PRAGMA index_list(t1);
} {/sqlite_autoindex_t1_1 1 pk/}
+
+do_catchsql_test without_rowid6-600 {
+ CREATE TABLE t6(a,b,c,PRIMARY KEY(a,rowid,b))WITHOUT ROWID;
+} {1 {no such column: rowid}}
finish_test
Index: tool/fuzzershell.c
==================================================================
--- tool/fuzzershell.c
+++ tool/fuzzershell.c
@@ -320,10 +320,11 @@
"Read SQL text from FILE... (or from standard input if FILE... is omitted)\n"
"and then evaluate each block of SQL contained therein.\n"
"Options:\n"
" --autovacuum Enable AUTOVACUUM mode\n"
" --database FILE Use database FILE instead of an in-memory database\n"
+" --disable-lookaside Turn off lookaside memory\n"
" --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n"
" --help Show this help text\n"
" --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n"
" --oom Run each test multiple times in a simulated OOM loop\n"
" --pagesize N Set the page size to N\n"
@@ -455,10 +456,11 @@
sqlite3_int64 iBegin; /* Start time for the whole program */
sqlite3_int64 iStart, iEnd; /* Start and end-times for a test case */
const char *zDbName = 0; /* Name of an on-disk database file to open */
iBegin = timeOfDay();
+ sqlite3_shutdown();
zFailCode = getenv("TEST_FAILURE");
g.zArgv0 = argv[0];
zPrompt = "";
for(i=1; i=argc-1 ) abendError("missing argument on %s\n", argv[i]);
zDbName = argv[i+1];
i += 1;
+ }else
+ if( strcmp(z,"disable-lookaside")==0 ){
+ nLook = 1;
+ szLook = 0;
}else
if( strcmp(z, "f")==0 && i+1