1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Merge the latest enhancements from trunk.

FossilOrigin-Name: 7bde6d4d8cf05e1beb9bdf20b85760dc3e7a76c9
This commit is contained in:
drh
2015-08-31 14:27:29 +00:00
13 changed files with 596 additions and 138 deletions

View File

@@ -566,9 +566,9 @@ libtclsqlite3.la: tclsqlite.lo libsqlite3.la
-version-info "8:6:8" \
-avoid-version
sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
$(LTLINK) $(READLINE_FLAGS) \
-o $@ $(TOP)/src/shell.c libsqlite3.la \
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 \
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h

View File

@@ -390,9 +390,9 @@ CORE_LINK_OPTS = /DEF:sqlite3.def
#
!IFNDEF SHELL_COMPILE_OPTS
!IF $(DYNAMIC_SHELL)!=0
SHELL_COMPILE_OPTS = $(SHELL_CCONV_OPTS) -DSQLITE_API=__declspec(dllimport)
SHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 $(SHELL_CCONV_OPTS) -DSQLITE_API=__declspec(dllimport)
!ELSE
SHELL_COMPILE_OPTS = $(SHELL_CCONV_OPTS)
SHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 $(SHELL_CCONV_OPTS)
!ENDIF
!ENDIF
@@ -1224,8 +1224,8 @@ libsqlite3.lib: $(LIBOBJ)
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
$(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
sqlite3.exe: $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
$(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c \
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 \
/link /pdb:sqlite3sh.pdb $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
sqldiff.exe: $(TOP)\tool\sqldiff.c sqlite3.c sqlite3.h

View File

@@ -21,7 +21,9 @@
** This implementation parses JSON text at 250 MB/s, so it is hard to see
** how JSONB might improve on that.)
*/
#if !defined(_SQLITEINT_H_)
#include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
@@ -80,6 +82,7 @@ static const char * const jsonType[] = {
#define JNODE_REMOVE 0x04 /* Do not output */
#define JNODE_REPLACE 0x08 /* Replace with JsonNode.iVal */
#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */
#define JNODE_JSON 0x20 /* Treat REPLACE as JSON text */
/* A single node of parsed JSON
@@ -105,6 +108,7 @@ struct JsonParse {
const char *zJson; /* Original JSON string */
u32 *aUp; /* Index of parent of each node */
u8 oom; /* Set to true if out of memory */
u8 nErr; /* Number of errors seen */
};
/**************************************************************************
@@ -238,7 +242,8 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
*/
static void jsonAppendValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
sqlite3_value *pValue, /* Value to append */
u8 textIsJson /* Try to treat text values as JSON */
){
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
@@ -255,7 +260,11 @@ static void jsonAppendValue(
case SQLITE_TEXT: {
const char *z = (const char*)sqlite3_value_text(pValue);
u32 n = (u32)sqlite3_value_bytes(pValue);
if( textIsJson ){
jsonAppendRaw(p, z, n);
}else{
jsonAppendString(p, z, n);
}
break;
}
default: {
@@ -355,7 +364,8 @@ static void jsonRenderNode(
if( pNode[j].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ){
if( pNode[j].jnFlags & JNODE_REPLACE ){
jsonAppendSeparator(pOut);
jsonAppendValue(pOut, aReplace[pNode[j].iVal]);
jsonAppendValue(pOut, aReplace[pNode[j].iVal],
(pNode[j].jnFlags & JNODE_JSON)!=0);
}
}else{
jsonAppendSeparator(pOut);
@@ -380,7 +390,8 @@ static void jsonRenderNode(
jsonRenderNode(&pNode[j], pOut, aReplace);
jsonAppendChar(pOut, ':');
if( pNode[j+1].jnFlags & JNODE_REPLACE ){
jsonAppendValue(pOut, aReplace[pNode[j+1].iVal]);
jsonAppendValue(pOut, aReplace[pNode[j+1].iVal],
(pNode[j+1].jnFlags & JNODE_JSON)!=0);
}else{
jsonRenderNode(&pNode[j+1], pOut, aReplace);
}
@@ -397,6 +408,20 @@ static void jsonRenderNode(
}
}
/*
** Return a JsonNode and all its descendents as a JSON string.
*/
static void jsonReturnJson(
JsonNode *pNode, /* Node to return */
sqlite3_context *pCtx, /* Return value for this function */
sqlite3_value **aReplace /* Array of replacement values */
){
JsonString s;
jsonInit(&s, pCtx);
jsonRenderNode(pNode, &s, aReplace);
jsonResult(&s);
}
/*
** Make the JsonNode the return value of the function.
*/
@@ -501,10 +526,7 @@ static void jsonReturn(
}
case JSON_ARRAY:
case JSON_OBJECT: {
JsonString s;
jsonInit(&s, pCtx);
jsonRenderNode(pNode, &s, aReplace);
jsonResult(&s);
jsonReturnJson(pNode, pCtx, aReplace);
break;
}
}
@@ -668,7 +690,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
j++;
c = pParse->zJson[j+1];
}
if( c<'0' || c>'0' ) return -1;
if( c<'0' || c>'9' ) return -1;
continue;
}
break;
@@ -708,8 +730,14 @@ static int jsonParse(
while( isspace(zJson[i]) ) i++;
if( zJson[i] ) i = -1;
}
if( i<0 ){
if( pParse->oom && pCtx!=0 ) sqlite3_result_error_nomem(pCtx);
if( i<=0 ){
if( pCtx!=0 ){
if( pParse->oom ){
sqlite3_result_error_nomem(pCtx);
}else{
sqlite3_result_error(pCtx, "malformed JSON", -1);
}
}
jsonParseReset(pParse);
return 1;
}
@@ -759,7 +787,7 @@ static int jsonParseFindParents(JsonParse *pParse){
}
/* forward declaration */
static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*);
static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
/*
** Search along zPath to find the node specified. Return a pointer
@@ -770,11 +798,12 @@ static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*);
** possible to do so and if no existing node corresponds to zPath. If
** new nodes are appended *pApnd is set to 1.
*/
static JsonNode *jsonLookup(
static JsonNode *jsonLookupStep(
JsonParse *pParse, /* The JSON to search */
u32 iRoot, /* Begin the search at this node */
const char *zPath, /* The path to search */
int *pApnd /* Append nodes to complete path if not NULL */
int *pApnd, /* Append nodes to complete path if not NULL */
const char **pzErr /* Make *pzErr point to any syntax error in zPath */
){
u32 i, j, nKey;
const char *zKey;
@@ -793,14 +822,17 @@ static JsonNode *jsonLookup(
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
}
if( nKey==0 ) return 0;
if( nKey==0 ){
*pzErr = zPath;
return 0;
}
j = 1;
for(;;){
while( j<=pRoot->n ){
if( pRoot[j].n==nKey+2
&& strncmp(&pRoot[j].u.zJContent[1],zKey,nKey)==0
){
return jsonLookup(pParse, iRoot+j+1, &zPath[i], pApnd);
return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
}
j++;
j += jsonNodeSize(&pRoot[j]);
@@ -816,7 +848,7 @@ static JsonNode *jsonLookup(
iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
zPath += i;
pNode = jsonLookupAppend(pParse, zPath, pApnd);
pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
if( pParse->oom ) return 0;
if( pNode ){
pRoot = &pParse->aNode[iRoot];
@@ -834,7 +866,10 @@ static JsonNode *jsonLookup(
i = i*10 + zPath[0] - '0';
zPath++;
}
if( zPath[0]!=']' ) return 0;
if( zPath[0]!=']' ){
*pzErr = zPath;
return 0;
}
zPath++;
j = 1;
for(;;){
@@ -848,13 +883,13 @@ static JsonNode *jsonLookup(
j = 1;
}
if( j<=pRoot->n ){
return jsonLookup(pParse, iRoot+j, zPath, pApnd);
return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
}
if( i==0 && pApnd ){
u32 iStart;
JsonNode *pNode;
iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
pNode = jsonLookupAppend(pParse, zPath, pApnd);
pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
if( pParse->oom ) return 0;
if( pNode ){
pRoot = &pParse->aNode[iRoot];
@@ -863,6 +898,8 @@ static JsonNode *jsonLookup(
}
return pNode;
}
}else if( zPath[0]!=0 ){
*pzErr = zPath;
}
return 0;
}
@@ -874,7 +911,8 @@ static JsonNode *jsonLookup(
static JsonNode *jsonLookupAppend(
JsonParse *pParse, /* Append content to the JSON parse */
const char *zPath, /* Description of content to append */
int *pApnd /* Set this flag to 1 */
int *pApnd, /* Set this flag to 1 */
const char **pzErr /* Make this point to any syntax error */
){
*pApnd = 1;
if( zPath[0]==0 ){
@@ -889,9 +927,76 @@ static JsonNode *jsonLookupAppend(
return 0;
}
if( pParse->oom ) return 0;
return jsonLookup(pParse, pParse->nNode-1, zPath, pApnd);
return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
}
/*
** Return the text of a syntax error message on a JSON path. Space is
** obtained from sqlite3_malloc().
*/
static char *jsonPathSyntaxError(const char *zErr){
return sqlite3_mprintf("JSON path error near '%q'", zErr);
}
/*
** Do a node lookup using zPath. Return a pointer to the node on success.
** Return NULL if not found or if there is an error.
**
** On an error, write an error message into pCtx and increment the
** pParse->nErr counter.
**
** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
** nodes are appended.
**
** If the path starts with $$ then set *pFlags to JNODE_REPLACE|JNODE_JSON
** as a single to the caller that the input text to be inserted should be
** interpreted as JSON rather than as ordinary text.
*/
static JsonNode *jsonLookup(
JsonParse *pParse, /* The JSON to search */
const char *zPath, /* The path to search */
int *pApnd, /* Append nodes to complete path if not NULL */
sqlite3_context *pCtx, /* Report errors here, if not NULL */
u8 *pFlags /* Write JNODE_REPLACE or _REPLACE|_JSON here */
){
const char *zErr = 0;
JsonNode *pNode = 0;
u8 fg = JNODE_REPLACE;
if( zPath==0 ) return 0;
if( zPath[0]!='$' ){
zErr = zPath;
goto lookup_err;
}
zPath++;
if( zPath[0]=='$' ){
if( pFlags==0 ){
zErr = zPath;
goto lookup_err;
}
zPath++;
fg = JNODE_REPLACE|JNODE_JSON;
}
if( pFlags ) *pFlags = fg;
pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
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);
}
}
if( pFlags ) *pFlags = fg;
return 0;
}
/*
** Report the wrong number of arguments for json_insert(), json_replace()
** or json_set().
@@ -906,6 +1011,7 @@ static void jsonWrongNumArgs(
sqlite3_free(zMsg);
}
/****************************************************************************
** SQL functions used for testing and debugging
****************************************************************************/
@@ -952,7 +1058,7 @@ static void jsonTest1Func(
){
JsonParse x; /* The parse */
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
jsonReturn(x.aNode, ctx, 0);
jsonReturnJson(x.aNode, ctx, 0);
jsonParseReset(&x);
}
@@ -993,7 +1099,7 @@ static void jsonArrayFunc(
jsonAppendChar(&jx, '[');
for(i=0; i<argc; i++){
jsonAppendSeparator(&jx);
jsonAppendValue(&jx, argv[i]);
jsonAppendValue(&jx, argv[i], 0);
}
jsonAppendChar(&jx, ']');
jsonResult(&jx);
@@ -1015,37 +1121,36 @@ static void jsonArrayLengthFunc(
JsonParse x; /* The parse */
sqlite3_int64 n = 0;
u32 i;
const char *zPath;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 ) return;
if( zPath[0]!='$' ) return;
zPath++;
}else{
zPath = 0;
}
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0]))==0 ){
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
if( x.nNode ){
JsonNode *pNode = x.aNode;
if( zPath ) pNode = jsonLookup(&x, 0, zPath, 0);
if( pNode->eType==JSON_ARRAY ){
JsonNode *pNode;
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
pNode = jsonLookup(&x, zPath, 0, ctx, 0);
}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);
}
if( !x.oom ) sqlite3_result_int64(ctx, n);
}
/*
** json_extract(JSON, PATH)
** json_extract(JSON, PATH, ...)
**
** Return the element described by PATH. Return NULL if JSON is not
** valid JSON or if there is no PATH element or if PATH is malformed.
** Return the element described by PATH. Return NULL if there is no
** PATH element. If there are multiple PATHs, then return a JSON array
** with the result from each path. Throw an error if the JSON or any PATH
** is malformed.
*/
static void jsonExtractFunc(
sqlite3_context *ctx,
@@ -1055,16 +1160,33 @@ static void jsonExtractFunc(
JsonParse x; /* The parse */
JsonNode *pNode;
const char *zPath;
assert( argc==2 );
zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 ) return;
if( zPath[0]!='$' ) return;
zPath++;
JsonString jx;
int i;
if( argc<2 ) return;
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
pNode = jsonLookup(&x, 0, zPath, 0);
jsonInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
pNode = jsonLookup(&x, zPath, 0, ctx, 0);
if( x.nErr ) break;
if( argc>2 ){
jsonAppendSeparator(&jx);
if( pNode ){
jsonRenderNode(pNode, &jx, 0);
}else{
jsonAppendRaw(&jx, "null", 4);
}
}else if( pNode ){
jsonReturn(pNode, ctx, 0);
}
}
if( argc>2 && i==argc ){
jsonAppendChar(&jx, ']');
jsonResult(&jx);
}
jsonReset(&jx);
jsonParseReset(&x);
}
@@ -1101,7 +1223,7 @@ static void jsonObjectFunc(
n = (u32)sqlite3_value_bytes(argv[i]);
jsonAppendString(&jx, z, n);
jsonAppendChar(&jx, ':');
jsonAppendValue(&jx, argv[i+1]);
jsonAppendValue(&jx, argv[i+1], 0);
}
jsonAppendChar(&jx, '}');
jsonResult(&jx);
@@ -1111,9 +1233,8 @@ static void jsonObjectFunc(
/*
** json_remove(JSON, PATH, ...)
**
** Remove the named elements from JSON and return the result. Ill-formed
** PATH arguments are silently ignored. If JSON is ill-formed, then NULL
** is returned.
** Remove the named elements from JSON and return the result. malformed
** JSON or PATH arguments result in an error.
*/
static void jsonRemoveFunc(
sqlite3_context *ctx,
@@ -1130,15 +1251,16 @@ static void jsonRemoveFunc(
if( x.nNode ){
for(i=1; i<(u32)argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
if( zPath==0 ) continue;
if( zPath[0]!='$' ) continue;
pNode = jsonLookup(&x, 0, &zPath[1], 0);
if( zPath==0 ) goto remove_done;
pNode = jsonLookup(&x, zPath, 0, ctx, 0);
if( x.nErr ) goto remove_done;
if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
}
if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
jsonReturn(x.aNode, ctx, 0);
jsonReturnJson(x.aNode, ctx, 0);
}
}
remove_done:
jsonParseReset(&x);
}
@@ -1146,7 +1268,7 @@ static void jsonRemoveFunc(
** json_replace(JSON, PATH, VALUE, ...)
**
** Replace the value at PATH with VALUE. If PATH does not already exist,
** this routine is a no-op. If JSON is ill-formed, return NULL.
** this routine is a no-op. If JSON or PATH is malformed, throw an error.
*/
static void jsonReplaceFunc(
sqlite3_context *ctx,
@@ -1166,21 +1288,23 @@ static void jsonReplaceFunc(
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
if( x.nNode ){
for(i=1; i<(u32)argc; i+=2){
u8 jnFlags = JNODE_REPLACE;
zPath = (const char*)sqlite3_value_text(argv[i]);
if( zPath==0 ) continue;
if( zPath[0]!='$' ) continue;
pNode = jsonLookup(&x, 0, &zPath[1], 0);
pNode = jsonLookup(&x, zPath, 0, ctx, &jnFlags);
if( x.nErr ) goto replace_err;
if( pNode ){
pNode->jnFlags |= JNODE_REPLACE;
pNode->jnFlags &= ~JNODE_JSON;
pNode->jnFlags |= jnFlags;
pNode->iVal = i+1;
}
}
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
}else{
jsonReturn(x.aNode, ctx, argv);
jsonReturnJson(x.aNode, ctx, argv);
}
}
replace_err:
jsonParseReset(&x);
}
@@ -1189,12 +1313,12 @@ static void jsonReplaceFunc(
**
** Set the value at PATH to VALUE. Create the PATH if it does not already
** exist. Overwrite existing values that do exist.
** If JSON is ill-formed, return NULL.
** If JSON or PATH is malformed, throw an error.
**
** json_insert(JSON, PATH, VALUE, ...)
**
** Create PATH and initialize it to VALUE. If PATH already exists, this
** routine is a no-op. If JSON is ill-formed, return NULL.
** routine is a no-op. If JSON or PATH is malformed, throw an error.
*/
static void jsonSetFunc(
sqlite3_context *ctx,
@@ -1216,23 +1340,25 @@ static void jsonSetFunc(
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
if( x.nNode ){
for(i=1; i<(u32)argc; i+=2){
u8 jnFlags = JNODE_REPLACE;
zPath = (const char*)sqlite3_value_text(argv[i]);
if( zPath==0 ) continue;
if( zPath[0]!='$' ) continue;
bApnd = 0;
pNode = jsonLookup(&x, 0, &zPath[1], &bApnd);
pNode = jsonLookup(&x, zPath, &bApnd, ctx, &jnFlags);
if( x.oom ){
sqlite3_result_error_nomem(ctx);
goto jsonSetDone;
}else if( x.nErr ){
goto jsonSetDone;
}else if( pNode && (bApnd || bIsSet) ){
pNode->jnFlags |= JNODE_REPLACE;
pNode->jnFlags &= ~JNODE_JSON;
pNode->jnFlags |= jnFlags;
pNode->iVal = i+1;
}
}
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
}else{
jsonReturn(x.aNode, ctx, argv);
jsonReturnJson(x.aNode, ctx, argv);
}
}
jsonSetDone:
@@ -1243,8 +1369,8 @@ jsonSetDone:
** json_type(JSON)
** json_type(JSON, PATH)
**
** Return the top-level "type" of a JSON string. Return NULL if the
** input is not a well-formed JSON string.
** Return the top-level "type" of a JSON string. Throw an error if
** either the JSON or PATH inputs are not well-formed.
*/
static void jsonTypeFunc(
sqlite3_context *ctx,
@@ -1254,18 +1380,15 @@ static void jsonTypeFunc(
JsonParse x; /* The parse */
const char *zPath;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 ) return;
if( zPath[0]!='$' ) return;
zPath++;
}else{
zPath = 0;
}
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
if( x.nNode ){
JsonNode *pNode = x.aNode;
if( zPath ) pNode = jsonLookup(&x, 0, zPath, 0);
JsonNode *pNode;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
pNode = jsonLookup(&x, zPath, 0, ctx, 0);
}else{
pNode = x.aNode;
}
if( pNode ){
sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
}
@@ -1276,7 +1399,8 @@ static void jsonTypeFunc(
/*
** json_valid(JSON)
**
** Return 1 if JSON is a valid JSON string. Return 0 otherwise.
** Return 1 if JSON is a well-formed JSON string according to RFC-7159.
** Return 0 otherwise.
*/
static void jsonValidFunc(
sqlite3_context *ctx,
@@ -1286,7 +1410,7 @@ static void jsonValidFunc(
JsonParse x; /* The parse */
int rc = 0;
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0]))==0
if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0
&& x.nNode>0
){
rc = 1;
@@ -1295,6 +1419,7 @@ static void jsonValidFunc(
sqlite3_result_int(ctx, rc);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/****************************************************************************
** The json_each virtual table
****************************************************************************/
@@ -1642,27 +1767,45 @@ static int jsonEachFilter(
if( z==0 ) return SQLITE_OK;
if( idxNum&2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 || zPath[0]!='$' ) return SQLITE_OK;
if( zPath==0 ) return SQLITE_OK;
if( zPath[0]!='$' ){
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = jsonPathSyntaxError(zPath);
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)
|| (p->bRecursive && jsonParseFindParents(&p->sParse))
){
if( jsonParse(&p->sParse, 0, p->zJson) ){
int rc = SQLITE_NOMEM;
if( p->sParse.oom==0 ){
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
}
jsonEachCursorReset(p);
return rc;
}else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
jsonEachCursorReset(p);
return SQLITE_NOMEM;
}else{
JsonNode *pNode;
if( idxNum==3 ){
const char *zErr = 0;
p->bRecursive = 0;
n = sqlite3_value_bytes(argv[1]);
p->zPath = sqlite3_malloc64( n+1 );
if( p->zPath==0 ) return SQLITE_NOMEM;
memcpy(p->zPath, zPath, (size_t)n+1);
pNode = jsonLookup(&p->sParse, 0, p->zPath+1, 0);
if( pNode==0 ){
pNode = jsonLookupStep(&p->sParse, 0, p->zPath+1, 0, &zErr);
if( p->sParse.nErr ){
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}else if( pNode==0 ){
return SQLITE_OK;
}
}else{
@@ -1734,6 +1877,7 @@ static sqlite3_module jsonTreeModule = {
0, /* xRelease */
0 /* xRollbackTo */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/****************************************************************************
** The following routine is the only publically visible identifier in this
@@ -1760,7 +1904,7 @@ int sqlite3_json_init(
{ "json_array", -1, 0, jsonArrayFunc },
{ "json_array_length", 1, 0, jsonArrayLengthFunc },
{ "json_array_length", 2, 0, jsonArrayLengthFunc },
{ "json_extract", 2, 0, jsonExtractFunc },
{ "json_extract", -1, 0, jsonExtractFunc },
{ "json_insert", -1, 0, jsonSetFunc },
{ "json_object", -1, 0, jsonObjectFunc },
{ "json_remove", -1, 0, jsonRemoveFunc },
@@ -1777,6 +1921,7 @@ int sqlite3_json_init(
{ "json_nodecount", 1, 0, jsonNodeCountFunc },
#endif
};
#ifndef SQLITE_OMIT_VIRTUALTABLE
static const struct {
const char *zName;
sqlite3_module *pModule;
@@ -1784,6 +1929,7 @@ int sqlite3_json_init(
{ "json_each", &jsonEachModule },
{ "json_tree", &jsonTreeModule },
};
#endif
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
@@ -1792,8 +1938,10 @@ int sqlite3_json_init(
(void*)&aFunc[i].flag,
aFunc[i].xFunc, 0, 0);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
}
#endif
return rc;
}

View File

@@ -489,7 +489,7 @@ static int rbuDeltaApply(
/* ERROR: copy exceeds output file size */
return -1;
}
if( ofst+cnt > lenSrc ){
if( (int)(ofst+cnt) > lenSrc ){
/* ERROR: copy extends past end of input */
return -1;
}
@@ -504,7 +504,7 @@ static int rbuDeltaApply(
/* ERROR: insert command gives an output larger than predicted */
return -1;
}
if( cnt>lenDelta ){
if( (int)cnt>lenDelta ){
/* ERROR: insert count exceeds size of delta */
return -1;
}
@@ -1117,7 +1117,7 @@ static void rbuTableType(
}
rbuTableType_end: {
int i;
unsigned int i;
for(i=0; i<sizeof(aStmt)/sizeof(aStmt[0]); i++){
rbuFinalize(p, aStmt[i]);
}
@@ -1530,7 +1530,7 @@ static char *rbuObjIterGetSetlist(
if( p->rc==SQLITE_OK ){
int i;
if( strlen(zMask)!=pIter->nTblCol ){
if( (int)strlen(zMask)!=pIter->nTblCol ){
rbuBadControlError(p);
}else{
const char *zSep = "";
@@ -3680,7 +3680,8 @@ static int rbuVfsOpen(
rbuVfsShmMap, /* xShmMap */
rbuVfsShmLock, /* xShmLock */
rbuVfsShmBarrier, /* xShmBarrier */
rbuVfsShmUnmap /* xShmUnmap */
rbuVfsShmUnmap, /* xShmUnmap */
0, 0 /* xFetch, xUnfetch */
};
rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;

View File

@@ -439,9 +439,9 @@ libsqlite3.a: $(LIBOBJ)
$(AR) libsqlite3.a $(LIBOBJ)
$(RANLIB) libsqlite3.a
sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) \
$(TOP)/src/shell.c \
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 \
libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h

View File

@@ -1,9 +1,9 @@
C Fix\sthe\sOR-optimization\sso\sthat\sit\salways\signores\ssubplans\sthat\sdo\snot\suse\san\sindex.
D 2015-08-27T23:42:43.629
C Merge\sthe\slatest\senhancements\sfrom\strunk.
D 2015-08-31T14:27:29.928
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in e2218eb228374422969de7b1680eda6864affcef
F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc 10af19cc089862481d49b347acd99c02635ddc49
F Makefile.msc b268d8be2e800b9d35f074b1ed6b2f698deebdd6
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F VERSION ccfc4d1576dbfdeece0a4372a2e6a2e37d3e7975
@@ -192,7 +192,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767
F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
F ext/misc/json1.c 541004e47235cefc2843ab03c100517452931913
F ext/misc/json1.c bd51e8c1e8ce580e6f21493bd8e94ed5aca3d777
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
@@ -226,7 +226,7 @@ F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
F ext/rbu/sqlite3rbu.c 1650e682b3568db0ed97ff2c7ba5d1c8ea060a84
F ext/rbu/sqlite3rbu.c 4ba82bd850aa012f73c31dd242d570f18c9cc35a
F ext/rbu/sqlite3rbu.h 5357f070cd8c0bcad459b620651ec1656859e4d0
F ext/rbu/test_rbu.c 2a3652241fa45d5eaa141775e4ae68c1d3582c03
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
@@ -258,7 +258,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 04840e8277ab5159af16172eafd214dae7cffff5
F main.mk 8da13ed011a7ae19450b7554910ff4afa3bd22b7
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk 0e7f04a8eb90f92259e47d80110e4e98d7ce337a
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@@ -282,7 +282,7 @@ F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
F src/btree.c f48b3ef91676c06a90a8832987ecef6b94c931ee
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0
F src/build.c 6b7f6ccacd9cbd113f1948b4268cb81a87ee513a
F src/build.c e0902658fc86dbd60a5c6772ca45429c69ee81fe
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
@@ -338,7 +338,7 @@ F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c e6dc5a5490cf93afc1cc2cb58280c98da56acb3c
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c b52c80f2b1bdb62491f9ce40eea0c5f80c78d105
F src/shell.c 5a08835e85c502978bde35a89d4045833f772876
F src/shell.c 6332ef06db1390ef812cfdff1fc97b4fd76cdd42
F src/sqlite.h.in 378bebc8fe6a88bade25e5f23b7e6123fdc64b00
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h f700e6a9dd1fdcccc9951ab022b366fb66b9e413
@@ -411,7 +411,7 @@ F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
F src/vtab.c d31174e4c8f592febab3fa7f69e18320b4fd657a
F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c 6fb6b68969e4692593c2552c4e7bff5882de2cb8
F src/wal.c 8cd07f1f99e1a81346db1c9da879bef6c6f97cf6
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
F src/where.c acec45dc602a4f58e80e6fa088b9379ccfffd3a4
@@ -810,7 +810,7 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307
F test/jrnlmode.test 7864d59cf7f6e552b9b99ba0f38acd167edc10fa
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
F test/json101.test 950ed4e8deb8ad4c10bd4fbc858eb54143de9867
F test/json101.test 11535d8986184500f4c30cc2f0b154b4ab05cc4e
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
@@ -942,7 +942,7 @@ F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
F test/rollbackfault.test 6a004f71087cc399296cffbb5429ea6da655ae65
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test 09fcded0c96fbc0ed11fb75faa3b0bad32cb011a
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
@@ -1264,7 +1264,7 @@ F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada
F test/wal3.test 2b5445e5da44780b9b44712f5a38523f7aeb0941
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 88b5d9a6a3d1532497ee9f4296f010d66f07e33c
F test/wal6.test 527581f5527bf9c24394991e2be83000aace5f9e
F test/wal6.test 4421cd5a2fa99d29cc91ef12fb23bed171ed3a4c
F test/wal64k.test 163655ecd2cb8afef4737cac2a40fdd2eeaf20b8
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
F test/wal8.test 75c42e1bc4545c277fed212f8fc9b7723cd02216
@@ -1380,7 +1380,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 73d361ce9e4d72c943def8b0b3caa227f9199aed 66f92a16866e5825363636b9cc4b8f9b29d9e84d
R b576dddb4c7e794820789b114785ae51
P cf452028d1be2c5578a07f6e21b4d8b613373eb8 1da60c3dda4254620052a83c853c2d2b6dd5009f
R 64522485917d5b93eb19dc63143d2895
U drh
Z e54726d52fd301793beed6904550b28b
Z 590b51e0f03c976eccd82950be2fb3fc

View File

@@ -1 +1 @@
cf452028d1be2c5578a07f6e21b4d8b613373eb8
7bde6d4d8cf05e1beb9bdf20b85760dc3e7a76c9

View File

@@ -356,7 +356,7 @@ Table *sqlite3LocateTable(
p = sqlite3FindTable(pParse->db, zName, zDbase);
if( p==0 ){
const char *zMsg = isView ? "no such view" : "no such table";
#ifndef SQLITE_OMIT_VIRTUAL_TABLE
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* If zName is the not the name of a table in the schema created using
** CREATE, then check to see if it is the name of an virtual table that
** can be an eponymous virtual table. */

View File

@@ -4619,6 +4619,13 @@ int SQLITE_CDECL main(int argc, char **argv){
}
data.out = stdout;
#ifdef SQLITE_ENABLE_JSON1
{
extern int sqlite3_json_init(sqlite3*);
sqlite3_auto_extension((void(*)(void))sqlite3_json_init);
}
#endif
/* Go ahead and open the database file if it already exists. If the
** file does not exist, delay opening it. This prevents empty database
** files from being created if a user mistypes the database name argument

View File

@@ -428,6 +428,7 @@ struct Wal {
u8 syncHeader; /* Fsync the WAL header if true */
u8 padToSectorBoundary; /* Pad transactions out to the next sector */
WalIndexHdr hdr; /* Wal-index header for current transaction */
u32 minFrame; /* Ignore wal frames before this one */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
#ifdef SQLITE_DEBUG
@@ -2296,12 +2297,27 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
** instead.
**
** This does not guarantee that the copy of the wal-index header is up to
** date before proceeding. That would not be possible without somehow
** blocking writers. It only guarantees that a dangerous checkpoint or
** log-wrap (either of which would require an exclusive lock on
** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
** Before checking that the live wal-index header has not changed
** since it was read, set Wal.minFrame to the first frame in the wal
** file that has not yet been checkpointed. This client will not need
** to read any frames earlier than minFrame from the wal file - they
** can be safely read directly from the database file.
**
** Because a ShmBarrier() call is made between taking the copy of
** nBackfill and checking that the wal-header in shared-memory still
** matches the one cached in pWal->hdr, it is guaranteed that the
** checkpointer that set nBackfill was not working with a wal-index
** header newer than that cached in pWal->hdr. If it were, that could
** cause a problem. The checkpointer could omit to checkpoint
** a version of page X that lies before pWal->minFrame (call that version
** A) on the basis that there is a newer version (version B) of the same
** page later in the wal file. But if version B happens to like past
** frame pWal->hdr.mxFrame - then the client would incorrectly assume
** that it can read version A from the database file. However, since
** we can guarantee that the checkpointer that set nBackfill could not
** see any pages past pWal->hdr.mxFrame, this problem does not come up.
*/
pWal->minFrame = pInfo->nBackfill+1;
walShmBarrier(pWal);
if( pInfo->aReadMark[mxI]!=mxReadMark
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
@@ -2372,6 +2388,7 @@ int sqlite3WalFindFrame(
u32 iRead = 0; /* If !=0, WAL frame to return data from */
u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */
int iHash; /* Used to loop through N hash tables */
int iMinHash;
/* This routine is only be called from within a read transaction. */
assert( pWal->readLock>=0 || pWal->lockError );
@@ -2412,7 +2429,8 @@ int sqlite3WalFindFrame(
** This condition filters out entries that were added to the hash
** table after the current read-transaction had started.
*/
for(iHash=walFramePage(iLast); iHash>=0 && iRead==0; iHash--){
iMinHash = walFramePage(pWal->minFrame);
for(iHash=walFramePage(iLast); iHash>=iMinHash && iRead==0; iHash--){
volatile ht_slot *aHash; /* Pointer to hash table */
volatile u32 *aPgno; /* Pointer to array of page numbers */
u32 iZero; /* Frame number corresponding to aPgno[0] */
@@ -2427,7 +2445,7 @@ int sqlite3WalFindFrame(
nCollide = HASHTABLE_NSLOT;
for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
u32 iFrame = aHash[iKey] + iZero;
if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){
if( iFrame<=iLast && iFrame>=pWal->minFrame && aPgno[aHash[iKey]]==pgno ){
assert( iFrame>iRead || CORRUPT_DB );
iRead = iFrame;
}

View File

@@ -50,5 +50,246 @@ do_catchsql_test json1-2.4 {
SELECT json_object('a',1,'b',x'abcd');
} {1 {JSON cannot hold BLOB values}}
do_execsql_test json1-3.1 {
SELECT json_replace('{"a":1,"b":2}','$.a','[3,4,5]');
} {{{"a":"[3,4,5]","b":2}}}
do_execsql_test json1-3.2 {
SELECT json_replace('{"a":1,"b":2}','$$.a','[3,4,5]');
} {{{"a":[3,4,5],"b":2}}}
do_execsql_test json1-3.3 {
SELECT json_type(json_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b');
} {text}
do_execsql_test json1-3.4 {
SELECT json_type(json_set('{"a":1,"b":2}','$$.b','{"x":3,"y":4}'),'$.b');
} {object}
# Per rfc7159, any JSON value is allowed at the top level, and whitespace
# is permitting before and/or after that value.
#
do_execsql_test json1-4.1 {
CREATE TABLE j1(x);
INSERT INTO j1(x)
VALUES('true'),('false'),('null'),('123'),('-234'),('34.5e+6'),
('""'),('"\""'),('"\\"'),('"abcdefghijlmnopqrstuvwxyz"'),
('[]'),('{}'),('[true,false,null,123,-234,34.5e+6,{},[]]'),
('{"a":true,"b":{"c":false}}');
SELECT * FROM j1 WHERE NOT json_valid(x);
} {}
do_execsql_test json1-4.2 {
SELECT * FROM j1 WHERE NOT json_valid(char(0x20,0x09,0x0a,0x0d)||x);
} {}
do_execsql_test json1-4.3 {
SELECT * FROM j1 WHERE NOT json_valid(x||char(0x20,0x09,0x0a,0x0d));
} {}
# But an empty string, or a string of pure whitespace is not valid JSON.
#
do_execsql_test json1-4.4 {
SELECT json_valid(''), json_valid(char(0x20,0x09,0x0a,0x0d));
} {0 0}
# json_remove() and similar functions with no edit operations return their
# input unchanged.
#
do_execsql_test json1-4.5 {
SELECT x FROM j1 WHERE json_remove(x)<>x;
} {}
do_execsql_test json1-4.6 {
SELECT x FROM j1 WHERE json_replace(x)<>x;
} {}
do_execsql_test json1-4.7 {
SELECT x FROM j1 WHERE json_set(x)<>x;
} {}
do_execsql_test json1-4.8 {
SELECT x FROM j1 WHERE json_insert(x)<>x;
} {}
# json_extract(JSON,'$') will return objects and arrays without change.
#
do_execsql_test json-4.10 {
SELECT count(*) FROM j1 WHERE json_type(x) IN ('object','array');
SELECT x FROM j1
WHERE json_extract(x,'$')<>x
AND json_type(x) IN ('object','array');
} {4}
do_execsql_test json-5.1 {
CREATE TABLE j2(id INTEGER PRIMARY KEY, json, src);
INSERT INTO j2(id,json,src)
VALUES(1,'{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
],
"children": [],
"spouse": null
}','https://en.wikipedia.org/wiki/JSON');
INSERT INTO j2(id,json,src)
VALUES(2, '{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil''s Food" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
}','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html');
INSERT INTO j2(id,json,src)
VALUES(3,'[
{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil''s Food" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
},
{
"id": "0002",
"type": "donut",
"name": "Raised",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
},
{
"id": "0003",
"type": "donut",
"name": "Old Fashioned",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
}
]','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html');
SELECT count(*) FROM j2;
} {3}
do_execsql_test json-5.2 {
SELECT id, json_valid(json), json_type(json), '|' FROM j2 ORDER BY id;
} {1 1 object | 2 1 object | 3 1 array |}
ifcapable !vtab {
finish_test
return
}
# fullkey is always the same as path+key (with appropriate formatting)
#
do_execsql_test json-5.3 {
SELECT j2.rowid, jx.rowid, fullkey, path, key
FROM j2, json_tree(j2.json) AS jx
WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']'
ELSE '.'||key END);
} {}
do_execsql_test json-5.4 {
SELECT j2.rowid, jx.rowid, fullkey, path, key
FROM j2, json_each(j2.json) AS jx
WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']'
ELSE '.'||key END);
} {}
# Verify that the json_each.json and json_tree.json output is always the
# same as input.
#
do_execsql_test json-5.5 {
SELECT j2.rowid, jx.rowid, fullkey, path, key
FROM j2, json_each(j2.json) AS jx
WHERE jx.json<>j2.json;
} {}
do_execsql_test json-5.6 {
SELECT j2.rowid, jx.rowid, fullkey, path, key
FROM j2, json_tree(j2.json) AS jx
WHERE jx.json<>j2.json;
} {}
do_execsql_test json-5.7 {
SELECT j2.rowid, jx.rowid, fullkey, path, key
FROM j2, json_each(j2.json) AS jx
WHERE jx.value<>jx.atom AND type NOT IN ('array','object');
} {}
do_execsql_test json-5.8 {
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');
} {}
finish_test

View File

@@ -144,7 +144,8 @@ do_test rowid-2.8 {
execsql {SELECT x FROM t1 ORDER BY x}
} {1 3 5 7 9}
if 0 { # we can now....
if 0 { # With the index-on-expressions enhancement, creating
# an index on ROWID has become possible.
# We cannot index by ROWID
#
do_test rowid-2.9 {

View File

@@ -193,5 +193,47 @@ do_test 3.x {
db2 close
} {}
#-------------------------------------------------------------------------
# Check that if a wal file has been partially checkpointed, no frames are
# read from the checkpointed part.
#
reset_db
do_execsql_test 4.1 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t1(a, b);
CREATE TABLE t2(a, b);
PRAGMA wal_checkpoint = truncate;
} {wal 0 0 0}
do_test 4.2 {
execsql { INSERT INTO t1 VALUES(1, 2) }
file size test.db-wal
} [wal_file_size 1 1024]
do_test 4.3 {
sqlite3 db2 test.db
execsql {
BEGIN;
INSERT INTO t2 VALUES(3, 4);
}
execsql { PRAGMA wal_checkpoint = passive } db2
} {0 1 1}
do_test 4.3 {
execsql { COMMIT }
db2 close
hexio_write test.db-wal 0 [string repeat 00 2000]
sqlite3 db2 test.db
} {}
do_test 4.4.1 {
catchsql { SELECT * FROM t1 } db2
} {0 {1 2}}
do_test 4.4.2 {
catchsql { SELECT * FROM t2 } db2
} {1 {database disk image is malformed}}
finish_test