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:
@@ -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
|
||||
|
@@ -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
|
||||
|
342
ext/misc/json1.c
342
ext/misc/json1.c
@@ -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);
|
||||
jsonAppendString(p, z, n);
|
||||
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( x.nNode ){
|
||||
JsonNode *pNode = x.aNode;
|
||||
if( zPath ) pNode = jsonLookup(&x, 0, zPath, 0);
|
||||
if( pNode->eType==JSON_ARRAY ){
|
||||
assert( (pNode->jnFlags & JNODE_APPEND)==0 );
|
||||
for(i=1; i<=pNode->n; n++){
|
||||
i += jsonNodeSize(&pNode[i]);
|
||||
}
|
||||
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, 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]);
|
||||
}
|
||||
}
|
||||
jsonParseReset(&x);
|
||||
}
|
||||
if( !x.oom ) sqlite3_result_int64(ctx, n);
|
||||
if( x.nErr==0 ) sqlite3_result_int64(ctx, n);
|
||||
jsonParseReset(&x);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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);
|
||||
if( pNode ){
|
||||
jsonReturn(pNode, ctx, 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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
6
main.mk
6
main.mk
@@ -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
|
||||
|
32
manifest
32
manifest
@@ -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
|
||||
|
@@ -1 +1 @@
|
||||
cf452028d1be2c5578a07f6e21b4d8b613373eb8
|
||||
7bde6d4d8cf05e1beb9bdf20b85760dc3e7a76c9
|
@@ -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. */
|
||||
|
@@ -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
|
||||
|
32
src/wal.c
32
src/wal.c
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user