/* Generated by re2c 1.0.3 on Mon Apr 6 21:17:12 2020 */
#line 1 "p21sql.l"
/*
** 2015-08-12
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This SQLite extension implements P21 functions. The interface is
** modeled after MySQL JSON functions:
**
** https://dev.mysql.com/doc/refman/5.7/en/json.html
**
** For the time being, all P21 params are stored as pure text.
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_P21SQL)
#if !defined(SQLITEINT_H)
#include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1
#include
#include
#include
#include
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAM
# define UNUSED_PARAM(X) (void)(X)
#endif
#ifndef LARGEST_INT64
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
#endif
/*
** Versions of isspace(), isalnum() and isdigit() to which it is safe
** to pass signed char values.
*/
#ifdef sqlite3Isdigit
/* Use the SQLite core versions if this routine is part of the
** SQLite amalgamation */
# define safe_isdigit(x) sqlite3Isdigit(x)
# define safe_isalnum(x) sqlite3Isalnum(x)
# define safe_isxdigit(x) sqlite3Isxdigit(x)
#else
/* Use the standard library for separate compilation */
#include /* amalgamator: keep */
# define safe_isdigit(x) isdigit((unsigned char)(x))
# define safe_isalnum(x) isalnum((unsigned char)(x))
# define safe_isxdigit(x) isxdigit((unsigned char)(x))
#endif
/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char p21IsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
#define safe_isspace(x) (p21IsSpace[(unsigned char)x])
#ifndef SQLITE_AMALGAMATION
/* Unsigned integer types. These are already defined in the sqliteInt.h,
** but the definitions need to be repeated for separate compilation. */
typedef sqlite3_uint64 u64;
typedef unsigned int u32;
typedef unsigned short int u16;
typedef unsigned char u8;
#endif
/* some C implementations don't have these? (inttypes.h / stdint.h) */
#ifndef UINT16_WIDTH
# define UINT16_WIDTH 16
#endif
#ifndef UINT16_MAX
# define UINT16_MAX 65535
#endif
/* Objects */
typedef struct P21String P21String;
typedef struct P21Node P21Node;
typedef struct P21Parse P21Parse;
/* An instance of this object represents a P21 parameter string
** under construction. Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON (here P21!).
*/
struct P21String {
sqlite3_context *pCtx; /* Function context - put error messages here */
char *zBuf; /* Append P21 content here */
u64 nAlloc; /* Bytes of storage available in zBuf[] */
u64 nUsed; /* Bytes of zBuf[] currently used */
u8 bStatic; /* True if zBuf is static space */
u8 bErr; /* True if an error has been encountered */
char zSpace[100]; /* Initial static space */
};
#define P21_EMPTY 0x1 /* optional attribute not provided : '$' */
#define P21_DERIVED 0x2 /* derived attribute not provided : '*' */
#define P21_ENUMERATION 0x3 /* (also) includes boolean and logical values */
#define P21_INTEGER 0x4
#define P21_REAL 0x5
#define P21_STRING 0x6
#define P21_BINARY 0x7
#define P21_EID 0x8 /* entity_instance_name */
#define P21_LIST 0x9
#define P21_RECORD 0xA /* simple_record */
#define P21_SUBTYPE 80 /* Ascii for "P" */
/*
** Names of the various P21 types:
*/
static const char * const p21Type[] = {
"",
"empty", "derived", "enumeration", "integer", "real",
"string", "binary", "eid", "list", "record"
};
/* Bit values for the P21Node.jnFlag field
*/
#define PNODE_RAW 0x01 /* Content is raw, not P21 encoded */
#define PNODE_ESCAPE 0x02 /* Content is text with \ escapes */
#define PNODE_REMOVE 0x04 /* Do not output */
#define PNODE_REPLACE 0x08 /* Replace with P21Node.u.iReplace */
#define PNODE_PATCH 0x10 /* Patch with P21Node.u.pPatch */
#define PNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
#define PNODE_LABEL 0x40 /* Is a label of an object */
/* A single node of parsed P21 params
*/
struct P21Node {
u8 eType; /* One of the P21_ type values */
u8 jnFlags; /* P21Node flags */
u16 n_kw; /* store the KEYWORD length */
u32 n; /* Bytes of content, or number of sub-nodes */
union {
const char *zJContent; /* Content for INT, REAL, and STRING */
u32 iAppend; /* More terms for ARRAY and OBJECT */
u32 iKey; /* Key for ARRAY objects in p21_tree() */
u32 iReplace; /* Replacement content for PNODE_REPLACE */
P21Node *pPatch; /* Node chain of patch for PNODE_PATCH */
} u;
};
/* A completely parsed P21 string
*/
struct P21Parse {
u32 nNode; /* Number of slots of aNode[] used */
u32 nAlloc; /* Number of slots of aNode[] allocated */
P21Node *aNode; /* Array of nodes containing the parse */
const char *zP21; /* Original P21 string */
u32 *aUp; /* Index of parent of each node */
u8 oom; /* Set to true if out of memory */
u8 nErr; /* Number of errors seen */
u16 iDepth; /* Nesting depth */
int nP21; /* Length of the zP21 string in bytes */
u32 iHold; /* Replace cache line with the lowest iHold value */
};
/*
** Maximum nesting depth of P21 for this implementation.
*/
#define P21_MAX_DEPTH 20
/**************************************************************************
** Utility routines for dealing with P21String objects
**************************************************************************/
/* Set the P21String object to an empty string
*/
static void p21Zero(P21String *p){
p->zBuf = p->zSpace;
p->nAlloc = sizeof(p->zSpace);
p->nUsed = 0;
p->bStatic = 1;
}
/* Initialize the P21String object
*/
static void p21Init(P21String *p, sqlite3_context *pCtx){
p->pCtx = pCtx;
p->bErr = 0;
p21Zero(p);
}
/* Free all allocated memory and reset the P21String object back to its
** initial state.
*/
static void p21Reset(P21String *p){
if( !p->bStatic ) sqlite3_free(p->zBuf);
p21Zero(p);
}
/* Report an out-of-memory (OOM) condition
*/
static void p21Oom(P21String *p){
p->bErr = 1;
sqlite3_result_error_nomem(p->pCtx);
p21Reset(p);
}
/* Enlarge p->zBuf so that it can hold at least N more bytes.
** Return zero on success. Return non-zero on an OOM error
*/
static int p21Grow(P21String *p, u32 N){
u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
char *zNew;
if( p->bStatic ){
if( p->bErr ) return 1;
zNew = sqlite3_malloc64(nTotal);
if( zNew==0 ){
p21Oom(p);
return SQLITE_NOMEM;
}
memcpy(zNew, p->zBuf, (size_t)p->nUsed);
p->zBuf = zNew;
p->bStatic = 0;
}else{
zNew = sqlite3_realloc64(p->zBuf, nTotal);
if( zNew==0 ){
p21Oom(p);
return SQLITE_NOMEM;
}
p->zBuf = zNew;
}
p->nAlloc = nTotal;
return SQLITE_OK;
}
/* Append N bytes from zIn onto the end of the P21String string.
*/
static void p21AppendRaw(P21String *p, const char *zIn, u32 N){
if( (N+p->nUsed >= p->nAlloc) && p21Grow(p,N)!=0 ) return;
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
/* Append formatted text (not to exceed N bytes) to the P21String.
*/
static void p21Printf(int N, P21String *p, const char *zFormat, ...){
va_list ap;
if( (p->nUsed + N >= p->nAlloc) && p21Grow(p, N) ) return;
va_start(ap, zFormat);
sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
va_end(ap);
p->nUsed += (int)strlen(p->zBuf+p->nUsed);
}
/* Append a single character
*/
static void p21AppendChar(P21String *p, char c){
if( p->nUsed>=p->nAlloc && p21Grow(p,1)!=0 ) return;
p->zBuf[p->nUsed++] = c;
}
/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
*/
static void p21AppendSeparator(P21String *p){
char c;
if( p->nUsed==0 ) return;
c = p->zBuf[p->nUsed-1];
if( c!='(' ) p21AppendChar(p, ',');
}
/* Append the N-byte string in zIn to the end of the P21String string
** under construction. Enclose the string in '...' and escape
** any double-quotes or backslash characters contained within the
** string.
*/
static void p21AppendString(P21String *p, const char *zIn, u32 N){
u32 i;
if( (N+p->nUsed+2 >= p->nAlloc) && p21Grow(p,N+2)!=0 ) return;
p->zBuf[p->nUsed++] = '\'';
for(i=0; inUsed+N+3-i > p->nAlloc) && p21Grow(p,N+3-i)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
}else if( c<=0x1f ){
static const char aSpecial[] = {
0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
assert( sizeof(aSpecial)==32 );
assert( aSpecial['\b']=='b' );
assert( aSpecial['\f']=='f' );
assert( aSpecial['\n']=='n' );
assert( aSpecial['\r']=='r' );
assert( aSpecial['\t']=='t' );
if( aSpecial[c] ){
c = aSpecial[c];
goto p21_simple_escape;
}
if( (p->nUsed+N+7+i > p->nAlloc) && p21Grow(p,N+7-i)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
p->zBuf[p->nUsed++] = 'u';
p->zBuf[p->nUsed++] = '0';
p->zBuf[p->nUsed++] = '0';
p->zBuf[p->nUsed++] = '0' + (c>>4);
c = "0123456789abcdef"[c&0xf];
}
p->zBuf[p->nUsed++] = c;
}
p->zBuf[p->nUsed++] = '\'';
assert( p->nUsednAlloc );
}
/*
** Append a function parameter value to the P21 string under
** construction.
*/
static void p21AppendValue(
P21String *p, /* Append to this P21 string */
sqlite3_value *pValue /* Value to append */
){
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
p21AppendRaw(p, "$", 1);
break;
}
case SQLITE_INTEGER:
case SQLITE_FLOAT: {
const char *z = (const char*)sqlite3_value_text(pValue);
/* TODO: confirm format is valid */
u32 n = (u32)sqlite3_value_bytes(pValue);
p21AppendRaw(p, z, n);
break;
}
case SQLITE_TEXT: {
const char *z = (const char*)sqlite3_value_text(pValue);
u32 n = (u32)sqlite3_value_bytes(pValue);
if( sqlite3_value_subtype(pValue)==P21_SUBTYPE ){
p21AppendRaw(p, z, n);
}else{
p21AppendString(p, z, n);
}
break;
}
default: {
if( p->bErr==0 ){
sqlite3_result_error(p->pCtx, "P21 cannot hold BLOB values", -1);
p->bErr = 2;
p21Reset(p);
}
break;
}
}
}
/* Make the P21 in p the result of the SQL function.
*/
static void p21Result(P21String *p){
if( p->bErr==0 ){
sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
p->bStatic ? SQLITE_TRANSIENT : sqlite3_free,
SQLITE_UTF8);
p21Zero(p);
}
assert( p->bStatic );
}
/**************************************************************************
** Utility routines for dealing with P21Node and P21Parse objects
**************************************************************************/
/*
** Return the number of consecutive P21Node slots need to represent
** the parsed P21 at pNode. The minimum answer is 1. For ARRAY and
** OBJECT types, the number might be larger.
**
** Appended elements are not counted. The value returned is the number
** by which the P21Node counter should increment in order to go to the
** next peer value.
*/
static u32 p21NodeSize(P21Node *pNode){
return pNode->eType < P21_LIST ? 1 : pNode->n + 1;
}
/*
** Reclaim all memory allocated by a P21Parse object. But do not
** delete the P21Parse object itself.
*/
static void p21ParseReset(P21Parse *pParse){
sqlite3_free(pParse->aNode);
pParse->aNode = 0;
pParse->nNode = 0;
pParse->nAlloc = 0;
sqlite3_free(pParse->aUp);
pParse->aUp = 0;
}
/*
** Free a P21Parse object that was obtained from sqlite3_malloc().
*/
static void p21ParseFree(P21Parse *pParse){
p21ParseReset(pParse);
sqlite3_free(pParse);
}
/*
** Convert the P21Node pNode into a pure P21 string and
** append to pOut. Subsubstructure is also included. Return
** the number of P21Node objects that are encoded.
*/
static void p21RenderNode(
P21Node *pNode, /* The node to render */
P21String *pOut, /* Write P21 here */
sqlite3_value **aReplace /* Replacement values */
){
if( pNode->jnFlags & (PNODE_REPLACE|PNODE_PATCH) ){
if( pNode->jnFlags & PNODE_REPLACE ){
p21AppendValue(pOut, aReplace[pNode->u.iReplace]);
return;
}
pNode = pNode->u.pPatch;
}
switch( pNode->eType ){
default: {
assert( pNode->eType==P21_EMPTY );
p21AppendChar(pOut, '$');
break;
}
case P21_ENUMERATION: {
p21AppendRaw(pOut, pNode->u.zJContent, pNode->n);
break;
}
case P21_DERIVED: {
p21AppendChar(pOut, '*');
break;
}
case P21_BINARY: {
p21AppendRaw(pOut, pNode->u.zJContent, pNode->n);
break;
}
case P21_EID: {
p21AppendRaw(pOut, pNode->u.zJContent, pNode->n);
break;
}
case P21_STRING: {
if( pNode->jnFlags & PNODE_RAW ){
p21AppendString(pOut, pNode->u.zJContent, pNode->n);
break;
}
/* Fall through into the next case */
}
case P21_REAL:
case P21_INTEGER: {
p21AppendRaw(pOut, pNode->u.zJContent, pNode->n);
break;
}
case P21_LIST: {
u32 j = 1;
p21AppendChar(pOut, '(');
for(;;){
while( j<=pNode->n ){
if( (pNode[j].jnFlags & PNODE_REMOVE)==0 ){
p21AppendSeparator(pOut);
p21RenderNode(&pNode[j], pOut, aReplace);
}
j += p21NodeSize(&pNode[j]);
}
if( (pNode->jnFlags & PNODE_APPEND)==0 ) break;
pNode = &pNode[pNode->u.iAppend];
j = 1;
}
p21AppendChar(pOut, ')');
break;
}
case P21_RECORD: {
u32 j = 1;
p21AppendRaw(pOut, pNode->u.zJContent, pNode->n_kw);
p21AppendChar(pOut, '(');
for(;;){
while( j<= pNode->n ){
if( (pNode[j].jnFlags & PNODE_REMOVE)==0 ){
p21AppendSeparator(pOut);
p21RenderNode(&pNode[j], pOut, aReplace);
}
j += p21NodeSize(&pNode[j]);
}
if( (pNode->jnFlags & PNODE_APPEND)==0 ) break;
pNode = &pNode[pNode->u.iAppend];
j = 1;
}
p21AppendChar(pOut, ')');
break;
}
}
}
/*
** Return a P21Node and all its descendents as a P21 string.
*/
static void p21ReturnP21(
P21Node *pNode, /* Node to return */
sqlite3_context *pCtx, /* Return value for this function */
sqlite3_value **aReplace /* Array of replacement values */
){
P21String s;
p21Init(&s, pCtx);
p21RenderNode(pNode, &s, aReplace);
p21Result(&s);
sqlite3_result_subtype(pCtx, P21_SUBTYPE);
}
/*
** Translate a single byte of Hex into an integer.
** This routine only works if h really is a valid hexadecimal
** character: 0..9a..fA..F
*/
static u8 p21HexToInt(int h){
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
#ifdef SQLITE_EBCDIC
h += 9*(1&~(h>>4));
#else
h += 9*(1&(h>>6));
#endif
return (u8)(h & 0xf);
}
/*
** Convert a 4-byte hex string into an integer
*/
static u32 p21HexToInt4(const char *z){
u32 v;
assert( safe_isxdigit(z[0]) );
assert( safe_isxdigit(z[1]) );
assert( safe_isxdigit(z[2]) );
assert( safe_isxdigit(z[3]) );
v = (p21HexToInt(z[0])<<12)
+ (p21HexToInt(z[1])<<8)
+ (p21HexToInt(z[2])<<4)
+ p21HexToInt(z[3]);
return v;
}
/*
** Make the P21Node the return value of the function.
*/
static void p21Return(
P21Node *pNode, /* Node to return */
sqlite3_context *pCtx, /* Return value for this function */
sqlite3_value **aReplace /* Array of replacement values */
){
switch( pNode->eType ){
default: {
assert( pNode->eType==P21_EMPTY );
sqlite3_result_null(pCtx);
break;
}
case P21_DERIVED: {
assert(0);
}
case P21_ENUMERATION: {
assert(0);
}
case P21_BINARY: {
assert(0);
}
case P21_EID: {
sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, SQLITE_TRANSIENT);
break;
}
case P21_INTEGER: {
sqlite3_int64 i = 0;
const char *z = pNode->u.zJContent;
if( z[0]=='-' ){ z++; }
while( z[0]>='0' && z[0]<='9' ){
unsigned v = *(z++) - '0';
if( i>=LARGEST_INT64/10 ){
if( i>LARGEST_INT64/10 ) goto int_as_real;
if( z[0]>='0' && z[0]<='9' ) goto int_as_real;
if( v==9 ) goto int_as_real;
if( v==8 ){
if( pNode->u.zJContent[0]=='-' ){
sqlite3_result_int64(pCtx, SMALLEST_INT64);
goto int_done;
}else{
goto int_as_real;
}
}
}
i = i*10 + v;
}
if( pNode->u.zJContent[0]=='-' ){ i = -i; }
sqlite3_result_int64(pCtx, i);
int_done:
break;
int_as_real: /* fall through to real */;
}
case P21_REAL: {
double r;
#ifdef SQLITE_AMALGAMATION
const char *z = pNode->u.zJContent;
sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
#else
r = strtod(pNode->u.zJContent, 0);
#endif
sqlite3_result_double(pCtx, r);
break;
}
case P21_STRING: {
#if 0 /* Never happens because PNODE_RAW is only set by p21_set(),
** p21_insert() and p21_replace() and those routines do not
** call p21Return() */
if( pNode->jnFlags & PNODE_RAW ){
sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
SQLITE_TRANSIENT);
}else
#endif
assert( (pNode->jnFlags & PNODE_RAW)==0 );
if( (pNode->jnFlags & PNODE_ESCAPE)==0 ){
/* P21 formatted without any backslash-escapes */
sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
SQLITE_TRANSIENT);
}else{
/* Translate P21 formatted string into raw text */
u32 i;
u32 n = pNode->n;
const char *z = pNode->u.zJContent;
char *zOut;
u32 j;
/* TODO: */
assert(0);
zOut = sqlite3_malloc( n+1 );
if( zOut==0 ){
sqlite3_result_error_nomem(pCtx);
break;
}
for(i=1, j=0; i>6));
zOut[j++] = 0x80 | (v&0x3f);
}else{
u32 vlo;
if( (v&0xfc00)==0xd800
&& i>18);
zOut[j++] = 0x80 | ((v>>12)&0x3f);
zOut[j++] = 0x80 | ((v>>6)&0x3f);
zOut[j++] = 0x80 | (v&0x3f);
}else{
zOut[j++] = 0xe0 | (v>>12);
zOut[j++] = 0x80 | ((v>>6)&0x3f);
zOut[j++] = 0x80 | (v&0x3f);
}
}
}else{
if( c=='b' ){
c = '\b';
}else if( c=='f' ){
c = '\f';
}else if( c=='n' ){
c = '\n';
}else if( c=='r' ){
c = '\r';
}else if( c=='t' ){
c = '\t';
}
zOut[j++] = c;
}
}
}
zOut[j] = 0;
sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
}
break;
}
case P21_LIST:
case P21_RECORD: {
p21ReturnP21(pNode, pCtx, aReplace);
break;
}
}
}
/* Forward reference */
static int p21ParseAddNode(P21Parse*,u32,u32,const char*);
/*
** A macro to hint to the compiler that a function should not be
** inlined.
*/
#if defined(__GNUC__)
# define P21_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER) && _MSC_VER>=1310
# define P21_NOINLINE __declspec(noinline)
#else
# define P21_NOINLINE
#endif
static P21_NOINLINE int p21ParseAddNodeExpand(
P21Parse *pParse, /* Append the node to this object */
u32 eType, /* Node type */
u32 n, /* Content size or sub-node count */
const char *zContent /* Content */
){
u32 nNew;
P21Node *pNew;
assert( pParse->nNode>=pParse->nAlloc );
if( pParse->oom ) return -1;
nNew = pParse->nAlloc*2 + 10;
pNew = sqlite3_realloc64(pParse->aNode, sizeof(P21Node)*nNew);
if( pNew==0 ){
pParse->oom = 1;
return -1;
}
pParse->nAlloc = nNew;
pParse->aNode = pNew;
assert( pParse->nNodenAlloc );
return p21ParseAddNode(pParse, eType, n, zContent);
}
/*
** Create a new P21Node instance based on the arguments and append that
** instance to the P21Parse. Return the index in pParse->aNode[] of the
** new node, or -1 if a memory allocation fails.
*/
static int p21ParseAddNode(
P21Parse *pParse, /* Append the node to this object */
u32 eType, /* Node type */
u32 n, /* Content size or sub-node count */
const char *zContent /* Content */
){
P21Node *p;
if( pParse->nNode>=pParse->nAlloc ){
return p21ParseAddNodeExpand(pParse, eType, n, zContent);
}
p = &pParse->aNode[pParse->nNode];
p->eType = (u8)eType;
p->jnFlags = 0;
p->n = n;
p->u.zJContent = zContent;
return pParse->nNode++;
}
/*
** Return true if z[] begins with 4 (or more) hexadecimal digits
*/
static int p21Is4Hex(const char *z){
int i;
for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0;
return 1;
}
/*
** Parse P21 value which begins at pParse->zP21[i]. Return the
** index of the first character past the end of the value parsed.
**
** Return negative for a syntax error.
*/
static int p21ParseValue(P21Parse *pParse, u32 i) {
static int cxtStack[P21_MAX_DEPTH];
const unsigned char *sp, *cur, *mrk, *tok, *end;
const unsigned char *yyt1;
int *piThis, x;
u32 n;
P21Node *pNode;
sp = cur = tok = &pParse->zP21[i];
piThis = cxtStack + pParse->iDepth;
#line 832 "p21sql.l"
start:
tok = cur;
#line 822 "p21sql.c"
{
unsigned char yych;
yych = *cur;
if (yych <= '\r') {
if (yych <= 0x0008) goto yy2;
if (yych <= '\n') goto yy3;
if (yych >= '\r') goto yy3;
} else {
if (yych <= ' ') {
if (yych >= ' ') goto yy3;
} else {
if (yych == '(') goto yy6;
}
}
yy2:
#line 850 "p21sql.l"
{
/* (simple_entity_instance) parameter_list */
*piThis = p21ParseAddNode(pParse, P21_RECORD, 0, 0);
if (*piThis < 0) return -1;
if( ++pParse->iDepth > P21_MAX_DEPTH ) return -1;
piThis = cxtStack + pParse->iDepth;
goto params1;
}
#line 848 "p21sql.c"
yy3:
yych = *++cur;
if (yych <= '\f') {
if (yych <= 0x0008) goto yy5;
if (yych <= '\n') goto yy3;
} else {
if (yych <= '\r') goto yy3;
if (yych == ' ') goto yy3;
}
yy5:
#line 838 "p21sql.l"
{
goto start;
}
#line 863 "p21sql.c"
yy6:
++cur;
#line 841 "p21sql.l"
{
/* (complex_entity_instance) parameter_list */
*piThis = p21ParseAddNode(pParse, P21_LIST, 0, 0);
if (*piThis < 0) return -1;
if( ++pParse->iDepth > P21_MAX_DEPTH ) return -1;
piThis = cxtStack + pParse->iDepth;
goto keywords;
}
#line 876 "p21sql.c"
}
#line 859 "p21sql.l"
keywords:
tok = cur;
#line 885 "p21sql.c"
{
unsigned char yych;
yych = *(mrk = cur);
if (yych <= '(') {
if (yych <= '\r') {
if (yych <= 0x0008) goto yy10;
if (yych <= '\n') goto yy11;
if (yych >= '\r') goto yy11;
} else {
if (yych <= 0x001F) goto yy10;
if (yych <= ' ') goto yy11;
if (yych <= '!') goto yy14;
}
} else {
if (yych <= '^') {
if (yych <= ')') goto yy16;
if (yych <= '@') goto yy10;
if (yych <= 'Z') goto yy18;
} else {
if (yych == '`') goto yy10;
if (yych <= 'z') goto yy18;
}
}
yy10:
#line 885 "p21sql.l"
{
/* fix-up and revert to P21_RECORD */
pNode = pParse->aNode + *(piThis - 1);
pNode->eType = P21_RECORD;
assert(pParse->iDepth == 1);
goto params1;
}
#line 918 "p21sql.c"
yy11:
yych = *++cur;
if (yych <= '\f') {
if (yych <= 0x0008) goto yy13;
if (yych <= '\n') goto yy11;
} else {
if (yych <= '\r') goto yy11;
if (yych == ' ') goto yy11;
}
yy13:
#line 865 "p21sql.l"
{
goto keywords;
}
#line 933 "p21sql.c"
yy14:
yych = *++cur;
if (yych <= '^') {
if (yych <= '@') goto yy15;
if (yych <= 'Z') goto yy18;
} else {
if (yych == '`') goto yy15;
if (yych <= 'z') goto yy18;
}
yy15:
cur = mrk;
goto yy10;
yy16:
++cur;
#line 877 "p21sql.l"
{
piThis = cxtStack + --pParse->iDepth;
pNode = pParse->aNode + *piThis;
assert(pNode->eType == P21_LIST);
pNode->n = pParse->nNode - (u32)*piThis - 1;
goto eol;
}
#line 957 "p21sql.c"
yy18:
yych = *++cur;
if (yych <= '(') {
if (yych <= '\r') {
if (yych <= 0x0008) goto yy15;
if (yych <= '\n') {
yyt1 = cur;
goto yy20;
}
if (yych <= '\f') goto yy15;
yyt1 = cur;
} else {
if (yych == ' ') {
yyt1 = cur;
goto yy20;
}
if (yych <= '\'') goto yy15;
yyt1 = cur;
goto yy22;
}
} else {
if (yych <= 'Z') {
if (yych <= '/') goto yy15;
if (yych <= '9') goto yy18;
if (yych <= '@') goto yy15;
goto yy18;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy15;
goto yy18;
} else {
if (yych <= '`') goto yy15;
if (yych <= 'z') goto yy18;
goto yy15;
}
}
}
yy20:
yych = *++cur;
if (yych <= '\r') {
if (yych <= 0x0008) goto yy15;
if (yych <= '\n') goto yy20;
if (yych <= '\f') goto yy15;
goto yy20;
} else {
if (yych <= ' ') {
if (yych <= 0x001F) goto yy15;
goto yy20;
} else {
if (yych != '(') goto yy15;
}
}
yy22:
++cur;
end = yyt1;
#line 868 "p21sql.l"
{
*piThis = p21ParseAddNode(pParse, P21_RECORD, 0, tok);
if (*piThis < 0) return -1;
pParse->aNode[*piThis].n_kw = (u16)(end - tok);
if( ++pParse->iDepth > P21_MAX_DEPTH ) return -1;
piThis = cxtStack + pParse->iDepth;
goto params2;
}
#line 1023 "p21sql.c"
}
#line 892 "p21sql.l"
params1:
tok = cur;
#line 1032 "p21sql.c"
{
unsigned char yych;
unsigned int yyaccept = 0;
yych = *(mrk = cur);
if (yych <= ')') {
if (yych <= '!') {
if (yych <= '\f') {
if (yych <= 0x0008) goto yy26;
if (yych <= '\n') goto yy27;
} else {
if (yych <= '\r') goto yy27;
if (yych <= 0x001F) goto yy26;
if (yych <= ' ') goto yy27;
goto yy30;
}
} else {
if (yych <= '$') {
if (yych <= '"') goto yy32;
if (yych <= '#') goto yy33;
goto yy34;
} else {
if (yych <= '&') goto yy26;
if (yych <= '\'') goto yy36;
if (yych <= '(') goto yy38;
goto yy40;
}
}
} else {
if (yych <= '9') {
if (yych <= ',') {
if (yych <= '*') goto yy42;
if (yych <= '+') goto yy44;
goto yy46;
} else {
if (yych <= '-') goto yy44;
if (yych <= '.') goto yy48;
if (yych >= '0') goto yy49;
}
} else {
if (yych <= '^') {
if (yych <= '@') goto yy26;
if (yych <= 'Z') goto yy52;
} else {
if (yych == '`') goto yy26;
if (yych <= 'z') goto yy52;
}
}
}
yy26:
#line 959 "p21sql.l"
{
if (pParse->iDepth) --pParse->iDepth;
piThis = cxtStack + pParse->iDepth;
pNode = pParse->aNode + *piThis;
assert(pNode->eType == P21_RECORD);
pNode->n = pParse->nNode - (u32)*piThis - 1;
goto eol;
}
#line 1091 "p21sql.c"
yy27:
yych = *++cur;
if (yych <= '\f') {
if (yych <= 0x0008) goto yy29;
if (yych <= '\n') goto yy27;
} else {
if (yych <= '\r') goto yy27;
if (yych == ' ') goto yy27;
}
yy29:
#line 898 "p21sql.l"
{
goto params1;
}
#line 1106 "p21sql.c"
yy30:
yych = *++cur;
if (yych <= '^') {
if (yych <= '@') goto yy31;
if (yych <= 'Z') goto yy52;
} else {
if (yych == '`') goto yy31;
if (yych <= 'z') goto yy52;
}
yy31:
cur = mrk;
if (yyaccept <= 1) {
if (yyaccept == 0) {
goto yy26;
} else {
goto yy60;
}
} else {
goto yy67;
}
yy32:
yych = *++cur;
if (yych <= '/') goto yy31;
if (yych <= '3') goto yy54;
goto yy31;
yy33:
yych = *++cur;
if (yych <= '/') goto yy31;
if (yych <= '9') goto yy56;
goto yy31;
yy34:
++cur;
#line 945 "p21sql.l"
{
p21ParseAddNode(pParse, P21_EMPTY, cur - tok, tok);
goto params1;
}
#line 1144 "p21sql.c"
yy36:
yych = *++cur;
if (yych <= 'Z') {
if (yych <= 0x001F) goto yy31;
if (yych == '\'') goto yy59;
goto yy36;
} else {
if (yych <= '\\') {
if (yych <= '[') goto yy31;
goto yy61;
} else {
if (yych <= ']') goto yy31;
if (yych <= '~') goto yy36;
goto yy31;
}
}
yy38:
++cur;
#line 910 "p21sql.l"
{
*piThis = p21ParseAddNode(pParse, P21_LIST, 0, 0);
if (*piThis < 0) return -1;
if( ++pParse->iDepth > P21_MAX_DEPTH ) return -1;
piThis = cxtStack + pParse->iDepth;
goto params1;
}
#line 1172 "p21sql.c"
yy40:
++cur;
#line 953 "p21sql.l"
{
piThis = cxtStack + --pParse->iDepth;
pNode = pParse->aNode + *piThis;
pNode->n = pParse->nNode - (u32)*piThis - 1;
goto params1;
}
#line 1182 "p21sql.c"
yy42:
++cur;
#line 949 "p21sql.l"
{
p21ParseAddNode(pParse, P21_DERIVED, cur - tok, tok);
goto params1;
}
#line 1190 "p21sql.c"
yy44:
yych = *++cur;
if (yych <= ',') {
if (yych == '+') goto yy44;
goto yy31;
} else {
if (yych <= '-') goto yy44;
if (yych <= '/') goto yy31;
if (yych <= '9') goto yy49;
goto yy31;
}
yy46:
++cur;
#line 918 "p21sql.l"
{
goto params1;
}
#line 1208 "p21sql.c"
yy48:
yych = *++cur;
if (yych <= '@') goto yy31;
if (yych <= 'Z') goto yy63;
if (yych == '_') goto yy63;
goto yy31;
yy49:
yych = *++cur;
if (yych == '.') goto yy65;
if (yych <= '/') goto yy51;
if (yych <= '9') goto yy49;
yy51:
#line 925 "p21sql.l"
{
p21ParseAddNode(pParse, P21_INTEGER, cur - tok, tok);
goto params1;
}
#line 1226 "p21sql.c"
yy52:
yych = *++cur;
if (yych <= '(') {
if (yych <= '\r') {
if (yych <= 0x0008) goto yy31;
if (yych <= '\n') {
yyt1 = cur;
goto yy68;
}
if (yych <= '\f') goto yy31;
yyt1 = cur;
goto yy68;
} else {
if (yych == ' ') {
yyt1 = cur;
goto yy68;
}
if (yych <= '\'') goto yy31;
yyt1 = cur;
goto yy70;
}
} else {
if (yych <= 'Z') {
if (yych <= '/') goto yy31;
if (yych <= '9') goto yy52;
if (yych <= '@') goto yy31;
goto yy52;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy31;
goto yy52;
} else {
if (yych <= '`') goto yy31;
if (yych <= 'z') goto yy52;
goto yy31;
}
}
}
yy54:
yych = *++cur;
if (yych <= '/') {
if (yych == '"') goto yy72;
goto yy31;
} else {
if (yych <= '9') goto yy54;
if (yych <= '@') goto yy31;
if (yych <= 'F') goto yy54;
goto yy31;
}
yy56:
yych = *++cur;
if (yych <= '/') goto yy58;
if (yych <= '9') goto yy56;
yy58:
#line 941 "p21sql.l"
{
p21ParseAddNode(pParse, P21_EID, cur - tok, tok);
goto params1;
}
#line 1286 "p21sql.c"
yy59:
yyaccept = 1;
yych = *(mrk = ++cur);
if (yych == '\'') goto yy36;
yy60:
#line 929 "p21sql.l"
{
p21ParseAddNode(pParse, P21_STRING, cur - tok, tok);
goto params1;
}
#line 1297 "p21sql.c"
yy61:
yych = *++cur;
if (yych <= 'S') {
if (yych <= '&') {
if (yych <= 0x001F) goto yy31;
goto yy36;
} else {
if (yych <= '\'') goto yy59;
if (yych <= 'R') goto yy36;
goto yy74;
}
} else {
if (yych <= '\\') {
if (yych <= 'Z') goto yy36;
if (yych <= '[') goto yy31;
goto yy61;
} else {
if (yych <= ']') goto yy31;
if (yych <= '~') goto yy36;
goto yy31;
}
}
yy63:
yych = *++cur;
if (yych <= '9') {
if (yych == '.') goto yy75;
if (yych <= '/') goto yy31;
goto yy63;
} else {
if (yych <= 'Z') {
if (yych <= '@') goto yy31;
goto yy63;
} else {
if (yych == '_') goto yy63;
goto yy31;
}
}
yy65:
yyaccept = 2;
yych = *(mrk = ++cur);
if (yych <= '/') goto yy67;
if (yych <= '9') goto yy65;
if (yych == 'E') goto yy77;
yy67:
#line 921 "p21sql.l"
{
p21ParseAddNode(pParse, P21_REAL, cur - tok, tok);
goto params1;
}
#line 1347 "p21sql.c"
yy68:
yych = *++cur;
if (yych <= '\r') {
if (yych <= 0x0008) goto yy31;
if (yych <= '\n') goto yy68;
if (yych <= '\f') goto yy31;
goto yy68;
} else {
if (yych <= ' ') {
if (yych <= 0x001F) goto yy31;
goto yy68;
} else {
if (yych != '(') goto yy31;
}
}
yy70:
++cur;
end = yyt1;
#line 901 "p21sql.l"
{
*piThis = p21ParseAddNode(pParse, P21_RECORD, 0, tok);
if (*piThis < 0) return -1;
pParse->aNode[*piThis].n_kw = (u16)(end - tok);
if( ++pParse->iDepth > P21_MAX_DEPTH ) return -1;
piThis = cxtStack + pParse->iDepth;
goto params1;
}
#line 1376 "p21sql.c"
yy72:
++cur;
#line 933 "p21sql.l"
{
p21ParseAddNode(pParse, P21_BINARY, cur - tok, tok);
goto params1;
}
#line 1384 "p21sql.c"
yy74:
yych = *++cur;
if (yych <= 'Z') {
if (yych <= 0x001F) goto yy31;
if (yych == '\'') goto yy59;
goto yy36;
} else {
if (yych <= '\\') {
if (yych <= '[') goto yy31;
goto yy79;
} else {
if (yych <= ']') goto yy31;
if (yych <= '~') goto yy36;
goto yy31;
}
}
yy75:
++cur;
#line 937 "p21sql.l"
{
p21ParseAddNode(pParse, P21_ENUMERATION, cur - tok, tok);
goto params1;
}
#line 1408 "p21sql.c"
yy77:
yych = *++cur;
if (yych <= ',') {
if (yych == '+') goto yy77;
goto yy31;
} else {
if (yych <= '-') goto yy77;
if (yych <= '/') goto yy31;
if (yych <= '9') goto yy80;
goto yy31;
}
yy79:
yych = *++cur;
if (yych <= 'S') {
if (yych <= '&') {
if (yych <= 0x001F) goto yy31;
goto yy36;
} else {
if (yych <= '\'') goto yy82;
if (yych <= 'R') goto yy36;
goto yy74;
}
} else {
if (yych <= '\\') {
if (yych <= 'Z') goto yy36;
if (yych <= '[') goto yy31;
goto yy61;
} else {
if (yych <= ']') goto yy31;
if (yych <= '~') goto yy36;
goto yy31;
}
}
yy80:
yych = *++cur;
if (yych <= '/') goto yy67;
if (yych <= '9') goto yy80;
goto yy67;
yy82:
yyaccept = 1;
yych = *(mrk = ++cur);
if (yych <= 'Z') {
if (yych <= 0x001F) goto yy60;
if (yych == '\'') goto yy82;
goto yy36;
} else {
if (yych <= '\\') {
if (yych <= '[') goto yy60;
goto yy61;
} else {
if (yych <= ']') goto yy60;
if (yych <= '~') goto yy36;
goto yy60;
}
}
}
#line 967 "p21sql.l"
params2:
tok = cur;
#line 1472 "p21sql.c"
{
unsigned char yych;
unsigned int yyaccept = 0;
yych = *cur;
if (yych <= ')') {
if (yych <= '!') {
if (yych <= '\f') {
if (yych <= 0x0008) goto yy86;
if (yych <= '\n') goto yy87;
} else {
if (yych <= '\r') goto yy87;
if (yych <= 0x001F) goto yy86;
if (yych <= ' ') goto yy87;
goto yy90;
}
} else {
if (yych <= '$') {
if (yych <= '"') goto yy91;
if (yych <= '#') goto yy92;
goto yy93;
} else {
if (yych <= '&') goto yy86;
if (yych <= '\'') goto yy95;
if (yych <= '(') goto yy97;
goto yy99;
}
}
} else {
if (yych <= '9') {
if (yych <= ',') {
if (yych <= '*') goto yy101;
if (yych <= '+') goto yy103;
goto yy105;
} else {
if (yych <= '-') goto yy103;
if (yych <= '.') goto yy107;
if (yych >= '0') goto yy108;
}
} else {
if (yych <= '^') {
if (yych <= '@') goto yy86;
if (yych <= 'Z') goto yy111;
} else {
if (yych == '`') goto yy86;
if (yych <= 'z') goto yy111;
}
}
}
yy86:
cur = mrk;
if (yyaccept == 0) {
goto yy119;
} else {
goto yy126;
}
yy87:
yych = *++cur;
if (yych <= '\f') {
if (yych <= 0x0008) goto yy89;
if (yych <= '\n') goto yy87;
} else {
if (yych <= '\r') goto yy87;
if (yych == ' ') goto yy87;
}
yy89:
#line 973 "p21sql.l"
{
goto params2;
}
#line 1542 "p21sql.c"
yy90:
yych = *++cur;
if (yych <= '^') {
if (yych <= '@') goto yy86;
if (yych <= 'Z') goto yy111;
goto yy86;
} else {
if (yych == '`') goto yy86;
if (yych <= 'z') goto yy111;
goto yy86;
}
yy91:
yych = *++cur;
if (yych <= '/') goto yy86;
if (yych <= '3') goto yy113;
goto yy86;
yy92:
yych = *++cur;
if (yych <= '/') goto yy86;
if (yych <= '9') goto yy115;
goto yy86;
yy93:
++cur;
#line 1020 "p21sql.l"
{
p21ParseAddNode(pParse, P21_EMPTY, cur - tok, tok);
goto params2;
}
#line 1571 "p21sql.c"
yy95:
yych = *++cur;
if (yych <= 'Z') {
if (yych <= 0x001F) goto yy86;
if (yych == '\'') goto yy118;
goto yy95;
} else {
if (yych <= '\\') {
if (yych <= '[') goto yy86;
goto yy120;
} else {
if (yych <= ']') goto yy86;
if (yych <= '~') goto yy95;
goto yy86;
}
}
yy97:
++cur;
#line 985 "p21sql.l"
{
*piThis = p21ParseAddNode(pParse, P21_LIST, 0, 0);
if (*piThis < 0) return -1;
if( ++pParse->iDepth > P21_MAX_DEPTH ) return -1;
piThis = cxtStack + pParse->iDepth;
goto params2;
}
#line 1599 "p21sql.c"
yy99:
++cur;
#line 1028 "p21sql.l"
{
piThis = cxtStack + --pParse->iDepth;
pNode = pParse->aNode + *piThis;
pNode->n = pParse->nNode - (u32)*piThis - 1;
if (pParse->iDepth > 1) {
goto params2;
} else {
goto keywords;
}
}
#line 1613 "p21sql.c"
yy101:
++cur;
#line 1024 "p21sql.l"
{
p21ParseAddNode(pParse, P21_DERIVED, cur - tok, tok);
goto params2;
}
#line 1621 "p21sql.c"
yy103:
yych = *++cur;
if (yych <= ',') {
if (yych == '+') goto yy103;
goto yy86;
} else {
if (yych <= '-') goto yy103;
if (yych <= '/') goto yy86;
if (yych <= '9') goto yy108;
goto yy86;
}
yy105:
++cur;
#line 993 "p21sql.l"
{
goto params2;
}
#line 1639 "p21sql.c"
yy107:
yych = *++cur;
if (yych <= '@') goto yy86;
if (yych <= 'Z') goto yy122;
if (yych == '_') goto yy122;
goto yy86;
yy108:
yych = *++cur;
if (yych == '.') goto yy124;
if (yych <= '/') goto yy110;
if (yych <= '9') goto yy108;
yy110:
#line 1000 "p21sql.l"
{
p21ParseAddNode(pParse, P21_INTEGER, cur - tok, tok);
goto params2;
}
#line 1657 "p21sql.c"
yy111:
yych = *++cur;
if (yych <= '(') {
if (yych <= '\r') {
if (yych <= 0x0008) goto yy86;
if (yych <= '\n') {
yyt1 = cur;
goto yy127;
}
if (yych <= '\f') goto yy86;
yyt1 = cur;
goto yy127;
} else {
if (yych == ' ') {
yyt1 = cur;
goto yy127;
}
if (yych <= '\'') goto yy86;
yyt1 = cur;
goto yy129;
}
} else {
if (yych <= 'Z') {
if (yych <= '/') goto yy86;
if (yych <= '9') goto yy111;
if (yych <= '@') goto yy86;
goto yy111;
} else {
if (yych <= '_') {
if (yych <= '^') goto yy86;
goto yy111;
} else {
if (yych <= '`') goto yy86;
if (yych <= 'z') goto yy111;
goto yy86;
}
}
}
yy113:
yych = *++cur;
if (yych <= '/') {
if (yych == '"') goto yy131;
goto yy86;
} else {
if (yych <= '9') goto yy113;
if (yych <= '@') goto yy86;
if (yych <= 'F') goto yy113;
goto yy86;
}
yy115:
yych = *++cur;
if (yych <= '/') goto yy117;
if (yych <= '9') goto yy115;
yy117:
#line 1016 "p21sql.l"
{
p21ParseAddNode(pParse, P21_EID, cur - tok, tok);
goto params2;
}
#line 1717 "p21sql.c"
yy118:
yyaccept = 0;
yych = *(mrk = ++cur);
if (yych == '\'') goto yy95;
yy119:
#line 1004 "p21sql.l"
{
p21ParseAddNode(pParse, P21_STRING, cur - tok, tok);
goto params2;
}
#line 1728 "p21sql.c"
yy120:
yych = *++cur;
if (yych <= 'S') {
if (yych <= '&') {
if (yych <= 0x001F) goto yy86;
goto yy95;
} else {
if (yych <= '\'') goto yy118;
if (yych <= 'R') goto yy95;
goto yy133;
}
} else {
if (yych <= '\\') {
if (yych <= 'Z') goto yy95;
if (yych <= '[') goto yy86;
goto yy120;
} else {
if (yych <= ']') goto yy86;
if (yych <= '~') goto yy95;
goto yy86;
}
}
yy122:
yych = *++cur;
if (yych <= '9') {
if (yych == '.') goto yy134;
if (yych <= '/') goto yy86;
goto yy122;
} else {
if (yych <= 'Z') {
if (yych <= '@') goto yy86;
goto yy122;
} else {
if (yych == '_') goto yy122;
goto yy86;
}
}
yy124:
yyaccept = 1;
yych = *(mrk = ++cur);
if (yych <= '/') goto yy126;
if (yych <= '9') goto yy124;
if (yych == 'E') goto yy136;
yy126:
#line 996 "p21sql.l"
{
p21ParseAddNode(pParse, P21_REAL, cur - tok, tok);
goto params2;
}
#line 1778 "p21sql.c"
yy127:
yych = *++cur;
if (yych <= '\r') {
if (yych <= 0x0008) goto yy86;
if (yych <= '\n') goto yy127;
if (yych <= '\f') goto yy86;
goto yy127;
} else {
if (yych <= ' ') {
if (yych <= 0x001F) goto yy86;
goto yy127;
} else {
if (yych != '(') goto yy86;
}
}
yy129:
++cur;
end = yyt1;
#line 976 "p21sql.l"
{
*piThis = p21ParseAddNode(pParse, P21_RECORD, 0, tok);
if (*piThis < 0) return -1;
pParse->aNode[*piThis].n_kw = (u16)(end - tok);
if( ++pParse->iDepth > P21_MAX_DEPTH ) return -1;
piThis = cxtStack + pParse->iDepth;
goto params2;
}
#line 1807 "p21sql.c"
yy131:
++cur;
#line 1008 "p21sql.l"
{
p21ParseAddNode(pParse, P21_BINARY, cur - tok, tok);
goto params2;
}
#line 1815 "p21sql.c"
yy133:
yych = *++cur;
if (yych <= 'Z') {
if (yych <= 0x001F) goto yy86;
if (yych == '\'') goto yy118;
goto yy95;
} else {
if (yych <= '\\') {
if (yych <= '[') goto yy86;
goto yy138;
} else {
if (yych <= ']') goto yy86;
if (yych <= '~') goto yy95;
goto yy86;
}
}
yy134:
++cur;
#line 1012 "p21sql.l"
{
p21ParseAddNode(pParse, P21_ENUMERATION, cur - tok, tok);
goto params2;
}
#line 1839 "p21sql.c"
yy136:
yych = *++cur;
if (yych <= ',') {
if (yych == '+') goto yy136;
goto yy86;
} else {
if (yych <= '-') goto yy136;
if (yych <= '/') goto yy86;
if (yych <= '9') goto yy139;
goto yy86;
}
yy138:
yych = *++cur;
if (yych <= 'S') {
if (yych <= '&') {
if (yych <= 0x001F) goto yy86;
goto yy95;
} else {
if (yych <= '\'') goto yy141;
if (yych <= 'R') goto yy95;
goto yy133;
}
} else {
if (yych <= '\\') {
if (yych <= 'Z') goto yy95;
if (yych <= '[') goto yy86;
goto yy120;
} else {
if (yych <= ']') goto yy86;
if (yych <= '~') goto yy95;
goto yy86;
}
}
yy139:
yych = *++cur;
if (yych <= '/') goto yy126;
if (yych <= '9') goto yy139;
goto yy126;
yy141:
yyaccept = 0;
yych = *(mrk = ++cur);
if (yych <= 'Z') {
if (yych <= 0x001F) goto yy119;
if (yych == '\'') goto yy141;
goto yy95;
} else {
if (yych <= '\\') {
if (yych <= '[') goto yy119;
goto yy120;
} else {
if (yych <= ']') goto yy119;
if (yych <= '~') goto yy95;
goto yy119;
}
}
}
#line 1039 "p21sql.l"
eol:
tok = cur;
#line 1903 "p21sql.c"
{
unsigned char yych;
yych = *cur;
if (yych >= 0x0001) goto yy147;
++cur;
#line 1045 "p21sql.l"
{
return cur - sp;
}
#line 1913 "p21sql.c"
yy147:
++cur;
#line 1048 "p21sql.l"
{
return -1;
}
#line 1920 "p21sql.c"
}
#line 1051 "p21sql.l"
}
/*
** Parse a complete P21 string. Return 0 on success or non-zero if there
** are any errors. If an error occurs, free all memory associated with
** pParse.
**
** pParse is uninitialized when this routine is called.
*/
static int p21Parse(
P21Parse *pParse, /* Initialize and fill this P21Parse object */
sqlite3_context *pCtx, /* Report errors here */
const char *zP21 /* Input P21 text to be parsed */
){
int i;
memset(pParse, 0, sizeof(*pParse));
if( zP21==0 ) return 1;
pParse->zP21 = zP21;
i = p21ParseValue(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
assert( pParse->iDepth==0 );
}
if( i<=0 ){
if( pCtx!=0 ){
if( pParse->oom ){
sqlite3_result_error_nomem(pCtx);
}else{
sqlite3_result_error(pCtx, "malformed P21", -1);
}
}
p21ParseReset(pParse);
return 1;
}
return 0;
}
/* Mark node i of pParse as being a child of iParent. Call recursively
** to fill in all the descendants of node i.
*/
static void p21ParseFillInParentage(P21Parse *pParse, u32 i, u32 iParent){
P21Node *pNode = &pParse->aNode[i];
u32 j;
pParse->aUp[i] = iParent;
switch( pNode->eType ){
case P21_RECORD:
case P21_LIST: {
for(j=1; j<=pNode->n; j += p21NodeSize(pNode+j)){
p21ParseFillInParentage(pParse, i+j, i);
}
break;
}
default: {
break;
}
}
}
/*
** Compute the parentage of all nodes in a completed parse.
*/
static int p21ParseFindParents(P21Parse *pParse){
u32 *aUp;
assert( pParse->aUp==0 );
aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
if( aUp==0 ){
pParse->oom = 1;
return SQLITE_NOMEM;
}
p21ParseFillInParentage(pParse, 0, 0);
return SQLITE_OK;
}
/*
** Magic number used for the P21 parse cache in sqlite3_get_auxdata()
*/
#define P21_CACHE_ID (-429938) /* First cache entry */
#define P21_CACHE_SZ 4 /* Max number of cache entries */
/*
** Obtain a complete parse of the P21 found in the first argument
** of the argv array. Use the sqlite3_get_auxdata() cache for this
** parse if it is available. If the cache is not available or if it
** is no longer valid, parse the P21 again and return the new parse,
** and also register the new parse so that it will be available for
** future sqlite3_get_auxdata() calls.
*/
static P21Parse *p21ParseCached(
sqlite3_context *pCtx,
sqlite3_value **argv,
sqlite3_context *pErrCtx
){
const char *zP21 = (const char*)sqlite3_value_text(argv[0]);
int nP21 = sqlite3_value_bytes(argv[0]);
P21Parse *p;
P21Parse *pMatch = 0;
int iKey;
int iMinKey = 0;
u32 iMinHold = 0xffffffff;
u32 iMaxHold = 0;
if( zP21==0 ) return 0;
for(iKey=0; iKeynP21==nP21
&& memcmp(p->zP21,zP21,nP21)==0
){
p->nErr = 0;
pMatch = p;
}else if( p->iHoldiHold;
iMinKey = iKey;
}
if( p->iHold>iMaxHold ){
iMaxHold = p->iHold;
}
}
if( pMatch ){
pMatch->nErr = 0;
pMatch->iHold = iMaxHold+1;
return pMatch;
}
p = sqlite3_malloc64( sizeof(*p) + nP21 + 1 );
if( p==0 ){
sqlite3_result_error_nomem(pCtx);
return 0;
}
memset(p, 0, sizeof(*p));
p->zP21 = (char*)&p[1];
memcpy((char*)p->zP21, zP21, nP21+1);
if( p21Parse(p, pErrCtx, p->zP21) ){
sqlite3_free(p);
return 0;
}
p->nP21 = nP21;
p->iHold = iMaxHold+1;
sqlite3_set_auxdata(pCtx, P21_CACHE_ID+iMinKey, p,
(void(*)(void*))p21ParseFree);
return (P21Parse*)sqlite3_get_auxdata(pCtx, P21_CACHE_ID+iMinKey);
}
/*
** Compare the OBJECT label at pNode against zKey,nKey. Return true on
** a match.
*/
static int p21LabelCompare(P21Node *pNode, const char *zKey, u32 nKey){
if( pNode->jnFlags & PNODE_RAW ){
if( pNode->n!=nKey ) return 0;
return strncmp(pNode->u.zJContent, zKey, nKey)==0;
}else{
if( pNode->n!=nKey+2 ) return 0;
return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
}
}
/* forward declaration */
static P21Node *p21LookupAppend(P21Parse*,const char*,int*,const char**);
/*
** Search along zPath to find the node specified. Return a pointer
** to that node, or NULL if zPath is malformed or if there is no such
** node.
**
** If pApnd!=0, then try to append new nodes to complete zPath if it is
** possible to do so and if no existing node corresponds to zPath. If
** new nodes are appended *pApnd is set to 1.
*/
static P21Node *p21LookupStep(
P21Parse *pParse, /* The P21 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 */
const char **pzErr /* Make *pzErr point to any syntax error in zPath */
){
u32 i, j, nKey;
const char *zKey;
P21Node *pRoot = &pParse->aNode[iRoot];
if( zPath[0]==0 ) return pRoot;
if( pRoot->jnFlags & PNODE_REPLACE ) return 0;
if( zPath[0]=='.' ){
if( pRoot->eType!=P21_RECORD ) return 0;
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
for(i=1; zPath[i] && zPath[i]!='"'; i++){}
nKey = i-1;
if( zPath[i] ){
i++;
}else{
*pzErr = zPath;
return 0;
}
}else{
zKey = zPath;
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
}
if( nKey==0 ){
*pzErr = zPath;
return 0;
}
j = 1;
for(;;){
while( j<=pRoot->n ){
if( p21LabelCompare(pRoot+j, zKey, nKey) ){
return p21LookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
}
j++;
j += p21NodeSize(&pRoot[j]);
}
if( (pRoot->jnFlags & PNODE_APPEND)==0 ) break;
iRoot += pRoot->u.iAppend;
pRoot = &pParse->aNode[iRoot];
j = 1;
}
if( pApnd ){
u32 iStart, iLabel;
P21Node *pNode;
iStart = p21ParseAddNode(pParse, P21_RECORD, 2, 0);
iLabel = p21ParseAddNode(pParse, P21_STRING, nKey, zKey);
zPath += i;
pNode = p21LookupAppend(pParse, zPath, pApnd, pzErr);
if( pParse->oom ) return 0;
if( pNode ){
pRoot = &pParse->aNode[iRoot];
pRoot->u.iAppend = iStart - iRoot;
pRoot->jnFlags |= PNODE_APPEND;
pParse->aNode[iLabel].jnFlags |= PNODE_RAW;
}
return pNode;
}
}else if( zPath[0]=='[' ){
i = 0;
j = 1;
while( safe_isdigit(zPath[j]) ){
i = i*10 + zPath[j] - '0';
j++;
}
if( j<2 || zPath[j]!=']' ){
if( zPath[1]=='#' ){
P21Node *pBase = pRoot;
int iBase = iRoot;
if( pRoot->eType!=P21_LIST && pRoot->eType!=P21_RECORD) return 0;
for(;;){
while( j<=pBase->n ){
if( (pBase[j].jnFlags & PNODE_REMOVE)==0 ) i++;
j += p21NodeSize(&pBase[j]);
}
if( (pBase->jnFlags & PNODE_APPEND)==0 ) break;
iBase += pBase->u.iAppend;
pBase = &pParse->aNode[iBase];
j = 1;
}
j = 2;
if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){
unsigned int x = 0;
j = 3;
do{
x = x*10 + zPath[j] - '0';
j++;
}while( safe_isdigit(zPath[j]) );
if( x>i ) return 0;
i -= x;
}
if( zPath[j]!=']' ){
*pzErr = zPath;
return 0;
}
}else{
*pzErr = zPath;
return 0;
}
}
if( pRoot->eType!=P21_LIST && pRoot->eType!=P21_RECORD ) return 0;
zPath += j + 1;
j = 1;
for(;;){
while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & PNODE_REMOVE)!=0) ){
if( (pRoot[j].jnFlags & PNODE_REMOVE)==0 ) i--;
j += p21NodeSize(&pRoot[j]);
}
if( (pRoot->jnFlags & PNODE_APPEND)==0 ) break;
iRoot += pRoot->u.iAppend;
pRoot = &pParse->aNode[iRoot];
j = 1;
}
if( j<=pRoot->n ){
return p21LookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
}
if( i==0 && pApnd ){
u32 iStart;
P21Node *pNode;
iStart = p21ParseAddNode(pParse, P21_LIST, 1, 0);
pNode = p21LookupAppend(pParse, zPath, pApnd, pzErr);
if( pParse->oom ) return 0;
if( pNode ){
pRoot = &pParse->aNode[iRoot];
pRoot->u.iAppend = iStart - iRoot;
pRoot->jnFlags |= PNODE_APPEND;
}
return pNode;
}
}else{
*pzErr = zPath;
}
return 0;
}
/*
** Append content to pParse that will complete zPath. Return a pointer
** to the inserted node, or return NULL if the append fails.
*/
static P21Node *p21LookupAppend(
P21Parse *pParse, /* Append content to the P21 parse */
const char *zPath, /* Description of content to append */
int *pApnd, /* Set this flag to 1 */
const char **pzErr /* Make this point to any syntax error */
){
*pApnd = 1;
if( zPath[0]==0 ){
p21ParseAddNode(pParse, P21_EMPTY, 0, 0);
return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
}
if( zPath[0]=='.' ){
p21ParseAddNode(pParse, P21_RECORD, 0, 0);
}else if( strncmp(zPath,"[0]",3)==0 ){
p21ParseAddNode(pParse, P21_LIST, 0, 0);
}else{
return 0;
}
if( pParse->oom ) return 0;
return p21LookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
}
/*
** Return the text of a syntax error message on a P21 path. Space is
** obtained from sqlite3_malloc().
*/
static char *p21PathSyntaxError(const char *zErr){
return sqlite3_mprintf("P21 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.
*/
static P21Node *p21Lookup(
P21Parse *pParse, /* The P21 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 */
){
const char *zErr = 0;
P21Node *pNode = 0;
char *zMsg;
if( zPath==0 ) return 0;
if( zPath[0]!='$' ){
zErr = zPath;
goto lookup_err;
}
zPath++;
pNode = p21LookupStep(pParse, 0, zPath, pApnd, &zErr);
if( zErr==0 ) return pNode;
lookup_err:
pParse->nErr++;
assert( zErr!=0 && pCtx!=0 );
zMsg = p21PathSyntaxError(zErr);
if( zMsg ){
sqlite3_result_error(pCtx, zMsg, -1);
sqlite3_free(zMsg);
}else{
sqlite3_result_error_nomem(pCtx);
}
return 0;
}
/*
** Report the wrong number of arguments for p21_insert(), p21_replace()
** or p21_set().
*/
static void p21WrongNumArgs(
sqlite3_context *pCtx,
const char *zFuncName
){
char *zMsg = sqlite3_mprintf("p21_%s() needs an odd number of arguments",
zFuncName);
sqlite3_result_error(pCtx, zMsg, -1);
sqlite3_free(zMsg);
}
/*
** Mark all NULL entries in the Object passed in as PNODE_REMOVE.
*/
static void p21RemoveAllNulls(P21Node *pNode){
int i, n;
assert( pNode->eType==P21_RECORD );
n = pNode->n;
for(i=2; i<=n; i += p21NodeSize(&pNode[i])+1){
switch( pNode[i].eType ){
case P21_EMPTY:
pNode[i].jnFlags |= PNODE_REMOVE;
break;
case P21_RECORD:
p21RemoveAllNulls(&pNode[i]);
break;
}
}
}
/****************************************************************************
** SQL functions used for testing and debugging
****************************************************************************/
#ifdef SQLITE_DEBUG
/*
** The p21_parse(P21) function returns a string which describes
** a parse of the P21 provided. Or it returns NULL if P21 is not
** well-formed.
*/
static void p21ParseFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21String s; /* Output string - not real P21 */
P21Parse x; /* The parse */
u32 i;
assert( argc==1 );
if( p21Parse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
p21ParseFindParents(&x);
p21Init(&s, ctx);
for(i=0; inNode );
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
pNode = p21Lookup(p, zPath, 0, ctx);
}else{
pNode = p->aNode;
}
if( pNode==0 ){
return;
}
if( pNode->eType==P21_LIST ){
assert( (pNode->jnFlags & PNODE_APPEND)==0 );
for(i=1; i<=pNode->n; n++){
i += p21NodeSize(&pNode[i]);
}
}
sqlite3_result_int64(ctx, n);
}
/*
** p21_extract(P21, PATH, ...)
**
** Return the element described by PATH. Return NULL if there is no
** PATH element. If there are multiple PATHs, then return a P21 array
** with the result from each path. Throw an error if the P21 or any PATH
** is malformed.
*/
static void p21ExtractFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21Parse *p; /* The parse */
P21Node *pNode;
const char *zPath;
P21String jx;
int i;
if( argc<2 ) return;
p = p21ParseCached(ctx, argv, ctx);
if( p==0 ) return;
p21Init(&jx, ctx);
p21AppendChar(&jx, '[');
for(i=1; inErr ) break;
if( argc>2 ){
p21AppendSeparator(&jx);
if( pNode ){
p21RenderNode(pNode, &jx, 0);
}else{
p21AppendRaw(&jx, "null", 4);
}
}else if( pNode ){
p21Return(pNode, ctx, 0);
}
}
if( argc>2 && i==argc ){
p21AppendChar(&jx, ']');
p21Result(&jx);
sqlite3_result_subtype(ctx, P21_SUBTYPE);
}
p21Reset(&jx);
}
#if 0
/* TODO: a MergeRecord function could be useful
*/
static P21Node *p21MergePatch(
P21Parse *pParse, /* The P21 parser that contains the TARGET */
u32 iTarget, /* Node of the TARGET in pParse */
P21Node *pPatch /* The PATCH */
){
u32 i, j;
u32 iRoot;
P21Node *pTarget;
if( pPatch->eType!=P21_RECORD ){
return pPatch;
}
assert( iTargetnNode );
pTarget = &pParse->aNode[iTarget];
assert( (pPatch->jnFlags & PNODE_APPEND)==0 );
if( pTarget->eType!=P21_RECORD ){
p21RemoveAllNulls(pPatch);
return pPatch;
}
iRoot = iTarget;
for(i=1; in; i += p21NodeSize(&pPatch[i+1])+1){
u32 nKey;
const char *zKey;
assert( pPatch[i].eType==P21_STRING );
assert( pPatch[i].jnFlags & PNODE_LABEL );
nKey = pPatch[i].n;
zKey = pPatch[i].u.zJContent;
assert( (pPatch[i].jnFlags & PNODE_RAW)==0 );
for(j=1; jn; j += p21NodeSize(&pTarget[j+1])+1 ){
assert( pTarget[j].eType==P21_STRING );
assert( pTarget[j].jnFlags & PNODE_LABEL );
assert( (pPatch[i].jnFlags & PNODE_RAW)==0 );
if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){
if( pTarget[j+1].jnFlags & (PNODE_REMOVE|PNODE_PATCH) ) break;
if( pPatch[i+1].eType==P21_EMPTY ){
pTarget[j+1].jnFlags |= PNODE_REMOVE;
}else{
P21Node *pNew = p21MergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
if( pNew==0 ) return 0;
pTarget = &pParse->aNode[iTarget];
if( pNew!=&pTarget[j+1] ){
pTarget[j+1].u.pPatch = pNew;
pTarget[j+1].jnFlags |= PNODE_PATCH;
}
}
break;
}
}
if( j>=pTarget->n && pPatch[i+1].eType!=P21_EMPTY ){
int iStart, iPatch;
iStart = p21ParseAddNode(pParse, P21_RECORD, 2, 0);
p21ParseAddNode(pParse, P21_STRING, nKey, zKey);
iPatch = p21ParseAddNode(pParse, P21_TRUE, 0, 0);
if( pParse->oom ) return 0;
p21RemoveAllNulls(pPatch);
pTarget = &pParse->aNode[iTarget];
pParse->aNode[iRoot].jnFlags |= PNODE_APPEND;
pParse->aNode[iRoot].u.iAppend = iStart - iRoot;
iRoot = iStart;
pParse->aNode[iPatch].jnFlags |= PNODE_PATCH;
pParse->aNode[iPatch].u.pPatch = &pPatch[i+1];
}
}
return pTarget;
}
/*
** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a P21
** object that is the result of running the RFC 7396 MergePatch() algorithm
** on the two arguments.
*/
static void p21PatchFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21Parse x; /* The P21 that is being patched */
P21Parse y; /* The patch */
P21Node *pResult; /* The result of the merge */
UNUSED_PARAM(argc);
if( p21Parse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
if( p21Parse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){
p21ParseReset(&x);
return;
}
pResult = p21MergePatch(&x, 0, y.aNode);
assert( pResult!=0 || x.oom );
if( pResult ){
p21ReturnP21(pResult, ctx, 0);
}else{
sqlite3_result_error_nomem(ctx);
}
p21ParseReset(&x);
p21ParseReset(&y);
}
#endif
/*
** Implementation of the p21_object(NAME,VALUE,...) function. Return a P21
** object that contains all name/value given in arguments. Or if any name
** is not a string or if any value is a BLOB, throw an error.
*/
static void p21ObjectFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
int i;
P21String jx;
const char *z;
u32 n;
if( argc&1 ){
sqlite3_result_error(ctx, "p21_object() requires an even number "
"of arguments", -1);
return;
}
p21Init(&jx, ctx);
p21AppendChar(&jx, '{');
for(i=0; ijnFlags |= PNODE_REMOVE;
}
if( (x.aNode[0].jnFlags & PNODE_REMOVE)==0 ){
p21ReturnP21(x.aNode, ctx, 0);
}
remove_done:
p21ParseReset(&x);
}
/*
** p21_replace(P21, PATH, VALUE, ...)
**
** Replace the value at PATH with VALUE. If PATH does not already exist,
** this routine is a no-op. If P21 or PATH is malformed, throw an error.
*/
static void p21ReplaceFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21Parse x; /* The parse */
P21Node *pNode;
const char *zPath;
u32 i;
if( argc<1 ) return;
if( (argc&1)==0 ) {
p21WrongNumArgs(ctx, "replace");
return;
}
if( p21Parse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
assert( x.nNode );
for(i=1; i<(u32)argc; i+=2){
zPath = (const char*)sqlite3_value_text(argv[i]);
pNode = p21Lookup(&x, zPath, 0, ctx);
if( x.nErr ) goto replace_err;
if( pNode ){
pNode->jnFlags |= (u8)PNODE_REPLACE;
pNode->u.iReplace = i + 1;
}
}
if( x.aNode[0].jnFlags & PNODE_REPLACE ){
sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
}else{
p21ReturnP21(x.aNode, ctx, argv);
}
replace_err:
p21ParseReset(&x);
}
/*
** p21_set(P21, PATH, VALUE, ...)
**
** Set the value at PATH to VALUE. Create the PATH if it does not already
** exist. Overwrite existing values that do exist.
** If P21 or PATH is malformed, throw an error.
**
** p21_insert(P21, PATH, VALUE, ...)
**
** Create PATH and initialize it to VALUE. If PATH already exists, this
** routine is a no-op. If P21 or PATH is malformed, throw an error.
*/
static void p21SetFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21Parse x; /* The parse */
P21Node *pNode;
const char *zPath;
u32 i;
int bApnd;
int bIsSet = *(int*)sqlite3_user_data(ctx);
if( argc<1 ) return;
if( (argc&1)==0 ) {
p21WrongNumArgs(ctx, bIsSet ? "set" : "insert");
return;
}
if( p21Parse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
assert( x.nNode );
for(i=1; i<(u32)argc; i+=2){
zPath = (const char*)sqlite3_value_text(argv[i]);
bApnd = 0;
pNode = p21Lookup(&x, zPath, &bApnd, ctx);
if( x.oom ){
sqlite3_result_error_nomem(ctx);
goto p21SetDone;
}else if( x.nErr ){
goto p21SetDone;
}else if( pNode && (bApnd || bIsSet) ){
pNode->jnFlags |= (u8)PNODE_REPLACE;
pNode->u.iReplace = i + 1;
}
}
if( x.aNode[0].jnFlags & PNODE_REPLACE ){
sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
}else{
p21ReturnP21(x.aNode, ctx, argv);
}
p21SetDone:
p21ParseReset(&x);
}
/*
** p21_type(P21)
** p21_type(P21, PATH)
**
** Return the top-level "type" of a P21 string. Throw an error if
** either the P21 or PATH inputs are not well-formed.
*/
static void p21TypeFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21Parse *p; /* The parse */
const char *zPath;
P21Node *pNode;
p = p21ParseCached(ctx, argv, ctx);
if( p==0 ) return;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
pNode = p21Lookup(p, zPath, 0, ctx);
}else{
pNode = p->aNode;
}
if( pNode ){
sqlite3_result_text(ctx, p21Type[pNode->eType], -1, SQLITE_STATIC);
}
}
/*
** p21_valid(P21)
**
** Return 1 if P21 is a well-formed P21 string according to RFC-7159.
** Return 0 otherwise.
*/
static void p21ValidFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21Parse *p; /* The parse */
UNUSED_PARAM(argc);
p = p21ParseCached(ctx, argv, 0);
sqlite3_result_int(ctx, p!=0);
}
/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
/*
** p21_group_array(VALUE)
**
** Return a P21 array composed of all values in the aggregate.
*/
static void p21ArrayStep(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21String *pStr;
UNUSED_PARAM(argc);
pStr = (P21String*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
p21Init(pStr, ctx);
p21AppendChar(pStr, '[');
}else if( pStr->nUsed>1 ){
p21AppendChar(pStr, ',');
pStr->pCtx = ctx;
}
p21AppendValue(pStr, argv[0]);
}
}
static void p21ArrayCompute(sqlite3_context *ctx, int isFinal){
P21String *pStr;
pStr = (P21String*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
pStr->pCtx = ctx;
p21AppendChar(pStr, ']');
if( pStr->bErr ){
if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
assert( pStr->bStatic );
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
pStr->nUsed--;
}
}else{
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
}
sqlite3_result_subtype(ctx, P21_SUBTYPE);
}
static void p21ArrayValue(sqlite3_context *ctx){
p21ArrayCompute(ctx, 0);
}
static void p21ArrayFinal(sqlite3_context *ctx){
p21ArrayCompute(ctx, 1);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
/*
** This method works for both p21_group_array() and p21_group_object().
** It works by removing the first element of the group by searching forward
** to the first comma (",") that is not within a string and deleting all
** text through that comma.
*/
static void p21GroupInverse(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
unsigned int i;
int inStr = 0;
int nNest = 0;
char *z;
char c;
P21String *pStr;
UNUSED_PARAM(argc);
UNUSED_PARAM(argv);
pStr = (P21String*)sqlite3_aggregate_context(ctx, 0);
#ifdef NEVER
/* pStr is always non-NULL since p21ArrayStep() or p21ObjectStep() will
** always have been called to initalize it */
if( NEVER(!pStr) ) return;
#endif
z = pStr->zBuf;
for(i=1; (c = z[i])!=',' || inStr || nNest; i++){
if( i>=pStr->nUsed ){
pStr->nUsed = 1;
return;
}
if( c=='"' ){
inStr = !inStr;
}else if( c=='\\' ){
i++;
}else if( !inStr ){
if( c=='{' || c=='[' ) nNest++;
if( c=='}' || c==']' ) nNest--;
}
}
pStr->nUsed -= i;
memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1);
}
#else
# define p21GroupInverse 0
#endif
/*
** p21_group_obj(NAME,VALUE)
**
** Return a P21 object composed of all names and values in the aggregate.
*/
static void p21ObjectStep(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
P21String *pStr;
const char *z;
u32 n;
UNUSED_PARAM(argc);
pStr = (P21String*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
p21Init(pStr, ctx);
p21AppendChar(pStr, '{');
}else if( pStr->nUsed>1 ){
p21AppendChar(pStr, ',');
pStr->pCtx = ctx;
}
z = (const char*)sqlite3_value_text(argv[0]);
n = (u32)sqlite3_value_bytes(argv[0]);
p21AppendString(pStr, z, n);
p21AppendChar(pStr, ':');
p21AppendValue(pStr, argv[1]);
}
}
static void p21ObjectCompute(sqlite3_context *ctx, int isFinal){
P21String *pStr;
pStr = (P21String*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
p21AppendChar(pStr, '}');
if( pStr->bErr ){
if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
assert( pStr->bStatic );
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
pStr->nUsed--;
}
}else{
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
}
sqlite3_result_subtype(ctx, P21_SUBTYPE);
}
static void p21ObjectValue(sqlite3_context *ctx){
p21ObjectCompute(ctx, 0);
}
static void p21ObjectFinal(sqlite3_context *ctx){
p21ObjectCompute(ctx, 1);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/****************************************************************************
** The p21_each virtual table
****************************************************************************/
typedef struct P21EachCursor P21EachCursor;
struct P21EachCursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
u32 iBegin; /* The first node of the scan */
u32 i; /* Index in sParse.aNode[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
u8 eType; /* Type of top-level element */
char *zP21; /* Input P21 */
char *zRoot; /* Path by which to filter zP21 */
P21Parse sParse; /* Parse of the input P21 */
};
/* Constructor for the p21_each virtual table */
static int p21EachConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3_vtab *pNew;
int rc;
/* Column numbers */
#define PEACH_KEY 0
#define PEACH_VALUE 1
#define PEACH_TYPE 2
#define PEACH_ATOM 3
#define PEACH_ID 4
#define PEACH_PARENT 5
#define PEACH_FULLKEY 6
#define PEACH_PATH 7
/* The xBestIndex method assumes that the P21 and ROOT columns are
** the last two columns in the table. Should this ever changes, be
** sure to update the xBestIndex method. */
#define PEACH_P21 8
#define PEACH_ROOT 9
UNUSED_PARAM(pzErr);
UNUSED_PARAM(argv);
UNUSED_PARAM(argc);
UNUSED_PARAM(pAux);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
"p21 HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
/* destructor for p21_each virtual table */
static int p21EachDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/* constructor for a P21EachCursor object for p21_each(). */
static int p21EachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
P21EachCursor *pCur;
UNUSED_PARAM(p);
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/* Reset a P21EachCursor back to its original state. Free any memory
** held. */
static void p21EachCursorReset(P21EachCursor *p){
sqlite3_free(p->zP21);
sqlite3_free(p->zRoot);
p21ParseReset(&p->sParse);
p->iRowid = 0;
p->i = 0;
p->iEnd = 0;
p->eType = 0;
p->zP21 = 0;
p->zRoot = 0;
}
/* Destructor for a p21EachCursor object */
static int p21EachClose(sqlite3_vtab_cursor *cur){
P21EachCursor *p = (P21EachCursor*)cur;
p21EachCursorReset(p);
sqlite3_free(cur);
return SQLITE_OK;
}
/* Return TRUE if the p21EachCursor object has been advanced off the end
** of the P21 object */
static int p21EachEof(sqlite3_vtab_cursor *cur){
P21EachCursor *p = (P21EachCursor*)cur;
return p->i >= p->iEnd;
}
/* Advance the cursor to the next element for p21_tree() */
static int p21EachNext(sqlite3_vtab_cursor *cur){
P21EachCursor *p = (P21EachCursor*)cur;
switch( p->eType ){
case P21_RECORD:
case P21_LIST: {
p->i += p21NodeSize(&p->sParse.aNode[p->i]);
p->iRowid++;
break;
}
default: {
p->i = p->iEnd;
break;
}
}
return SQLITE_OK;
}
/* Append the name of the path for element i to pStr
*/
static void p21EachComputePath(
P21EachCursor *p, /* The cursor */
P21String *pStr, /* Write the path here */
u32 i /* Path to this element */
){
P21Node *pNode, *pUp;
u32 iUp;
if( i==0 ){
p21AppendChar(pStr, '$');
return;
}
iUp = p->sParse.aUp[i];
p21EachComputePath(p, pStr, iUp);
pNode = &p->sParse.aNode[i];
pUp = &p->sParse.aNode[iUp];
if( pUp->eType==P21_LIST ){
p21Printf(30, pStr, "[%d]", pUp->u.iKey);
}else{
assert( pUp->eType==P21_RECORD );
if( (pNode->jnFlags & PNODE_LABEL)==0 ) pNode--;
assert( pNode->eType==P21_STRING );
assert( pNode->jnFlags & PNODE_LABEL );
p21Printf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
}
}
/* Return the value of a column */
static int p21EachColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
P21EachCursor *p = (P21EachCursor*)cur;
P21Node *pThis = &p->sParse.aNode[p->i];
switch( i ){
case PEACH_KEY: {
if( p->i==0 ) break;
if( p->eType==P21_RECORD ){
p21Return(pThis, ctx, 0);
}else if( p->eType==P21_LIST ){
u32 iKey;
iKey = p->iRowid;
sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
}
break;
}
case PEACH_VALUE: {
if( pThis->jnFlags & PNODE_LABEL ) pThis++;
p21Return(pThis, ctx, 0);
break;
}
case PEACH_TYPE: {
if( pThis->jnFlags & PNODE_LABEL ) pThis++;
sqlite3_result_text(ctx, p21Type[pThis->eType], -1, SQLITE_STATIC);
break;
}
case PEACH_ATOM: {
if( pThis->jnFlags & PNODE_LABEL ) pThis++;
if( pThis->eType>=P21_LIST ) break;
p21Return(pThis, ctx, 0);
break;
}
case PEACH_ID: {
sqlite3_result_int64(ctx,
(sqlite3_int64)p->i + ((pThis->jnFlags & PNODE_LABEL)!=0));
break;
}
case PEACH_FULLKEY: {
P21String x;
p21Init(&x, ctx);
if( p->zRoot ){
p21AppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
}else{
p21AppendChar(&x, '$');
}
if( p->eType==P21_LIST ){
p21Printf(30, &x, "[%d]", p->iRowid);
}else if( p->eType==P21_RECORD ){
p21Printf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
}
p21Result(&x);
break;
}
case PEACH_PATH:
default: {
const char *zRoot = p->zRoot;
if( zRoot==0 ) zRoot = "$";
sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
break;
}
case PEACH_P21: {
assert( i==PEACH_P21 );
sqlite3_result_text(ctx, p->sParse.zP21, -1, SQLITE_STATIC);
break;
}
}
return SQLITE_OK;
}
/* Return the current rowid value */
static int p21EachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
P21EachCursor *p = (P21EachCursor*)cur;
*pRowid = p->iRowid;
return SQLITE_OK;
}
/* The query strategy is to look for an equality constraint on the p21
** column. Without such a constraint, the table cannot operate. idxNum is
** 1 if the constraint is found, 3 if the constraint and zRoot are found,
** and 0 otherwise.
*/
static int p21EachBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop counter or computed array index */
int aIdx[2]; /* Index of constraints for P21 and ROOT */
int unusableMask = 0; /* Mask of unusable P21 and ROOT constraints */
int idxMask = 0; /* Mask of usable == constraints P21 and ROOT */
const struct sqlite3_index_constraint *pConstraint;
/* This implementation assumes that P21 and ROOT are the last two
** columns in the table */
assert( PEACH_ROOT == PEACH_P21+1 );
UNUSED_PARAM(tab);
aIdx[0] = aIdx[1] = -1;
pConstraint = pIdxInfo->aConstraint;
for(i=0; inConstraint; i++, pConstraint++){
int iCol;
int iMask;
if( pConstraint->iColumn < PEACH_P21 ) continue;
iCol = pConstraint->iColumn - PEACH_P21;
assert( iCol==0 || iCol==1 );
iMask = 1 << iCol;
if( pConstraint->usable==0 ){
unusableMask |= iMask;
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
aIdx[iCol] = i;
idxMask |= iMask;
}
}
if( (unusableMask & ~idxMask)!=0 ){
/* If there are any unusable constraints on P21 or ROOT, then reject
** this entire plan */
return SQLITE_CONSTRAINT;
}
if( aIdx[0]<0 ){
/* No P21 input. Leave estimatedCost at the huge value that it was
** initialized to to discourage the query planner from selecting this
** plan. */
pIdxInfo->idxNum = 0;
}else{
pIdxInfo->estimatedCost = 1.0;
i = aIdx[0];
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
if( aIdx[1]<0 ){
pIdxInfo->idxNum = 1; /* Only P21 supplied. Plan 1 */
}else{
i = aIdx[1];
pIdxInfo->aConstraintUsage[i].argvIndex = 2;
pIdxInfo->aConstraintUsage[i].omit = 1;
pIdxInfo->idxNum = 3; /* Both P21 and ROOT are supplied. Plan 3 */
}
}
return SQLITE_OK;
}
/* Start a search on a new P21 string */
static int p21EachFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
P21EachCursor *p = (P21EachCursor*)cur;
const char *z;
const char *zRoot = 0;
sqlite3_int64 n;
UNUSED_PARAM(idxStr);
UNUSED_PARAM(argc);
p21EachCursorReset(p);
if( idxNum==0 ) return SQLITE_OK;
z = (const char*)sqlite3_value_text(argv[0]);
if( z==0 ) return SQLITE_OK;
n = sqlite3_value_bytes(argv[0]);
p->zP21 = sqlite3_malloc64( n+1 );
if( p->zP21==0 ) return SQLITE_NOMEM;
memcpy(p->zP21, z, (size_t)n+1);
if( p21Parse(&p->sParse, 0, p->zP21) ){
int rc = SQLITE_NOMEM;
if( p->sParse.oom==0 ){
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = sqlite3_mprintf("malformed P21");
if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
}
p21EachCursorReset(p);
return rc;
}else{
P21Node *pNode = 0;
if( idxNum==3 ){
const char *zErr = 0;
zRoot = (const char*)sqlite3_value_text(argv[1]);
if( zRoot==0 ) return SQLITE_OK;
n = sqlite3_value_bytes(argv[1]);
p->zRoot = sqlite3_malloc64( n+1 );
if( p->zRoot==0 ) return SQLITE_NOMEM;
memcpy(p->zRoot, zRoot, (size_t)n+1);
if( zRoot[0]!='$' ){
zErr = zRoot;
}else{
pNode = p21LookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
}
if( zErr ){
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = p21PathSyntaxError(zErr);
p21EachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}else if( pNode==0 ){
return SQLITE_OK;
}
}else{
pNode = p->sParse.aNode;
}
p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
p->eType = pNode->eType;
if( p->eType>=P21_LIST ){
pNode->u.iKey = 0;
p->iEnd = p->i + pNode->n + 1;
p->i++;
}else{
p->iEnd = p->i+1;
}
}
return SQLITE_OK;
}
/* The methods of the p21_each virtual table */
static sqlite3_module p21EachModule = {
0, /* iVersion */
0, /* xCreate */
p21EachConnect, /* xConnect */
p21EachBestIndex, /* xBestIndex */
p21EachDisconnect, /* xDisconnect */
0, /* xDestroy */
p21EachOpenEach, /* xOpen - open a cursor */
p21EachClose, /* xClose - close a cursor */
p21EachFilter, /* xFilter - configure scan constraints */
p21EachNext, /* xNext - advance a cursor */
p21EachEof, /* xEof - check for end of scan */
p21EachColumn, /* xColumn - read data */
p21EachRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/****************************************************************************
** The following routines are the only publically visible identifiers in this
** file. Call the following routines in order to register the various SQL
** functions and the virtual table implemented by this file.
****************************************************************************/
int sqlite3P21sqlInit(sqlite3 *db){
int rc = SQLITE_OK;
unsigned int i;
static const struct {
const char *zName;
int nArg;
int flag;
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "p21", 1, 0, p21RemoveFunc },
{ "p21_array", -1, 0, p21ArrayFunc },
{ "p21_array_length", 1, 0, p21ArrayLengthFunc },
{ "p21_array_length", 2, 0, p21ArrayLengthFunc },
{ "p21_extract", -1, 0, p21ExtractFunc },
{ "p21_insert", -1, 0, p21SetFunc },
{ "p21_object", -1, 0, p21ObjectFunc },
#if 0
{ "p21_patch", 2, 0, p21PatchFunc },
#endif
{ "p21_quote", 1, 0, p21QuoteFunc },
{ "p21_remove", -1, 0, p21RemoveFunc },
{ "p21_replace", -1, 0, p21ReplaceFunc },
{ "p21_set", -1, 1, p21SetFunc },
{ "p21_type", 1, 0, p21TypeFunc },
{ "p21_type", 2, 0, p21TypeFunc },
{ "p21_valid", 1, 0, p21ValidFunc },
#if SQLITE_DEBUG
/* DEBUG and TESTING functions */
{ "p21_parse", 1, 0, p21ParseFunc },
{ "p21_test1", 1, 0, p21Test1Func },
#endif
};
static const struct {
const char *zName;
int nArg;
void (*xStep)(sqlite3_context*,int,sqlite3_value**);
void (*xFinal)(sqlite3_context*);
void (*xValue)(sqlite3_context*);
} aAgg[] = {
{ "p21_group_array", 1,
p21ArrayStep, p21ArrayFinal, p21ArrayValue },
{ "p21_group_object", 2,
p21ObjectStep, p21ObjectFinal, p21ObjectValue },
};
#ifndef SQLITE_OMIT_VIRTUALTABLE
static const struct {
const char *zName;
sqlite3_module *pModule;
} aMod[] = {
{ "p21_each", &p21EachModule },
};
#endif
static const int enc =
SQLITE_UTF8 |
SQLITE_DETERMINISTIC |
SQLITE_INNOCUOUS;
for(i=0; i