Commit 55cb3d8b authored by Olivier Bertrand's avatar Olivier Bertrand

Add new json UDFs and make possible to use a json file name as json item.

  modified:   storage/connect/json.cpp
  modified:   storage/connect/json.h
  modified:   storage/connect/jsonudf.cpp
  modified:   storage/connect/mysql-test/connect/r/json_udf.result
  modified:   storage/connect/mysql-test/connect/t/json.test
  modified:   storage/connect/tabjson.cpp

Fix wrong calculation of Estimated Length when the table has virtual or special columns
  modified:   storage/connect/reldef.h
  modified:   storage/connect/tabdos.cpp

Fix wrong handling of null values in ODBCCOL::ReadColumn
  modified:   storage/connect/tabodbc.cpp

Fix crash when SetValue_char is called with a negative length value.
This can happen in odbconn.cpp when SQLFetch returns SQL_NO_TOTAL (-4) as length.
  modified:   storage/connect/odbconn.cpp
  modified:   storage/connect/value.cpp
parent 3b040a06
/*************** json CPP Declares Source Code File (.H) ***************/
/* Name: json.cpp Version 1.1 */
/* Name: json.cpp Version 1.2 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
......@@ -31,6 +31,7 @@
/***********************************************************************/
/* Parse a json string. */
/* Note: when pretty is not known, the caller set pretty to 3. */
/***********************************************************************/
PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
{
......@@ -62,16 +63,24 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
switch (s[i]) {
case '[':
if (jsp) {
if (pretty < 3) {
strcpy(g->Message, "More than one item in file");
goto err;
} else
goto tryit;
} else if (!(jsp = ParseArray(g, ++i, src)))
goto err;
break;
case '{':
if (jsp) {
if (pretty < 3) {
strcpy(g->Message, "More than one item in file");
goto err;
} else
goto tryit;
} else if (!(jsp = ParseObject(g, ++i, src)))
goto err;
......@@ -91,11 +100,6 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty);
goto err;
case '"':
if (!(jsp = ParseValue(g, i, src)))
goto err;
break;
case '(':
b = true;
break;
......@@ -106,9 +110,10 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
} // endif b
default:
sprintf(g->Message, "Bad '%c' character near %.*s",
s[i], ARGS);
if (!(jsp = ParseValue(g, i, src)))
goto err;
break;
}; // endswitch s[i]
if (!jsp)
......@@ -117,7 +122,13 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
g->jump_level--;
return jsp;
err:
tryit:
i = 0;
jsp = ParseArray(g, i, src);
g->jump_level--;
return jsp;
err:
g->jump_level--;
return NULL;
} // end of ParseJson
......@@ -130,6 +141,7 @@ PJAR ParseArray(PGLOBAL g, int& i, STRG& src)
char *s = src.str;
int len = src.len;
int level = 0;
bool pty = (!i);
PJAR jarp = new(g) JARRAY;
PJVAL jvp = NULL;
......@@ -160,16 +172,21 @@ PJAR ParseArray(PGLOBAL g, int& i, STRG& src)
if (level == 2) {
sprintf(g->Message, "Unexpected value near %.*s", ARGS);
return NULL;
} else if ((jvp = ParseValue(g, i, src))) {
} else if ((jvp = ParseValue(g, i, src)))
jarp->AddValue(g, jvp);
level = 2;
} else
else
return NULL;
level = 2;
level = (pty) ? 1 : 2;
break;
}; // endswitch s[i]
if (pty) {
// Case of Pretty == 0
jarp->InitArray(g);
return jarp;
} // endif pty
strcpy(g->Message, "Unexpected EOF in array");
return NULL;
} // end of ParseArray
......@@ -501,29 +518,38 @@ PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src)
/***********************************************************************/
/* Serialize a JSON tree: */
/***********************************************************************/
PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty)
PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty)
{
bool b = false, err = true;
JOUT *jp;
FILE *fs = NULL;
g->Message[0] = 0;
if (!jsp) {
strcpy(g->Message, "Null json tree");
return NULL;
} else if (!fs) {
} else if (!fn) {
// Serialize to a string
jp = new(g) JOUTSTR(g);
b = pretty == 1;
} else if (pretty == 2) {
} else {
if (!(fs = fopen(fn, "wb"))) {
sprintf(g->Message, MSG(OPEN_MODE_ERROR),
"w", (int)errno, fn);
strcat(strcat(g->Message, ": "), strerror(errno));
return g->Message;
} else if (pretty >= 2) {
// Serialize to a pretty file
jp = new(g) JOUTPRT(g, fs);
jp = new(g)JOUTPRT(g, fs);
} else {
// Serialize to a flat file
jp = new(g) JOUTFILE(g, fs);
jp = new(g)JOUTFILE(g, fs);
b = pretty == 1;
} // endif's
}
switch (jsp->GetType()) {
case TYPE_JAR:
err = SerializeArray(jp, (PJAR)jsp, b);
......@@ -923,6 +949,22 @@ void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key)
} // end of SetValue
/***********************************************************************/
/* Delete a value corresponding to the given key. */
/***********************************************************************/
void JOBJECT::DeleteKey(PSZ key)
{
PJPR jp, *pjp = &First;
for (jp = First; jp; jp = jp->Next)
if (!strcmp(jp->Key, key)) {
*pjp = jp->Next;
break;
} else
pjp = &jp->Next;
} // end of DeleteKey
/***********************************************************************/
/* True if void or if all members are nulls. */
/***********************************************************************/
......@@ -943,23 +985,25 @@ bool JOBJECT::IsNull(void)
void JARRAY::InitArray(PGLOBAL g)
{
int i;
PJVAL jvp;
PJVAL jvp, *pjvp = &First;
for (Size = 0, jvp = First; jvp; jvp = jvp->Next)
if (!jvp->Del)
Size++;
if (!Size) {
return;
} else if (Size > Alloc) {
if (Size > Alloc) {
// No need to realloc after deleting values
Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL));
Alloc = Size;
} // endif Size
for (i = 0, jvp = First; jvp; jvp = jvp->Next)
if (!jvp->Del)
if (!jvp->Del) {
Mvals[i++] = jvp;
pjvp = &jvp->Next;
Last = jvp;
} else
*pjvp = jvp->Next;
} // end of InitArray
......@@ -975,31 +1019,45 @@ PJVAL JARRAY::GetValue(int i)
} // end of GetValue
/***********************************************************************/
/* Add a Value to the Arrays Value list. */
/* Add a Value to the Array Value list. */
/***********************************************************************/
PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp)
PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp, int *x)
{
if (!jvp)
jvp = new(g) JVALUE;
if (x) {
int i = 0, n = *x;
PJVAL jp, *jpp = &First;
for (jp = First; jp && i < n; i++, jp = *(jpp = &jp->Next));
(*jpp) = jvp;
if (!(jvp->Next = jp))
Last = jvp;
} else {
if (Last)
Last->Next = jvp;
else
First = jvp;
Last = jvp;
} // endif x
return jvp;
} // end of AddValue
/***********************************************************************/
/* Add a Value to the Arrays Value list. */
/* Set the nth Value of the Array Value list. */
/***********************************************************************/
bool JARRAY::SetValue(PGLOBAL g, PJVAL jvp, int n)
{
int i = 0;
PJVAL jp, *jpp = &First;
for (i = 0, jp = First; i < n; i++, jp = *(jpp = &jp->Next))
for (jp = First; i < n; i++, jp = *(jpp = &jp->Next))
if (!jp)
*jpp = jp = new(g) JVALUE;
......@@ -1092,6 +1150,14 @@ int JVALUE::GetInteger(void)
return (Value) ? Value->GetIntValue() : 0;
} // end of GetInteger
/***********************************************************************/
/* Return the Value's Big integer value. */
/***********************************************************************/
long long JVALUE::GetBigint(void)
{
return (Value) ? Value->GetBigintValue() : 0;
} // end of GetBigint
/***********************************************************************/
/* Return the Value's Double value. */
/***********************************************************************/
......@@ -1134,7 +1200,15 @@ PSZ JVALUE::GetText(PGLOBAL g, PSZ text)
void JVALUE::SetInteger(PGLOBAL g, int n)
{
Value = AllocateValue(g, &n, TYPE_INT);
} // end of AddInteger
} // end of SetInteger
/***********************************************************************/
/* Set the Value's value as the given big integer. */
/***********************************************************************/
void JVALUE::SetBigint(PGLOBAL g, long long ll)
{
Value = AllocateValue(g, &ll, TYPE_BIGINT);
} // end of SetBigint
/***********************************************************************/
/* Set the Value's value as the given DOUBLE. */
......@@ -1142,7 +1216,7 @@ void JVALUE::SetInteger(PGLOBAL g, int n)
void JVALUE::SetFloat(PGLOBAL g, double f)
{
Value = AllocateValue(g, &f, TYPE_DOUBLE, 6);
} // end of AddFloat
} // end of SetFloat
/***********************************************************************/
/* Set the Value's value as the given string. */
......@@ -1150,7 +1224,7 @@ void JVALUE::SetFloat(PGLOBAL g, double f)
void JVALUE::SetString(PGLOBAL g, PSZ s)
{
Value = AllocateValue(g, s, TYPE_STRING);
} // end of AddFloat
} // end of SetString
/***********************************************************************/
/* True when its JSON or normal value is null. */
......
/**************** json H Declares Source Code File (.H) ****************/
/* Name: json.h Version 1.1 */
/* Name: json.h Version 1.2 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
......@@ -16,6 +16,7 @@
enum JTYP {TYPE_STRG = 1,
TYPE_DBL = 2,
TYPE_BOOL = 4,
TYPE_BINT = 5,
TYPE_INTG = 7,
TYPE_JSON = 12,
TYPE_JAR,
......@@ -40,13 +41,13 @@ typedef struct {
int len;
} STRG, *PSG;
PJSON ParseJson(PGLOBAL g, char *s, int n, int prty, bool *b = NULL);
PJSON ParseJson(PGLOBAL g, char *s, int n, int prty = 2, bool *b = NULL);
PJAR ParseArray(PGLOBAL g, int& i, STRG& src);
PJOB ParseObject(PGLOBAL g, int& i, STRG& src);
PJVAL ParseValue(PGLOBAL g, int& i, STRG& src);
char *ParseString(PGLOBAL g, int& i, STRG& src);
PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src);
PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty);
PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty);
bool SerializeArray(JOUT *js, PJAR jarp, bool b);
bool SerializeObject(JOUT *js, PJOB jobp);
bool SerializeValue(JOUT *js, PJVAL jvp);
......@@ -118,6 +119,7 @@ class JOUTPRT : public JOUTFILE {
/***********************************************************************/
class JPAIR : public BLOCK {
friend class JOBJECT;
friend class JSNX;
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
public:
......@@ -145,7 +147,7 @@ class JSON : public BLOCK {
virtual JTYP GetType(void) {return TYPE_JSON;}
virtual JTYP GetValType(void) {X return TYPE_JSON;}
virtual void InitArray(PGLOBAL g) {X}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL) {X return NULL;}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL, int *x = NULL) {X return NULL;}
virtual PJPR AddPair(PGLOBAL g, PSZ key) {X return NULL;}
virtual PJVAL GetValue(const char *key) {X return NULL;}
virtual PJOB GetObject(void) {return NULL;}
......@@ -165,6 +167,7 @@ class JSON : public BLOCK {
virtual void SetString(PGLOBAL g, PSZ s) {X}
virtual void SetInteger(PGLOBAL g, int n) {X}
virtual void SetFloat(PGLOBAL g, double f) {X}
virtual void DeleteKey(char *k) {X}
virtual bool DeleteValue(int i) {X return true;}
virtual bool IsNull(void) {X return true;}
......@@ -178,6 +181,7 @@ class JSON : public BLOCK {
class JOBJECT : public JSON {
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
friend class JSNX;
public:
JOBJECT(void) : JSON() {First = Last = NULL;}
......@@ -191,6 +195,7 @@ class JOBJECT : public JSON {
virtual PJVAL GetValue(const char* key);
virtual PSZ GetText(PGLOBAL g, PSZ text);
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key);
virtual void DeleteKey(char *k);
virtual bool IsNull(void);
protected:
......@@ -211,7 +216,7 @@ class JARRAY : public JSON {
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JAR;}
virtual PJAR GetArray(void) {return this;}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL);
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL, int *x = NULL);
virtual void InitArray(PGLOBAL g);
virtual PJVAL GetValue(int i);
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i);
......@@ -231,6 +236,7 @@ class JARRAY : public JSON {
/***********************************************************************/
class JVALUE : public JSON {
friend class JARRAY;
friend class JSNX;
friend PJVAL ParseValue(PGLOBAL, int&, STRG&);
friend bool SerializeValue(JOUT *, PJVAL);
public:
......@@ -251,6 +257,7 @@ class JVALUE : public JSON {
virtual PVAL GetValue(void) {return Value;}
virtual PJSON GetJson(void) {return (Jsp ? Jsp : this);}
virtual int GetInteger(void);
virtual long long GetBigint(void);
virtual double GetFloat(void);
virtual PSZ GetString(void);
virtual PSZ GetText(PGLOBAL g, PSZ text);
......@@ -258,6 +265,7 @@ class JVALUE : public JSON {
virtual void SetValue(PJSON jsp) {Jsp = jsp;}
virtual void SetString(PGLOBAL g, PSZ s);
virtual void SetInteger(PGLOBAL g, int n);
virtual void SetBigint(PGLOBAL g, longlong ll);
virtual void SetFloat(PGLOBAL g, double f);
virtual bool IsNull(void);
......
/************* jsonudf C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: jsonudf Version 1.0 */
/* PROGRAM NAME: jsonudf Version 1.1 */
/* (C) Copyright to the author Olivier BERTRAND 2015 */
/* This program are the JSON User Defined Functions . */
/***********************************************************************/
......@@ -11,59 +11,909 @@
#include <mysqld.h>
#include <mysql.h>
#include <sql_error.h>
#include <stdio.h>
#include "global.h"
#include "plgdbsem.h"
#include "json.h"
#include "jsonudf.h"
#define MEMFIX 512
#define UDF_EXEC_ARGS \
UDF_INIT*, UDF_ARGS*, char*, unsigned long*, char*, char*
#define MEMFIX 4096
#define PUSH_WARNING(M) \
push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, M)
uint GetJsonGrpSize(void);
static int IsJson(UDF_ARGS *args, int i);
static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i);
extern "C" {
DllExport my_bool Json_Value_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Value(UDF_EXEC_ARGS);
DllExport void Json_Value_deinit(UDF_INIT*);
/* ------------------------------ JSNX ------------------------------- */
DllExport my_bool Json_Array_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Array(UDF_EXEC_ARGS);
DllExport void Json_Array_deinit(UDF_INIT*);
/***********************************************************************/
/* JSNX public constructor. */
/***********************************************************************/
JSNX::JSNX(PGLOBAL g, PJSON row, int type, int len, int prec)
{
Row = row;
Value = AllocateValue(g, type, len, prec);
MulVal = NULL;
Nodes = NULL;
Jp = NULL;
Jpath = NULL;
Buf_Type = type;
Long = len;
Prec = prec;
Nod = 0;
Xnod = -1;
B = 0;
Xpd = false;
Parsed = false;
} // end of JSNX constructor
/***********************************************************************/
/* SetJpath: set and parse the json path. */
/***********************************************************************/
my_bool JSNX::SetJpath(PGLOBAL g, char *path)
{
// Check Value was allocated
if (!Value)
return true;
Value->SetNullable(true);
Jpath = path;
// Parse the json path
return ParseJpath(g);
} // end of SetJpath
/***********************************************************************/
/* Check whether this object is expanded. */
/***********************************************************************/
my_bool JSNX::CheckExpand(PGLOBAL g, int i, PSZ nm, my_bool b)
{
#if 0
if ((Tjp->Xcol && nm && !strcmp(nm, Tjp->Xcol) &&
(Tjp->Xval < 0 || Tjp->Xval == i)) || Xpd) {
Xpd = true; // Expandable object
Nodes[i].Op = OP_EXP;
} else if (b) {
strcpy(g->Message, "Cannot expand more than one branch");
return true;
} // endif Xcol
#endif // 0
return false;
} // end of CheckExpand
/***********************************************************************/
/* Analyse array processing options. */
/***********************************************************************/
my_bool JSNX::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
{
int n = (int)strlen(p);
my_bool dg = true, b = false;
PJNODE jnp = &Nodes[i];
if (*p) {
if (p[--n] == ']') {
p[n--] = 0;
p++;
} else {
// Wrong array specification
sprintf(g->Message,
// "Invalid array specification %s for %s", p, Name);
"Invalid array specification %s", p);
return true;
} // endif p
} else
b = true;
// To check whether a numeric Rank was specified
for (int k = 0; dg && p[k]; k++)
dg = isdigit(p[k]) > 0;
if (!n) {
// Default specifications
if (CheckExpand(g, i, nm, false))
return true;
else if (jnp->Op != OP_EXP) {
if (b) {
// Return 1st value (B is the index base)
jnp->Rank = B;
jnp->Op = OP_EQ;
} else if (!Value->IsTypeNum()) {
jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING);
jnp->Op = OP_CNC;
} else
jnp->Op = OP_ADD;
} // endif OP
} else if (dg) {
// Return nth value
jnp->Rank = atoi(p) - B;
jnp->Op = OP_EQ;
} else if (n == 1) {
// Set the Op value;
switch (*p) {
case '+': jnp->Op = OP_ADD; break;
case '*': jnp->Op = OP_MULT; break;
case '>': jnp->Op = OP_MAX; break;
case '<': jnp->Op = OP_MIN; break;
case '!': jnp->Op = OP_SEP; break; // Average
case '#': jnp->Op = OP_NUM; break;
case 'x':
case 'X': // Expand this array
strcpy(g->Message, "Expand not supported by this function");
return true;
#if 0
if (!Tjp->Xcol && nm) {
Xpd = true;
jnp->Op = OP_EXP;
Tjp->Xval = i;
Tjp->Xcol = nm;
} else if (CheckExpand(g, i, nm, true))
return true;
break;
#endif // 0
default:
sprintf(g->Message,
// "Invalid function specification %c for %s", *p, Name);
"Invalid function specification %c", *p);
return true;
} // endswitch *p
} else if (*p == '"' && p[n - 1] == '"') {
// This is a concat specification
jnp->Op = OP_CNC;
if (n > 2) {
// Set concat intermediate string
p[n - 1] = 0;
jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
} // endif n
} else {
// sprintf(g->Message, "Wrong array specification for %s", Name);
strcpy(g->Message, "Wrong array specification");
return true;
} // endif's
// For calculated arrays, a local Value must be used
switch (jnp->Op) {
case OP_NUM:
jnp->Valp = AllocateValue(g, TYPE_INT);
break;
case OP_ADD:
case OP_MULT:
case OP_SEP:
if (!IsTypeChar(Buf_Type))
jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
else
jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);
break;
case OP_MIN:
case OP_MAX:
jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
break;
case OP_CNC:
if (IsTypeChar(Buf_Type))
jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
else
jnp->Valp = AllocateValue(g, TYPE_STRING, 512);
break;
default:
break;
} // endswitch Op
if (jnp->Valp)
MulVal = AllocateValue(g, jnp->Valp);
return false;
} // end of SetArrayOptions
/***********************************************************************/
/* Parse the eventual passed Jpath information. */
/* This information can be specified in the Fieldfmt column option */
/* when creating the table. It permits to indicate the position of */
/* the node corresponding to that column. */
/***********************************************************************/
my_bool JSNX::ParseJpath(PGLOBAL g)
{
char *p, *p2 = NULL, *pbuf = NULL;
int i;
my_bool mul = false;
if (Parsed)
return false; // Already done
//else if (InitValue(g))
// return true;
else if (!Jpath)
// Jpath = Name;
return true;
#if 0
if (To_Tdb->GetOrig()) {
// This is an updated column, get nodes from origin
for (PJCOL colp = (PJCOL)Tjp->GetColumns(); colp;
colp = (PJCOL)colp->GetNext())
if (!stricmp(Name, colp->GetName())) {
Nod = colp->Nod;
Nodes = colp->Nodes;
goto fin;
} // endif Name
sprintf(g->Message, "Cannot parse updated column %s", Name);
return true;
} // endif To_Orig
#endif // 0
pbuf = PlugDup(g, Jpath);
// The Jpath must be analyzed
for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++)
Nod++; // One path node found
Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE));
memset(Nodes, 0, (Nod)* sizeof(JNODE));
// Analyze the Jpath for this column
for (i = 0, p = pbuf; i < Nod; i++, p = (p2 ? p2 + 1 : p + strlen(p))) {
if ((p2 = strchr(p, ':')))
*p2 = 0;
// Jpath must be explicit
if (*p == 0 || *p == '[') {
// Analyse intermediate array processing
if (SetArrayOptions(g, p, i, Nodes[i-1].Key))
return true;
} else if (*p == '*') {
// Return JSON
Nodes[i].Op = OP_XX;
} else {
Nodes[i].Key = p;
Nodes[i].Op = OP_EXIST;
} // endif's
} // endfor i, p
//fin:
MulVal = AllocateValue(g, Value);
Parsed = true;
return false;
} // end of ParseJpath
/***********************************************************************/
/* MakeJson: Serialize the json item and set value to it. */
/***********************************************************************/
PVAL JSNX::MakeJson(PGLOBAL g, PJSON jsp)
{
if (Value->IsTypeNum()) {
strcpy(g->Message, "Cannot make Json for a numeric value");
Value->Reset();
} else
Value->SetValue_psz(Serialize(g, jsp, NULL, 0));
return Value;
} // end of MakeJson
/***********************************************************************/
/* SetValue: Set a value from a JVALUE contains. */
/***********************************************************************/
void JSNX::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
{
if (val) {
switch (val->GetValType()) {
case TYPE_STRG:
case TYPE_INTG:
case TYPE_BINT:
case TYPE_DBL:
vp->SetValue_pval(val->GetValue());
break;
case TYPE_BOOL:
if (vp->IsTypeNum())
vp->SetValue(val->GetInteger() ? 1 : 0);
else
vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));
break;
case TYPE_JAR:
SetJsonValue(g, vp, val->GetArray()->GetValue(0), n);
break;
case TYPE_JOB:
// if (!vp->IsTypeNum() || !Strict) {
vp->SetValue_psz(val->GetObject()->GetText(g, NULL));
break;
// } // endif Type
default:
vp->Reset();
} // endswitch Type
} else {
vp->SetNull(true);
vp->Reset();
} // endif val
} // end of SetJsonValue
/***********************************************************************/
/* GetJson: */
/***********************************************************************/
PJVAL JSNX::GetJson(PGLOBAL g)
{
return GetValue(g, Row, 0);
} // end of GetJson
/***********************************************************************/
/* ReadValue: */
/***********************************************************************/
void JSNX::ReadValue(PGLOBAL g)
{
//if (!Tjp->SameRow || Xnod >= Tjp->SameRow)
// Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0));
Value->SetValue_pval(GetColumnValue(g, Row, 0));
// Set null when applicable
//if (Nullable)
// Value->SetNull(Value->IsZero());
} // end of ReadValue
/***********************************************************************/
/* GetColumnValue: */
/***********************************************************************/
PVAL JSNX::GetColumnValue(PGLOBAL g, PJSON row, int i)
{
int n = Nod - 1;
//my_bool expd = false;
//PJAR arp;
PJVAL val = NULL;
#if 0
for (; i < Nod && row; i++) {
if (Nodes[i].Op == OP_NUM) {
Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1);
return(Value);
} else if (Nodes[i].Op == OP_XX) {
return MakeJson(g, row);
} else switch (row->GetType()) {
case TYPE_JOB:
if (!Nodes[i].Key) {
// Expected Array was not there
if (i < Nod-1)
continue;
else
val = new(g)JVALUE(row);
} else
val = ((PJOB)row)->GetValue(Nodes[i].Key);
break;
case TYPE_JAR:
arp = (PJAR)row;
if (!Nodes[i].Key) {
if (Nodes[i].Op == OP_EQ)
val = arp->GetValue(Nodes[i].Rank);
else if (Nodes[i].Op == OP_EXP)
return ExpandArray(g, arp, i);
else
return CalculateArray(g, arp, i);
} else if (i < Nod-1) {
strcpy(g->Message, "Unexpected array");
val = NULL; // Not an expected array
} else
val = arp->GetValue(0);
break;
case TYPE_JVAL:
val = (PJVAL)row;
break;
default:
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
val = NULL;
} // endswitch Type
if (i < Nod-1)
row = (val) ? val->GetJson() : NULL;
} // endfor i
#endif // 0
val = GetValue(g, row, i);
SetJsonValue(g, Value, val, n);
return Value;
} // end of GetColumnValue
/***********************************************************************/
/* GetValue: */
/***********************************************************************/
PJVAL JSNX::GetValue(PGLOBAL g, PJSON row, int i)
{
//int n = Nod - 1;
my_bool expd = false;
PJAR arp;
PJVAL val = NULL;
for (; i < Nod && row; i++) {
if (Nodes[i].Op == OP_NUM) {
Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1);
val = new(g) JVALUE(g, Value);
return val;
} else if (Nodes[i].Op == OP_XX) {
return new(g) JVALUE(g, MakeJson(g, row));
} else switch (row->GetType()) {
case TYPE_JOB:
if (!Nodes[i].Key) {
// Expected Array was not there
if (i < Nod-1)
continue;
else
val = new(g) JVALUE(row);
} else
val = ((PJOB)row)->GetValue(Nodes[i].Key);
break;
case TYPE_JAR:
arp = (PJAR)row;
if (!Nodes[i].Key) {
if (Nodes[i].Op == OP_EQ)
val = arp->GetValue(Nodes[i].Rank);
else if (Nodes[i].Op == OP_EXP)
return (PJVAL)ExpandArray(g, arp, i);
else
return new(g) JVALUE(g, CalculateArray(g, arp, i));
} else if (i < Nod-1) {
strcpy(g->Message, "Unexpected array");
val = NULL; // Not an expected array
} else
val = arp->GetValue(0);
break;
case TYPE_JVAL:
val = (PJVAL)row;
break;
default:
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
val = NULL;
} // endswitch Type
if (i < Nod-1)
row = (val) ? val->GetJson() : NULL;
} // endfor i
// SetJsonValue(g, Value, val, n);
return val;
} // end of GetValue
/***********************************************************************/
/* ExpandArray: */
/***********************************************************************/
PVAL JSNX::ExpandArray(PGLOBAL g, PJAR arp, int n)
{
#if 0
int ars;
PJVAL jvp;
JVALUE jval;
ars = MY_MIN(Tjp->Limit, arp->size());
if (!(jvp = arp->GetValue((Nodes[n].Rx = Nodes[n].Nx)))) {
strcpy(g->Message, "Logical error expanding array");
longjmp(g->jumper[g->jump_level], 666);
} // endif jvp
if (n < Nod - 1 && jvp->GetJson()) {
jval.SetValue(GetColumnValue(g, jvp->GetJson(), n + 1));
jvp = &jval;
} // endif n
if (n >= Tjp->NextSame) {
if (++Nodes[n].Nx == ars) {
Nodes[n].Nx = 0;
Xnod = 0;
} else
Xnod = n;
Tjp->NextSame = Xnod;
} // endif NextSame
SetJsonValue(g, Value, jvp, n);
return Value;
#endif // 0
strcpy(g->Message, "Expand cannot be done by this function");
return NULL;
} // end of ExpandArray
/***********************************************************************/
/* CalculateArray: */
/***********************************************************************/
PVAL JSNX::CalculateArray(PGLOBAL g, PJAR arp, int n)
{
//int i, ars, nv = 0, nextsame = Tjp->NextSame;
int i, ars, nv = 0, nextsame = 0;
my_bool err;
OPVAL op = Nodes[n].Op;
PVAL val[2], vp = Nodes[n].Valp;
PJVAL jvrp, jvp;
JVALUE jval;
vp->Reset();
//ars = MY_MIN(Tjp->Limit, arp->size());
ars = arp->size();
for (i = 0; i < ars; i++) {
jvrp = arp->GetValue(i);
// do {
if (n < Nod - 1 && jvrp->GetJson()) {
// Tjp->NextSame = nextsame;
jval.SetValue(GetColumnValue(g, jvrp->GetJson(), n + 1));
jvp = &jval;
} else
jvp = jvrp;
if (!nv++) {
SetJsonValue(g, vp, jvp, n);
continue;
} else
SetJsonValue(g, MulVal, jvp, n);
if (!MulVal->IsZero()) {
switch (op) {
case OP_CNC:
if (Nodes[n].CncVal) {
val[0] = Nodes[n].CncVal;
err = vp->Compute(g, val, 1, op);
} // endif CncVal
val[0] = MulVal;
err = vp->Compute(g, val, 1, op);
break;
// case OP_NUM:
case OP_SEP:
val[0] = Nodes[n].Valp;
val[1] = MulVal;
err = vp->Compute(g, val, 2, OP_ADD);
break;
default:
val[0] = Nodes[n].Valp;
val[1] = MulVal;
err = vp->Compute(g, val, 2, op);
} // endswitch Op
if (err)
vp->Reset();
} // endif Zero
// } while (Tjp->NextSame > nextsame);
} // endfor i
if (op == OP_SEP) {
// Calculate average
MulVal->SetValue(nv);
val[0] = vp;
val[1] = MulVal;
if (vp->Compute(g, val, 2, OP_DIV))
vp->Reset();
} // endif Op
//Tjp->NextSame = nextsame;
return vp;
} // end of CalculateArray
#if 0
/***********************************************************************/
/* GetRow: Get the object containing this column. */
/***********************************************************************/
PJSON JSNX::GetRow(PGLOBAL g)
{
PJVAL val = NULL;
PJAR arp;
//PJSON nwr, row = Tjp->Row;
PJSON nwr, row = Row;
for (int i = 0; i < Nod-1 && row; i++) {
if (Nodes[i+1].Op == OP_XX)
break;
else switch (row->GetType()) {
case TYPE_JOB:
if (!Nodes[i].Key)
// Expected Array was not there
continue;
val = ((PJOB)row)->GetValue(Nodes[i].Key);
break;
case TYPE_JAR:
if (!Nodes[i].Key) {
arp = (PJAR)row;
if (Nodes[i].Op == OP_EQ)
val = arp->GetValue(Nodes[i].Rank);
else
val = arp->GetValue(Nodes[i].Rx);
} else {
strcpy(g->Message, "Unexpected array");
val = NULL; // Not an expected array
} // endif Nodes
break;
case TYPE_JVAL:
val = (PJVAL)row;
break;
default:
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
val = NULL;
} // endswitch Type
if (val) {
row = val->GetJson();
} else {
// Construct missing objects
for (i++; row && i < Nod; i++) {
if (Nodes[i].Op == OP_XX)
break;
else if (!Nodes[i].Key)
// Construct intermediate array
nwr = new(g)JARRAY;
else
nwr = new(g)JOBJECT;
if (row->GetType() == TYPE_JOB) {
((PJOB)row)->SetValue(g, new(g)JVALUE(nwr), Nodes[i-1].Key);
} else if (row->GetType() == TYPE_JAR) {
((PJAR)row)->AddValue(g, new(g)JVALUE(nwr));
((PJAR)row)->InitArray(g);
} else {
strcpy(g->Message, "Wrong type when writing new row");
nwr = NULL;
} // endif's
row = nwr;
} // endfor i
break;
} // endelse
} // endfor i
return row;
} // end of GetRow
/***********************************************************************/
/* WriteColumn: */
/***********************************************************************/
void JSNX::WriteColumn(PGLOBAL g)
{
/*********************************************************************/
/* Check whether this node must be written. */
/*********************************************************************/
if (Value != To_Val)
Value->SetValue_pval(To_Val, FALSE); // Convert the updated value
/*********************************************************************/
/* On INSERT Null values are represented by no node. */
/*********************************************************************/
if (Value->IsNull() && Tjp->Mode == MODE_INSERT)
return;
char *s;
PJOB objp = NULL;
PJAR arp = NULL;
PJVAL jvp = NULL;
PJSON jsp, row = GetRow(g);
JTYP type = row->GetType();
switch (row->GetType()) {
case TYPE_JOB: objp = (PJOB)row; break;
case TYPE_JAR: arp = (PJAR)row; break;
case TYPE_JVAL: jvp = (PJVAL)row; break;
default: row = NULL; // ???????????????????????????
} // endswitch Type
if (row) switch (Buf_Type) {
case TYPE_STRING:
if (Nodes[Nod-1].Op == OP_XX) {
s = Value->GetCharValue();
if (!(jsp = ParseJson(g, s, (int)strlen(s)))) {
strcpy(g->Message, s);
longjmp(g->jumper[g->jump_level], 666);
} // endif jsp
if (arp) {
if (Nod > 1 && Nodes[Nod-2].Op == OP_EQ)
arp->SetValue(g, new(g)JVALUE(jsp), Nodes[Nod-2].Rank);
else
arp->AddValue(g, new(g)JVALUE(jsp));
arp->InitArray(g);
} else if (objp) {
if (Nod > 1 && Nodes[Nod-2].Key)
objp->SetValue(g, new(g)JVALUE(jsp), Nodes[Nod-2].Key);
} else if (jvp)
jvp->SetValue(jsp);
break;
} // endif Op
// Passthru
case TYPE_DATE:
case TYPE_INT:
case TYPE_DOUBLE:
if (arp) {
if (Nodes[Nod-1].Op == OP_EQ)
arp->SetValue(g, new(g)JVALUE(g, Value), Nodes[Nod-1].Rank);
else
arp->AddValue(g, new(g)JVALUE(g, Value));
arp->InitArray(g);
} else if (objp) {
if (Nodes[Nod-1].Key)
objp->SetValue(g, new(g)JVALUE(g, Value), Nodes[Nod-1].Key);
} else if (jvp)
jvp->SetValue(Value);
break;
default: // ??????????
sprintf(g->Message, "Invalid column type %d", Buf_Type);
} // endswitch Type
} // end of WriteColumn
#endif // 0
/***********************************************************************/
/* Locate a value in a JSON tree: */
/***********************************************************************/
PSZ JSNX::Locate(PGLOBAL g, PJSON jsp, char *what,
enum Item_result type, unsigned long len)
{
my_bool b = false, err = true;
g->Message[0] = 0;
if (!jsp) {
strcpy(g->Message, "Null json tree");
return NULL;
} else // Write to the path string
Jp = new(g)JOUTPATH(g, what, type, len);
switch (jsp->GetType()) {
case TYPE_JAR:
err = LocateArray((PJAR)jsp);
break;
case TYPE_JOB:
err = LocateObject((PJOB)jsp);
break;
case TYPE_JVAL:
err = LocateValue((PJVAL)jsp);
break;
default:
err = true;
} // endswitch Type
if (err) {
if (!g->Message[0])
strcpy(g->Message, "Invalid json tree");
} else if (Jp->Found) {
Jp->WriteChr('\0');
PlugSubAlloc(g, NULL, Jp->N);
return Jp->Strp;
} // endif's
return NULL;
} // end of Locate
/***********************************************************************/
/* Locate in a JSON Array. */
/***********************************************************************/
my_bool JSNX::LocateArray(PJAR jarp)
{
char s[16];
size_t m = Jp->N;
for (int i = 0; i < jarp->size() && !Jp->Found; i++) {
Jp->N = m;
sprintf(s, "[%d]", i + B);
if (Jp->WriteStr(s))
return true;
if (LocateValue(jarp->GetValue(i)))
return true;
} // endfor i
return false;
} // end of LocateArray
/***********************************************************************/
/* Locate in a JSON Object. */
/***********************************************************************/
my_bool JSNX::LocateObject(PJOB jobp)
{
size_t m = Jp->N;
for (PJPR pair = jobp->First; pair && !Jp->Found; pair = pair->Next) {
Jp->N = m;
DllExport my_bool Json_Array_Add_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Array_Add(UDF_EXEC_ARGS);
DllExport void Json_Array_Add_deinit(UDF_INIT*);
if (Jp->WriteStr(pair->Key))
return true;
if (LocateValue(pair->Val))
return true;
} // endfor i
return false;
} // end of LocateObject
/***********************************************************************/
/* Locate a JSON Value. */
/***********************************************************************/
my_bool JSNX::LocateValue(PJVAL jvp)
{
char *p, buf[32];
PJAR jap;
PJOB jop;
PVAL valp;
DllExport my_bool Json_Array_Delete_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Array_Delete(UDF_EXEC_ARGS);
DllExport void Json_Array_Delete_deinit(UDF_INIT*);
if ((jap = jvp->GetArray())) {
if (Jp->WriteChr(':'))
return true;
DllExport my_bool Json_Object_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Object(UDF_EXEC_ARGS);
DllExport void Json_Object_deinit(UDF_INIT*);
return LocateArray(jap);
} else if ((jop = jvp->GetObject())) {
if (Jp->WriteChr(':'))
return true;
DllExport my_bool Json_Object_Nonull_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Object_Nonull(UDF_EXEC_ARGS);
DllExport void Json_Object_Nonull_deinit(UDF_INIT*);
return LocateObject(jop);
} else if (!(valp = jvp->Value) || valp->IsNull())
return false;
else switch (Jp->Type) {
case STRING_RESULT:
p = valp->GetCharString(buf);
Jp->Found = (strlen(p) == Jp->Len &&
!strncmp(Jp->What, valp->GetCharString(buf), Jp->Len));
break;
case INT_RESULT:
Jp->Found = *(longlong*)Jp->What == valp->GetBigintValue();
break;
case DECIMAL_RESULT:
Jp->Found = atof(Jp->What) == valp->GetFloatValue();
break;
case REAL_RESULT:
Jp->Found = *(double*)Jp->What == valp->GetFloatValue();
break;
default:
sprintf(Jp->g->Message, "Invalid type %d", Buf_Type);
return true;
} // endswitch Type
DllExport my_bool Json_Array_Grp_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport void Json_Array_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *);
DllExport char *Json_Array_Grp(UDF_EXEC_ARGS);
DllExport void Json_Array_Grp_clear(UDF_INIT *, char *, char *);
DllExport void Json_Array_Grp_deinit(UDF_INIT*);
return false;
} // end of LocateValue
DllExport my_bool Json_Object_Grp_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport void Json_Object_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *);
DllExport char *Json_Object_Grp(UDF_EXEC_ARGS);
DllExport void Json_Object_Grp_clear(UDF_INIT *, char *, char *);
DllExport void Json_Object_Grp_deinit(UDF_INIT*);
} // extern "C"
/* ---------------------------- JSON UDF ----------------------------- */
/***********************************************************************/
/* Allocate and initialise the memory area. */
/***********************************************************************/
static my_bool JsonInit(UDF_INIT *initid, char *message,
static my_bool JsonInit(UDF_INIT *initid, char *message, my_bool mbn,
unsigned long reslen, unsigned long memlen)
{
PGLOBAL g = PlugInit(NULL, memlen);
......@@ -78,27 +928,112 @@ static my_bool JsonInit(UDF_INIT *initid, char *message,
} else
initid->ptr = (char*)g;
initid->maybe_null = false;
initid->maybe_null = mbn;
initid->const_item = false;
initid->max_length = reslen;
return false;
} // end of Json_Object_init
} // end of Json_init
/***********************************************************************/
/* Check if a path was specified and set jvp according to it. */
/***********************************************************************/
static my_bool CheckPath(PGLOBAL g, UDF_ARGS *args, PJSON top, PJVAL& jvp, int n)
{
for (uint i = n; i < args->arg_count; i++)
if (args->arg_type[i] == STRING_RESULT) {
// A path to a subset of the json tree is given
char *path = MakePSZ(g, args, i);
PJSNX jsx = new(g)JSNX(g, top, TYPE_STRING);
if (jsx->SetJpath(g, path))
return true;
if (!(jvp = jsx->GetJson(g))) {
sprintf(g->Message, "No sub-item at '%s'", path);
return true;
} // endif jvp
break;
} // endif type
return false;
} // end of CheckPath
/***********************************************************************/
/* Make the result according to the first argument type. */
/***********************************************************************/
static char *MakeResult(PGLOBAL g, UDF_ARGS *args, PJSON top, int n = 2)
{
char *str;
if (IsJson(args, 0) == 2) {
// Make the change in the json file
char *msg;
int pretty = 2;
for (uint i = n; i < args->arg_count; i++)
if (args->arg_type[i] == INT_RESULT) {
pretty = (int)*(longlong*)args->args[i];
break;
} // endif type
if ((msg = Serialize(g, top, MakePSZ(g, args, 0), pretty)))
PUSH_WARNING(msg);
str = NULL;
} else if (!(str = Serialize(g, top, NULL, 0)))
PUSH_WARNING(g->Message);
return str;
} // end of MakeResult
/***********************************************************************/
/* Returns true if the argument is a JSON string. */
/* Returns not 0 if the argument is a JSON item or file name. */
/***********************************************************************/
static my_bool IsJson(UDF_ARGS *args, int i)
static int IsJson(UDF_ARGS *args, int i)
{
return (args->arg_type[i] == STRING_RESULT &&
!strnicmp(args->attributes[i], "Json_", 5));
int n = 0;
if (!strnicmp(args->attributes[i], "Json_", 5))
n = 1; // arg is a json item
else if (args->arg_type[i] == STRING_RESULT &&
!strnicmp(args->attributes[i], "Jfile_", 6))
n = 2; // arg is a json file name
return n;
} // end of IsJson
/***********************************************************************/
/* GetFileLength: returns file size in number of bytes. */
/***********************************************************************/
static long GetFileLength(char *fn)
{
int h;
long len;
h= open(fn, _O_RDONLY);
if (h != -1) {
if ((len = _filelength(h)) < 0)
len = 0;
close(h);
} else
len = 0;
return len;
} // end of GetFileLength
/***********************************************************************/
/* Calculate the reslen and memlen needed by a function. */
/***********************************************************************/
static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
unsigned long& reslen, unsigned long& memlen)
{
unsigned long i, k;
char fn[_MAX_PATH];
unsigned long i, k, n;
long fl, j = -1;
reslen = args->arg_count + 2;
// Calculate the result max length
......@@ -112,7 +1047,14 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
switch (args->arg_type[i]) {
case STRING_RESULT:
if (IsJson(args, i))
if (IsJson(args, i) == 2 && args->args[i]) {
n = MY_MIN(args->lengths[i], sizeof(fn) - 1);
memcpy(fn, args->args[i], n);
fn[n] = 0;
j = i;
fl = GetFileLength(fn);
reslen += fl;
} else if (IsJson(args, i) == 1)
reslen += args->lengths[i];
else
reslen += (args->lengths[i] + 1) * 2; // Pessimistic !
......@@ -153,7 +1095,17 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
switch (args->arg_type[i]) {
case STRING_RESULT:
if (IsJson(args, i))
if (IsJson(args, i) == 2 && args->args[i]) {
if ((signed)i != j) {
n = MY_MIN(args->lengths[i], sizeof(fn) - 1);
memcpy(fn, args->args[i], n);
fn[n] = 0;
j = -1;
fl = GetFileLength(fn);
} // endif i
memlen += fl * 5;
} else if (IsJson(args, i) == 1)
memlen += args->lengths[i] * 5; // Estimate parse memory
memlen += sizeof(TYPVAL<PSZ>);
......@@ -178,12 +1130,47 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
return false;
} // end of CalcLen
/***********************************************************************/
/* Check if the calculated memory is enough. */
/***********************************************************************/
static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args,
uint n, my_bool obj)
{
unsigned long rl, ml;
for (uint i = 0; i < n; i++)
if (IsJson(args, i) == 2) {
if (CalcLen(args, obj, rl, ml))
return true;
else if (ml > g->Sarea_Size) {
free(g->Sarea);
if (!(g->Sarea = PlugAllocMem(g, ml))) {
char errmsg[256];
sprintf(errmsg, MSG(WORK_AREA), g->Message);
strcpy(g->Message, errmsg);
g->Sarea_Size = 0;
return true;
} else
g->Sarea_Size = ml;
initid->max_length = rl;
} // endif Size
break;
} // endif IsJson
PlugSubSet(g, g->Sarea, g->Sarea_Size);
return false;
} // end of CheckMemory
/***********************************************************************/
/* Make a zero terminated string from the passed argument. */
/***********************************************************************/
static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i)
{
if (args->args[i]) {
if (args->arg_count > (unsigned)i && args->args[i]) {
int n = args->lengths[i];
PSZ s = (PSZ)PlugSubAlloc(g, NULL, n + 1);
......@@ -200,8 +1187,9 @@ static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i)
/***********************************************************************/
static PSZ MakeKey(PGLOBAL g, UDF_ARGS *args, int i)
{
if (args->arg_count > (unsigned)i) {
int n = args->attribute_lengths[i];
bool b; // true if attribute is zero terminated
my_bool b; // true if attribute is zero terminated
PSZ p, s = args->attributes[i];
if (s && *s && (n || *s == '\'')) {
......@@ -230,24 +1218,70 @@ static PSZ MakeKey(PGLOBAL g, UDF_ARGS *args, int i)
} // endif s
return s;
} // endif count
return "Key";
} // end of MakeKey
/***********************************************************************/
/* Return a json file contains. */
/***********************************************************************/
static char *GetJsonFile(PGLOBAL g, char *fn)
{
char *str;
int h, n, len;
h= open(fn, _O_RDONLY, _O_TEXT);
if (h == -1) {
sprintf(g->Message, "Error %d opening %s", errno, fn);
return NULL;
} // endif h
if ((len = _filelength(h)) < 0) {
sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn);
close(h);
return NULL;
} // endif len
str = (char*)PlugSubAlloc(g, NULL, len + 1);
if ((n = read(h, str, len)) < 0) {
sprintf(g->Message, "Error %d reading %d bytes from %s", errno, len, fn);
return NULL;
} // endif n
str[n] = 0;
close(h);
return str;
} // end of GetJsonFile
/***********************************************************************/
/* Make a JSON value from the passed argument. */
/***********************************************************************/
static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i)
static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, uint i)
{
char *sap = (args->arg_count > (unsigned)i) ? args->args[i] : NULL;
char *sap = (args->arg_count > i) ? args->args[i] : NULL;
int n, len;
long long bigint;
PJSON jsp;
PJVAL jvp = new(g) JVALUE;
if (sap) switch (args->arg_type[i]) {
case STRING_RESULT:
if (args->lengths[i]) {
if (IsJson(args, i)) {
if (!(jsp = ParseJson(g, sap, args->lengths[i], 0)))
push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
g->Message);
if ((len = args->lengths[i])) {
sap = MakePSZ(g, args, i);
if ((n = IsJson(args, i))) {
if (n == 2) {
if (!(sap = GetJsonFile(g, sap)))
PUSH_WARNING(g->Message);
len = (sap) ? strlen(sap) : 0;
} // endif n
if (!(jsp = ParseJson(g, sap, len, 3)))
PUSH_WARNING(g->Message);
if (jsp && jsp->GetType() == TYPE_JVAL)
jvp = (PJVAL)jsp;
......@@ -255,13 +1289,19 @@ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i)
jvp->SetValue(jsp);
} else
jvp->SetString(g, MakePSZ(g, args, i));
jvp->SetString(g, sap);
} // endif str
} // endif len
break;
case INT_RESULT:
jvp->SetInteger(g, (int)*(long long*)sap);
bigint = *(long long*)sap;
if (bigint > INT_MAX32 || bigint < INT_MIN32)
jvp->SetFloat(g, (double)bigint);
else
jvp->SetInteger(g, (int)bigint);
break;
case REAL_RESULT:
jvp->SetFloat(g, *(double*)sap);
......@@ -292,22 +1332,24 @@ my_bool Json_Value_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else
CalcLen(args, false, reslen, memlen);
return JsonInit(initid, message, reslen, memlen);
return JsonInit(initid, message, false, reslen, memlen);
} // end of Json_Value_init
char *Json_Value(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
PJVAL jvp;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
jvp = MakeValue(g, args, 0);
if (!CheckMemory(g, initid, args, 1, false)) {
PJVAL jvp = MakeValue(g, args, 0);
if (!(str = Serialize(g, jvp, NULL, 0)))
str = strcpy(result, g->Message);
} else
str = strcpy(result, g->Message);
*res_length = strlen(str);
return str;
} // end of Json_Value
......@@ -325,7 +1367,7 @@ my_bool Json_Array_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
unsigned long reslen, memlen;
CalcLen(args, false, reslen, memlen);
return JsonInit(initid, message, reslen, memlen);
return JsonInit(initid, message, false,reslen, memlen);
} // end of Json_Array_init
char *Json_Array(UDF_INIT *initid, UDF_ARGS *args, char *result,
......@@ -336,8 +1378,8 @@ char *Json_Array(UDF_INIT *initid, UDF_ARGS *args, char *result,
PJAR arp;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
arp = new(g) JARRAY;
if (!CheckMemory(g, initid, args, args->arg_count, false)) {
arp = new(g)JARRAY;
for (i = 0; i < args->arg_count; i++)
arp->AddValue(g, MakeValue(g, args, i));
......@@ -347,6 +1389,9 @@ char *Json_Array(UDF_INIT *initid, UDF_ARGS *args, char *result,
if (!(str = Serialize(g, arp, NULL, 0)))
str = strcpy(result, g->Message);
} else
str = strcpy(result, g->Message);
*res_length = strlen(str);
return str;
} // end of Json_Array
......@@ -357,37 +1402,36 @@ void Json_Array_deinit(UDF_INIT* initid)
} // end of Json_Array_deinit
/***********************************************************************/
/* Add values to a Json array. */
/* Add one or several values to a Json array. */
/***********************************************************************/
my_bool Json_Array_Add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
my_bool Json_Array_Add_Values_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
if (args->arg_count < 2) {
strcpy(message, "Json_Value_Add must have at least 2 arguments");
strcpy(message, "Json_Array_Add must have at least 2 arguments");
return true;
} else if (!IsJson(args, 0)) {
strcpy(message, "Json_Value_Add first argument must be a json item");
} else if (IsJson(args, 0) != 1) {
strcpy(message, "Json_Array_Add first argument must be a json string");
return true;
} else
CalcLen(args, false, reslen, memlen);
return JsonInit(initid, message, reslen, memlen);
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Array_Add_init
char *Json_Array_Add(UDF_INIT *initid, UDF_ARGS *args, char *result,
char *Json_Array_Add_Values(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
PJVAL jvp;
PJAR arp;
char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
jvp = MakeValue(g, args, 0);
if (!CheckMemory(g, initid, args, args->arg_count, false)) {
PJAR arp;
PJVAL jvp = MakeValue(g, args, 0);
if (jvp->GetValType() != TYPE_JAR) {
arp = new(g) JARRAY;
arp = new(g)JARRAY;
arp->AddValue(g, jvp);
} else
arp = jvp->GetArray();
......@@ -396,11 +1440,84 @@ char *Json_Array_Add(UDF_INIT *initid, UDF_ARGS *args, char *result,
arp->AddValue(g, MakeValue(g, args, i));
arp->InitArray(g);
str = Serialize(g, arp, NULL, 0);
} // endif CheckMemory
if (!(str = Serialize(g, arp, NULL, 0)))
str = strcpy(result, g->Message);
if (!str) {
PUSH_WARNING(g->Message);
str = args->args[0];
} // endif str
*res_length = strlen(str);
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_Array_Add_Values
void Json_Array_Add_Values_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Array_Add_Values_deinit
/***********************************************************************/
/* Add one value to a Json array. */
/***********************************************************************/
my_bool Json_Array_Add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
if (args->arg_count < 2) {
strcpy(message, "Json_Array_Add must have at least 2 arguments");
return true;
} else if (!IsJson(args, 0)) {
strcpy(message, "Json_Array_Add first argument must be a json item");
return true;
} else
CalcLen(args, false, reslen, memlen);
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Array_Add_init
char *Json_Array_Add(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!CheckMemory(g, initid, args, 2, false)) {
int *x = NULL, n = 2;
PJSON top;
PJVAL jvp;
PJAR arp;
if (args->arg_count > 2) {
if (args->arg_type[2] == INT_RESULT) {
x = (int*)PlugSubAlloc(g, NULL, sizeof(int));
*x = (int)*(longlong*)args->args[2];
n = 3;
} else if (!args->args[2])
n = 3;
} // endif count
jvp = MakeValue(g, args, 0);
top = jvp->GetJson();
if (CheckPath(g, args, top, jvp, n))
PUSH_WARNING(g->Message);
else if (jvp && jvp->GetValType() == TYPE_JAR) {
arp = jvp->GetArray();
arp->AddValue(g, MakeValue(g, args, 1), x);
arp->InitArray(g);
str = MakeResult(g, args, top, n);
} else
PUSH_WARNING("First argument is not an array");
} // endif CheckMemory
// In case of error or file, return unchanged argument
if (!str)
str = args->args[0];
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_Array_Add
......@@ -416,52 +1533,51 @@ my_bool Json_Array_Delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
if (args->arg_count != 2) {
strcpy(message, "Json_Value_Delete must have 2 arguments");
if (args->arg_count < 2) {
strcpy(message, "Json_Array_Delete must have at lest 2 arguments");
return true;
} else if (!IsJson(args, 0)) {
strcpy(message, "Json_Value_Delete first argument must be a json item");
strcpy(message, "Json_Aray_Delete first argument must be a json item");
return true;
} else if (args->arg_type[1] != INT_RESULT) {
PUSH_WARNING("Json_Aray_Delete second argument is not an integer (index)");
return true;
} else
CalcLen(args, false, reslen, memlen);
return JsonInit(initid, message, reslen, memlen);
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Array_Delete_init
char *Json_Array_Delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
int n;
PJVAL jvp;
PJAR arp;
char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
jvp = MakeValue(g, args, 0);
if (!CheckMemory(g, initid, args, 1, false)) {
int n;
PJAR arp;
PJVAL jvp = MakeValue(g, args, 0);
PJSON top = jvp->GetJson();
if (jvp->GetValType() != TYPE_JAR) {
push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
"First argument is not an array");
str = args->args[0];
} else if (args->arg_type[1] != INT_RESULT) {
push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
"Second argument is not an integer");
str = args->args[0];
} else {
if (CheckPath(g, args, top, jvp, 2))
PUSH_WARNING(g->Message);
else if (jvp && jvp->GetValType() == TYPE_JAR) {
n = *(int*)args->args[1];
arp = jvp->GetArray();
arp->DeleteValue(n);
arp->InitArray(g);
str = MakeResult(g, args, top);
} else
PUSH_WARNING("First argument is not an array");
if (!(str = Serialize(g, arp, NULL, 0))) {
str = strcpy(result, g->Message);
push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, str);
} // endif str
} // endif CheckMemory
} // endif's
// In case of error or file, return unchanged argument
if (!str)
str = args->args[0];
*res_length = strlen(str);
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_Array_Delete
......@@ -478,77 +1594,194 @@ my_bool Json_Object_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
unsigned long reslen, memlen;
CalcLen(args, true, reslen, memlen);
return JsonInit(initid, message, reslen, memlen);
return JsonInit(initid, message, false, reslen, memlen);
} // end of Json_Object_init
char *Json_Object(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!CheckMemory(g, initid, args, args->arg_count, true)) {
PJOB objp = new(g)JOBJECT;
for (uint i = 0; i < args->arg_count; i++)
objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i));
str = Serialize(g, objp, NULL, 0);
} // endif CheckMemory
if (!str)
str = strcpy(result, g->Message);
*res_length = strlen(str);
return str;
} // end of Json_Object
void Json_Object_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Object_deinit
/***********************************************************************/
/* Make a Json Oject containing all not null parameters. */
/***********************************************************************/
my_bool Json_Object_Nonull_init(UDF_INIT *initid, UDF_ARGS *args,
char *message)
{
unsigned long reslen, memlen;
CalcLen(args, true, reslen, memlen);
return JsonInit(initid, message, false, reslen, memlen);
} // end of Json_Object_Nonull_init
char *Json_Object_Nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
uint i;
PJOB objp;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
objp = new(g) JOBJECT;
if (!CheckMemory(g, initid, args, args->arg_count, true)) {
PJVAL jvp;
PJOB objp = new(g)JOBJECT;
for (uint i = 0; i < args->arg_count; i++)
if (!(jvp = MakeValue(g, args, i))->IsNull())
objp->SetValue(g, jvp, MakeKey(g, args, i));
str = Serialize(g, objp, NULL, 0);
} // endif CheckMemory
if (!str)
str = strcpy(result, g->Message);
*res_length = strlen(str);
return str;
} // end of Json_Object_Nonull
void Json_Object_Nonull_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Object_nonull_deinit
/***********************************************************************/
/* Add or replace a value in a Json Object. */
/***********************************************************************/
my_bool Json_Object_Add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
if (args->arg_count < 2) {
strcpy(message, "Json_Object_Add must have at least 2 arguments");
return true;
} else if (!IsJson(args, 0)) {
strcpy(message, "Json_Object_Add first argument must be a json item");
return true;
} else
CalcLen(args, false, reslen, memlen);
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Object_Add_init
char *Json_Object_Add(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *key, *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!CheckMemory(g, initid, args, 2, false)) {
PJOB jobp;
PJVAL jvp = MakeValue(g, args, 0);
PJSON top = jvp->GetJson();
if (CheckPath(g, args, top, jvp, 2))
PUSH_WARNING(g->Message);
else if (jvp && jvp->GetValType() == TYPE_JOB) {
jobp = jvp->GetObject();
jvp = MakeValue(g, args, 1);
key = MakeKey(g, args, 1);
jobp->SetValue(g, jvp, key);
str = MakeResult(g, args, top);
} else
PUSH_WARNING("First argument is not an object");
for (i = 0; i < args->arg_count; i++)
objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i));
} // endif CheckMemory
if (!(str = Serialize(g, objp, NULL, 0)))
str = strcpy(result, g->Message);
// In case of error or file, return unchanged argument
if (!str)
str = args->args[0];
*res_length = strlen(str);
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_Object
} // end of Json_Object_Add
void Json_Object_deinit(UDF_INIT* initid)
void Json_Object_Add_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Object_deinit
} // end of Json_Object_Add_deinit
/***********************************************************************/
/* Make a Json Oject containing all not null parameters. */
/* Delete a value from a Json object. */
/***********************************************************************/
my_bool Json_Object_Nonull_init(UDF_INIT *initid, UDF_ARGS *args,
char *message)
my_bool Json_Object_Delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
CalcLen(args, true, reslen, memlen);
return JsonInit(initid, message, reslen, memlen);
} // end of Json_Object_Nonull_init
if (args->arg_count < 2) {
strcpy(message, "Json_Object_Delete must have 2 or 3 arguments");
return true;
} else if (!IsJson(args, 0)) {
strcpy(message, "Json_Object_Delete first argument must be a json item");
return true;
} else if (args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Json_Object_Delete second argument must be a key string");
return true;
} else
CalcLen(args, false, reslen, memlen);
char *Json_Object_Nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Object_Delete_init
char *Json_Object_Delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
uint i;
PJOB objp;
PJVAL jvp;
char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
objp = new(g) JOBJECT;
if (!CheckMemory(g, initid, args, 1, false)) {
char *key;
PJOB jobp;
PJVAL jvp = MakeValue(g, args, 0);
PJSON top = jvp->GetJson();
if (CheckPath(g, args, top, jvp, 2))
PUSH_WARNING(g->Message);
else if (jvp && jvp->GetValType() == TYPE_JOB) {
key = MakeKey(g, args, 1);
jobp = jvp->GetObject();
jobp->DeleteKey(key);
str = MakeResult(g, args, top);
} else
PUSH_WARNING("First argument is not an object");
for (i = 0; i < args->arg_count; i++)
if (!(jvp = MakeValue(g, args, i))->IsNull())
objp->SetValue(g, jvp, MakeKey(g, args, i));
} // endif CheckMemory
if (!(str = Serialize(g, objp, NULL, 0)))
str = strcpy(result, g->Message);
// In case of error or file, return unchanged argument
if (!str)
str = args->args[0];
*res_length = strlen(str);
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_Object_Nonull
} // end of Json_Object_Delete
void Json_Object_Nonull_deinit(UDF_INIT* initid)
void Json_Object_Delete_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Object_nonull_deinit
} // end of Json_Object_Delete_deinit
/***********************************************************************/
/* Make a Json array from values comming from rows. */
/* Make a Json array from values coming from rows. */
/***********************************************************************/
my_bool Json_Array_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
......@@ -563,7 +1796,7 @@ my_bool Json_Array_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
reslen *= n;
memlen += ((memlen - MEMFIX) * (n - 1));
if (JsonInit(initid, message, reslen, memlen))
if (JsonInit(initid, message, false, reslen, memlen))
return true;
PGLOBAL g = (PGLOBAL)initid->ptr;
......@@ -592,8 +1825,7 @@ char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *, char *result,
PJAR arp = (PJAR)g->Activityp;
if (g->N < 0)
push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
"Result truncated to json_grp_size values");
PUSH_WARNING("Result truncated to json_grp_size values");
arp->InitArray(g);
......@@ -619,7 +1851,7 @@ void Json_Array_Grp_deinit(UDF_INIT* initid)
} // end of Json_Array_Grp_deinit
/***********************************************************************/
/* Make a Json object from values comming from rows. */
/* Make a Json object from values coming from rows. */
/***********************************************************************/
my_bool Json_Object_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
......@@ -634,7 +1866,7 @@ my_bool Json_Object_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
reslen *= n;
memlen += ((memlen - MEMFIX) * (n - 1));
if (JsonInit(initid, message, reslen, memlen))
if (JsonInit(initid, message, false, reslen, memlen))
return true;
PGLOBAL g = (PGLOBAL)initid->ptr;
......@@ -663,8 +1895,7 @@ char *Json_Object_Grp(UDF_INIT *initid, UDF_ARGS *, char *result,
PJOB objp = (PJOB)g->Activityp;
if (g->N < 0)
push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
"Result truncated to json_grp_size values");
PUSH_WARNING("Result truncated to json_grp_size values");
if (!(str = Serialize(g, objp, NULL, 0)))
str = strcpy(result, g->Message);
......@@ -687,4 +1918,469 @@ void Json_Object_Grp_deinit(UDF_INIT* initid)
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Object_Grp_deinit
/***********************************************************************/
/* Get a string value from a Json item. */
/***********************************************************************/
my_bool Json_Get_String_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
if (args->arg_count < 2) {
strcpy(message, "Json_Get_String must have at least 2 arguments");
return true;
} else if (!IsJson(args, 0) && args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Json_Get_String first argument must be a json item");
return true;
} else if (args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Second argument is not a (jpath) string");
return true;
} else
CalcLen(args, false, reslen, memlen);
memlen += 1000; // TODO: calculate this
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Get_String_init
char *Json_Get_String(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!CheckMemory(g, initid, args, 1, false)) {
char *p, *path;
PJSON jsp;
PJSNX jsx;
PJVAL jvp = MakeValue(g, args, 0);
if ((p = jvp->GetString())) {
if (!(jsp = ParseJson(g, p, strlen(p)))) {
PUSH_WARNING(g->Message);
return NULL;
} // endif jsp
} else
jsp = jvp->GetJson();
path = MakePSZ(g, args, 1);
jsx = new(g)JSNX(g, jsp, TYPE_STRING);
if (jsx->SetJpath(g, path)) {
PUSH_WARNING(g->Message);
return NULL;
} // endif SetJpath
jsx->ReadValue(g);
if (!jsx->GetValue()->IsNull())
str = jsx->GetValue()->GetCharValue();
} // endif CheckMemory
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_Get_String
void Json_Get_String_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Get_String_deinit
/***********************************************************************/
/* Get an integer value from a Json item. */
/***********************************************************************/
my_bool Json_Get_Int_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
if (args->arg_count != 2) {
strcpy(message, "Json_Get_Int must have 2 arguments");
return true;
} else if (!IsJson(args, 0) && args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Json_Get_Int first argument must be a json item");
return true;
} else if (args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Second argument is not a (jpath) string");
return true;
} else
CalcLen(args, false, reslen, memlen);
memlen += 1000; // TODO: calculate this
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Get_Int_init
long long Json_Get_Int(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!CheckMemory(g, initid, args, 1, false)) {
long long n;
char *p, *path;
PJSON jsp;
PJSNX jsx;
PJVAL jvp = MakeValue(g, args, 0);
if ((p = jvp->GetString())) {
if (!(jsp = ParseJson(g, p, strlen(p)))) {
PUSH_WARNING(g->Message);
return NULL;
} // endif jsp
} else
jsp = jvp->GetJson();
path = MakePSZ(g, args, 1);
jsx = new(g)JSNX(g, jsp, TYPE_BIGINT);
if (jsx->SetJpath(g, path)) {
PUSH_WARNING(g->Message);
return NULL;
} // endif SetJpath
jsx->ReadValue(g);
if (jsx->GetValue()->IsNull()) {
PUSH_WARNING("Value not found");
return NULL;
} // endif IsNull
n = jsx->GetValue()->GetBigintValue();
return n;
} else
return NULL;
} // end of Json_Get_Int
void Json_Get_Int_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Get_Int_deinit
/***********************************************************************/
/* Get a double value from a Json item. */
/***********************************************************************/
my_bool Json_Get_Real_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
if (args->arg_count < 2) {
strcpy(message, "Json_Get_Real must have at least 2 arguments");
return true;
} else if (!IsJson(args, 0) && args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Json_Get_Real first argument must be a json item");
return true;
} else if (args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Second argument is not a (jpath) string");
return true;
} else if (args->arg_count > 2) {
if (args->arg_type[2] != INT_RESULT) {
strcpy(message, "Third argument is not an integer (decimals)");
return true;
} else
initid->decimals = (uint)*(longlong*)args->args[2];
} else
initid->decimals = 15;
CalcLen(args, false, reslen, memlen);
memlen += 1000; // TODO: calculate this
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Get_Real_init
double Json_Get_Real(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!CheckMemory(g, initid, args, 1, false)) {
char *p, *path;
double d;
PJSON jsp;
PJSNX jsx;
PJVAL jvp = MakeValue(g, args, 0);
if ((p = jvp->GetString())) {
if (!(jsp = ParseJson(g, p, strlen(p)))) {
PUSH_WARNING(g->Message);
return NULL;
} // endif jsp
} else
jsp = jvp->GetJson();
path = MakePSZ(g, args, 1);
jsx = new(g)JSNX(g, jsp, TYPE_DOUBLE);
if (jsx->SetJpath(g, path)) {
PUSH_WARNING(g->Message);
return NULL;
} // endif SetJpath
jsx->ReadValue(g);
if (jsx->GetValue()->IsNull()) {
PUSH_WARNING("Value not found");
return NULL;
} // endif IsNull
d = jsx->GetValue()->GetFloatValue();
return d;
} else
return NULL;
} // end of Json_Get_Real
void Json_Get_Real_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Get_Real_deinit
/***********************************************************************/
/* Locate a value in a Json tree. */
/***********************************************************************/
my_bool Json_Locate_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen, more = 1000;
if (args->arg_count < 2) {
strcpy(message, "Json_Locate must have at least 2 arguments");
return true;
} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
strcpy(message, "Json_Locate first argument must be a json item");
return true;
} else if (args->arg_count > 2)
if (args->arg_type[2] != INT_RESULT) {
strcpy(message, "Third argument is not an integer (memory)");
return true;
} else
more = (ulong)*(longlong*)args->args[2];
CalcLen(args, false, reslen, memlen);
memlen += more; // TODO: calculate this
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Locate_init
char *Json_Locate(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!CheckMemory(g, initid, args, 1, false)) {
char *p, *path = NULL;
int rc;
PJVAL jvp;
PJSON jsp;
PJSNX jsx;
// Save stack and allocation environment and prepare error return
if (g->jump_level == MAX_JUMP) {
PUSH_WARNING(MSG(TOO_MANY_JUMPS));
return NULL;
} // endif jump_level
if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
PUSH_WARNING(g->Message);
goto err;
} // endif rc
jvp = MakeValue(g, args, 0);
if ((p = jvp->GetString())) {
if (!(jsp = ParseJson(g, p, strlen(p)))) {
PUSH_WARNING(g->Message);
goto err;
} // endif jsp
} else
jsp = jvp->GetJson();
jsx = new(g)JSNX(g, jsp, TYPE_STRING);
path = jsx->Locate(g, jsp, args->args[1], args->arg_type[1], args->lengths[1]);
err:
g->jump_level--;
*res_length = (path) ? strlen(path) : 0;
return path;
} else
return NULL;
} // end of Json_Locate
void Json_Locate_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Locate_deinit
/***********************************************************************/
/* Returns a json file as a string. */
/***********************************************************************/
my_bool Json_File_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen, more = 1024;
if (args->arg_count > 4) {
strcpy(message, "Json_File cannot accept more than 4 arguments");
return true;
} else if (args->arg_type[0] != STRING_RESULT) {
strcpy(message, "Json_File first argument must be a (string) file name");
return true;
} else if (args->arg_count > 1 && args->arg_type[1] != INT_RESULT) {
strcpy(message, "Second argument is not an integer (pretty)");
return true;
} else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
strcpy(message, "Third argument is not an integer (check)");
return true;
} else if (args->arg_count > 3) {
if (args->arg_type[3] != INT_RESULT) {
strcpy(message, "Fourth argument is not an integer (memory)");
return true;
} else
more += (ulong)*(longlong*)args->args[2];
} // endifs
initid->maybe_null = 1;
CalcLen(args, false, reslen, memlen);
if (args->arg_count > 2 && *(longlong*)args->args[2])
more += GetFileLength(args->args[0]) * 5;
memlen += more;
return JsonInit(initid, message, false, reslen, memlen);
} // end of Json_File_init
char *Json_File(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str, *fn;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
fn = MakePSZ(g, args, 0);
if (args->arg_count > 2 && *(longlong*)args->args[2]) {
char *memory;
int len, pretty;
HANDLE hFile;
MEMMAP mm;
PJSON jsp;
pretty = (int)*(longlong*)args->args[1];
/*******************************************************************/
/* Create the mapping file object. */
/*******************************************************************/
hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD rc = GetLastError();
if (!(*g->Message))
sprintf(g->Message, MSG(OPEN_MODE_ERROR), "map", (int)rc, fn);
PUSH_WARNING(g->Message);
return NULL;
} // endif hFile
/*******************************************************************/
/* Get the file size (assuming file is smaller than 4 GB) */
/*******************************************************************/
len = mm.lenL;
memory = (char *)mm.memory;
if (!len) { // Empty or deleted file
CloseFileHandle(hFile);
return NULL;
} // endif len
if (!memory) {
CloseFileHandle(hFile);
sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, GetLastError());
return NULL;
} // endif Memory
CloseFileHandle(hFile); // Not used anymore
hFile = INVALID_HANDLE_VALUE; // For Fblock
/*********************************************************************/
/* Parse the json file and allocate its tree structure. */
/*********************************************************************/
g->Message[0] = 0;
if (!(jsp = ParseJson(g, memory, len, pretty))) {
PUSH_WARNING(g->Message);
str = NULL;
} // endif jsp
CloseMemMap(memory, len);
if (jsp && !(str = Serialize(g, jsp, NULL, 0)))
PUSH_WARNING(g->Message);
} else
str = GetJsonFile(g, fn);
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_File
void Json_File_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_File_deinit
/***********************************************************************/
/* Make a json file from a json item. */
/***********************************************************************/
my_bool Json_Make_File_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen, more = 1024;
if (args->arg_count < 2 || args->arg_count > 3) {
strcpy(message, "Json_Make_File only accepts 2 or 3 arguments");
return true;
} else if (IsJson(args, 0) != 1) {
strcpy(message, "Json_Make_File first argument must be a json item");
return true;
} else if (args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Second argument must be a (string) file name");
return true;
} else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
strcpy(message, "Third argument is not an integer (pretty)");
return true;
} // endifs
CalcLen(args, false, reslen, memlen);
return JsonInit(initid, message, true, reslen, memlen);
} // end of Json_Make_File_init
char *Json_Make_File(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str, *fn, *msg;
int pretty;
PJVAL jvp;
PGLOBAL g = (PGLOBAL)initid->ptr;
PlugSubSet(g, g->Sarea, g->Sarea_Size);
jvp = MakeValue(g, args, 0);
fn = MakePSZ(g, args, 1);
pretty = (args->arg_count > 2) ? (int)*(longlong*)args->args[2] : 2;
if ((msg = Serialize(g, jvp->GetJson(), fn, pretty)))
PUSH_WARNING(msg);
str= fn;
*res_length = (str) ? strlen(str) : 0;
return str;
} // end of Json_Make_File
void Json_Make_File_deinit(UDF_INIT* initid)
{
PlugExit((PGLOBAL)initid->ptr);
} // end of Json_Make_File_deinit
/*************** tabjson H Declares Source Code File (.H) **************/
/* Name: jsonudf.h Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2015 */
/* */
/* This file contains the JSON UDF function and classe declares. */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "block.h"
#include "osutil.h"
#include "maputil.h"
#include "json.h"
#define UDF_EXEC_ARGS \
UDF_INIT*, UDF_ARGS*, char*, unsigned long*, char*, char*
/***********************************************************************/
/* The JSON tree node. Can be an Object or an Array. */
/***********************************************************************/
typedef struct _jnode {
PSZ Key; // The key used for object
OPVAL Op; // Operator used for this node
PVAL CncVal; // To cont value used for OP_CNC
PVAL Valp; // The internal array VALUE
int Rank; // The rank in array
int Rx; // Read row number
int Nx; // Next to read row number
} JNODE, *PJNODE;
typedef class JSNX *PJSNX;
typedef class JOUTPATH *PJTP;
extern "C" {
DllExport my_bool Json_Value_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Value(UDF_EXEC_ARGS);
DllExport void Json_Value_deinit(UDF_INIT*);
DllExport my_bool Json_Array_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Array(UDF_EXEC_ARGS);
DllExport void Json_Array_deinit(UDF_INIT*);
DllExport my_bool Json_Array_Add_Values_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Array_Add_Values(UDF_EXEC_ARGS);
DllExport void Json_Array_Add_Values_deinit(UDF_INIT*);
DllExport my_bool Json_Array_Add_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Array_Add(UDF_EXEC_ARGS);
DllExport void Json_Array_Add_deinit(UDF_INIT*);
DllExport my_bool Json_Array_Delete_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Array_Delete(UDF_EXEC_ARGS);
DllExport void Json_Array_Delete_deinit(UDF_INIT*);
DllExport my_bool Json_Object_Nonull_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Object_Nonull(UDF_EXEC_ARGS);
DllExport void Json_Object_Nonull_deinit(UDF_INIT*);
DllExport my_bool Json_Object_Add_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Object_Add(UDF_EXEC_ARGS);
DllExport void Json_Object_Add_deinit(UDF_INIT*);
DllExport my_bool Json_Object_Delete_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Object_Delete(UDF_EXEC_ARGS);
DllExport void Json_Object_Delete_deinit(UDF_INIT*);
DllExport my_bool Json_Object_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Object(UDF_EXEC_ARGS);
DllExport void Json_Object_deinit(UDF_INIT*);
DllExport my_bool Json_Array_Grp_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport void Json_Array_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *);
DllExport char *Json_Array_Grp(UDF_EXEC_ARGS);
DllExport void Json_Array_Grp_clear(UDF_INIT *, char *, char *);
DllExport void Json_Array_Grp_deinit(UDF_INIT*);
DllExport my_bool Json_Object_Grp_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport void Json_Object_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *);
DllExport char *Json_Object_Grp(UDF_EXEC_ARGS);
DllExport void Json_Object_Grp_clear(UDF_INIT *, char *, char *);
DllExport void Json_Object_Grp_deinit(UDF_INIT*);
DllExport my_bool Json_Get_String_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Get_String(UDF_EXEC_ARGS);
DllExport void Json_Get_String_deinit(UDF_INIT*);
DllExport my_bool Json_Get_Int_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport long long Json_Get_Int(UDF_EXEC_ARGS);
DllExport void Json_Get_Int_deinit(UDF_INIT*);
DllExport my_bool Json_Get_Real_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport double Json_Get_Real(UDF_EXEC_ARGS);
DllExport void Json_Get_Real_deinit(UDF_INIT*);
DllExport my_bool Json_Locate_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Locate(UDF_EXEC_ARGS);
DllExport void Json_Locate_deinit(UDF_INIT*);
DllExport my_bool Json_File_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_File(UDF_EXEC_ARGS);
DllExport void Json_File_deinit(UDF_INIT*);
DllExport my_bool Json_Make_File_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *Json_Make_File(UDF_EXEC_ARGS);
DllExport void Json_Make_File_deinit(UDF_INIT*);
} // extern "C"
/***********************************************************************/
/* Class JSNX: JSON access method. */
/***********************************************************************/
class JSNX : public BLOCK {
public:
// Constructors
JSNX(PGLOBAL g, PJSON row, int type, int len = 64, int prec = 0);
// Implementation
int GetPrecision(void) {return Prec;}
PVAL GetValue(void) {return Value;}
// Methods
my_bool SetJpath(PGLOBAL g, char *path);
my_bool ParseJpath(PGLOBAL g);
void ReadValue(PGLOBAL g);
PJVAL GetJson(PGLOBAL g);
char *Locate(PGLOBAL g, PJSON jsp, char *what,
enum Item_result type, unsigned long len);
protected:
my_bool CheckExpand(PGLOBAL g, int i, PSZ nm, my_bool b);
my_bool SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm);
PVAL GetColumnValue(PGLOBAL g, PJSON row, int i);
PJVAL GetValue(PGLOBAL g, PJSON row, int i);
PVAL ExpandArray(PGLOBAL g, PJAR arp, int n);
PVAL CalculateArray(PGLOBAL g, PJAR arp, int n);
PVAL MakeJson(PGLOBAL g, PJSON jsp);
void SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n);
//PJSON GetRow(PGLOBAL g);
my_bool LocateArray(PJAR jarp);
my_bool LocateObject(PJOB jobp);
my_bool LocateValue(PJVAL jvp);
// Default constructor not to be used
JSNX(void) {}
// Members
PJSON Row;
PVAL Value;
PVAL MulVal; // To value used by multiple column
PJTP Jp;
JNODE *Nodes; // The intermediate objects
char *Jpath; // The json path
int Buf_Type;
int Long;
int Prec;
int Nod; // The number of intermediate objects
int Xnod; // Index of multiple values
int B; // Index base
my_bool Xpd; // True for expandable column
my_bool Parsed; // True when parsed
}; // end of class JSNX
/***********************************************************************/
/* Class JOUTPATH. Used to make the locate path. */
/***********************************************************************/
class JOUTPATH : public JOUTSTR {
public:
JOUTPATH(PGLOBAL g, char *w, enum Item_result type, unsigned long len)
: JOUTSTR(g) {What = w; Type = type; Len = len; Found = false;}
// Members
enum Item_result Type;
unsigned long Len;
char *What;
my_bool Found;
}; // end of class JOUTPATH
......@@ -14,20 +14,23 @@ SELECT Json_Array(56,3.1416,'My name is "Foo"',NULL);
Json_Array(56,3.1416,'My name is "Foo"',NULL)
[56,3.141600,"My name is \"Foo\"",null]
SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL)) Array;
ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Value_Add must have at least 2 arguments
ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Array_Add must have at least 2 arguments
SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL),'One more') Array;
Array
[56,3.141600,"foo",null,"One more"]
SELECT Json_Array_Add(Json_Value('one value'),'One more');
Json_Array_Add(Json_Value('one value'),'One more')
["one value","One more"]
"one value"
Warnings:
Warning 1105 First argument is not an array
SELECT Json_Array_Add('one value','One more');
ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Value_Add first argument must be a json item
ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Array_Add first argument must be a json item
SELECT Json_Array_Add('one value' json_,'One more');
Json_Array_Add('one value' json_,'One more')
[null,"One more"]
one value
Warnings:
Warning 1105 Bad 'o' character near one value
Warning 1105 Unexpected character 'o' near one value
Warning 1105 First argument is not an array
SELECT Json_Value(56,3.1416,'foo',NULL);
ERROR HY000: Can't initialize function 'Json_Value'; Json_Value cannot accept more than 1 argument
SELECT Json_Value(3.1416);
......
{"ISBN":"9782212090819","LANG":"fr","SUBJECT":"applications","AUTHOR":[{"FIRSTNAME":"Jean-Michel","LASTNAME":"Bernadac"},{"FIRSTNAME":"Franois","LASTNAME":"Knab"}],"TITLE":"Construire une application XML","PUBLISHER":{"NAME":"Eyrolles","PLACE":"Paris"},"DATEPUB":1999}
{"ISBN":"9782840825685","LANG":"fr","SUBJECT":"applications","AUTHOR":[{"FIRSTNAME":"William J.","LASTNAME":"Pardi"}],"TITLE":"XML en Action","TRANSLATED":{"PREFIX":"adapt de l'anglais par","TRANSLATOR":{"FIRSTNAME":"James","LASTNAME":"Guerin"}},"PUBLISHER":{"NAME":"Microsoft Press","PLACE":"Paris"},"DATEPUB":2001}
......@@ -4,6 +4,7 @@
let $MYSQLD_DATADIR= `select @@datadir`;
--copy_file $MTR_SUITE_DIR/std_data/biblio.json $MYSQLD_DATADIR/test/biblio.json
--copy_file $MTR_SUITE_DIR/std_data/bib0.json $MYSQLD_DATADIR/test/bib0.json
--copy_file $MTR_SUITE_DIR/std_data/expense.json $MYSQLD_DATADIR/test/expense.json
--copy_file $MTR_SUITE_DIR/std_data/mulexp3.json $MYSQLD_DATADIR/test/mulexp3.json
--copy_file $MTR_SUITE_DIR/std_data/mulexp4.json $MYSQLD_DATADIR/test/mulexp4.json
......@@ -115,6 +116,33 @@ ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='biblio.json';
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # Testing a pretty=0 file
--echo #
CREATE TABLE t1
(
ISBN CHAR(15) NOT NULL,
Language CHAR(2) FIELD_FORMAT='LANG',
Subject CHAR(32) FIELD_FORMAT='SUBJECT',
AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:FIRSTNAME',
AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:LASTNAME',
Title CHAR(32) FIELD_FORMAT='TITLE',
Translation CHAR(32) FIELD_FORMAT='TRANSLATED:PREFIX',
TranslatorFN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:FIRSTNAME',
TranslatorLN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:LASTNAME',
Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
Year int(4) FIELD_FORMAT='DATEPUB',
INDEX IX(ISBN)
)
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bib0.json' LRECL=320 OPTION_LIST='Pretty=0';
SHOW INDEX FROM t1;
SELECT * FROM t1;
DESCRIBE SELECT * FROM t1 WHERE ISBN = '9782212090819';
--error ER_GET_ERRMSG
UPDATE t1 SET AuthorFN = 'Philippe' WHERE ISBN = '9782212090819';
DROP TABLE t1;
--echo #
--echo # A file with 2 arrays
--echo #
......@@ -258,6 +286,8 @@ DROP TABLE t1, t2, t3, t4;
# Clean up
#
--remove_file $MYSQLD_DATADIR/test/biblio.json
--remove_file $MYSQLD_DATADIR/test/bib0.dnx
--remove_file $MYSQLD_DATADIR/test/bib0.json
--remove_file $MYSQLD_DATADIR/test/expense.json
--remove_file $MYSQLD_DATADIR/test/mulexp3.json
--remove_file $MYSQLD_DATADIR/test/mulexp4.json
......
......@@ -2360,9 +2360,11 @@ int ODBConn::GetCatInfo(CATPARM *cap)
} // endif rc
for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) {
if (vlen[n] == SQL_NULL_DATA)
if (vlen[n] == SQL_NO_TOTAL)
ThrowDBX("Unexpected SQL_NO_TOTAL returned from SQLFetch");
else if (vlen[n] == SQL_NULL_DATA)
pval[n]->SetNull(true);
else if (crp->Type == TYPE_STRING && vlen[n] != SQL_NULL_DATA)
else if (crp->Type == TYPE_STRING/* && vlen[n] != SQL_NULL_DATA*/)
pval[n]->SetValue_char(pbuf[n], vlen[n]);
else
pval[n]->SetNull(false);
......
......@@ -193,7 +193,8 @@ class DllExport COLDEF : public COLCRT { /* Column description block
friend class COLBLK;
friend class DBFFAM;
friend class TDBASE;
public:
friend class TDBDOS;
public:
COLDEF(void); // Constructor
// Implementation
......
......@@ -2018,6 +2018,7 @@ int TDBDOS::EstimatedLength(void)
// result if we set dep to 1
dep = 1 + cdp->GetLong() / 20; // Why 20 ?????
} else for (; cdp; cdp = cdp->GetNext())
if (!(cdp->Flags & (U_VIRTUAL|U_SPECIAL)))
dep = MY_MAX(dep, cdp->GetOffset());
return (int)dep;
......@@ -2203,7 +2204,7 @@ bool TDBDOS::PrepareWriting(PGLOBAL)
} // endif Mode
return false;
} // end of WriteDB
} // end of PrepareWriting
/***********************************************************************/
/* WriteDB: Data Base write routine for DOS access method. */
......@@ -2215,7 +2216,7 @@ int TDBDOS::WriteDB(PGLOBAL g)
// Make the line to write
if (PrepareWriting(g))
return true;
return RC_FX;
if (trace > 1)
htrc("Write: line is='%s'\n", To_Line);
......
......@@ -755,7 +755,6 @@ int TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp)
} else
strcpy(To_Line, s);
Row->Clear();
return false;
} else
return true;
......@@ -979,6 +978,7 @@ bool JSONCOL::ParseJpath(PGLOBAL g)
if (!stricmp(Name, colp->GetName())) {
Nod = colp->Nod;
Nodes = colp->Nodes;
Xpd = colp->Xpd;
goto fin;
} // endif Name
......@@ -1045,6 +1045,7 @@ void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
switch (val->GetValType()) {
case TYPE_STRG:
case TYPE_INTG:
case TYPE_BINT:
case TYPE_DBL:
vp->SetValue_pval(val->GetValue());
break;
......@@ -1347,6 +1348,11 @@ PJSON JSONCOL::GetRow(PGLOBAL g)
/***********************************************************************/
void JSONCOL::WriteColumn(PGLOBAL g)
{
if (Xpd && Tjp->Pretty < 2) {
strcpy(g->Message, "Cannot write expanded column when Pretty is not 2");
longjmp(g->jumper[g->jump_level], 666);
} // endif Xpd
/*********************************************************************/
/* Check whether this node must be written. */
/*********************************************************************/
......@@ -1534,7 +1540,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
if (*objpath != '[') { // objpass is a key
if (jsp->GetType() != TYPE_JOB) {
strcpy(g->Message, "Table path does no match json file");
strcpy(g->Message, "Table path does not match the json file");
return RC_FX;
} // endif Type
......@@ -1550,7 +1556,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
} else if (objpath[strlen(objpath)-1] == ']') {
if (jsp->GetType() != TYPE_JAR) {
strcpy(g->Message, "Table path does no match json file");
strcpy(g->Message, "Table path does not match the json file");
return RC_FX;
} // endif Type
......@@ -1831,7 +1837,6 @@ void TDBJSON::CloseDB(PGLOBAL g)
// Save the modified document
char filename[_MAX_PATH];
PSZ msg;
FILE *fop;
Doc->InitArray(g);
......@@ -1839,11 +1844,7 @@ void TDBJSON::CloseDB(PGLOBAL g)
PlugSetPath(filename, ((PJDEF)To_Def)->Fn, GetPath());
// Serialize the modified table
if (!(fop = fopen(filename, "wb"))) {
sprintf(g->Message, MSG(OPEN_MODE_ERROR),
"w", (int)errno, filename);
strcat(strcat(g->Message, ": "), strerror(errno));
} else if ((msg = Serialize(g, Top, fop, Pretty)))
if ((msg = Serialize(g, Top, filename, Pretty)))
puts(msg);
} // end of CloseDB
......
......@@ -1358,10 +1358,6 @@ void ODBCCOL::ReadColumn(PGLOBAL g)
} // endif Buf_Type
// Handle null values
if (Value->IsZero())
Value->SetNull(Nullable);
if (trace > 1) {
char buf[64];
......
......@@ -103,6 +103,7 @@ ulonglong CharToNumber(char *p, int n, ulonglong maxval,
if (minus) *minus = false;
if (rc) *rc = false;
if (n <= 0) return 0LL;
// Eliminate leading blanks or 0
for (p2 = p + n; p < p2 && (*p == ' ' || *p == '0'); p++) ;
......@@ -705,7 +706,7 @@ bool TYPVAL<TYPE>::SetValue_char(char *p, int n)
template <>
bool TYPVAL<double>::SetValue_char(char *p, int n)
{
if (p) {
if (p && n > 0) {
char buf[64];
for (; n > 0 && *p == ' '; p++)
......@@ -1345,7 +1346,7 @@ bool TYPVAL<PSZ>::SetValue_char(char *p, int n)
{
bool rc;
if (p) {
if (p && n > 0) {
rc = n > Len;
if ((n = MY_MIN(n, Len))) {
......@@ -1804,7 +1805,7 @@ bool DECVAL::SetValue_char(char *p, int n)
{
bool rc;
if (p) {
if (p && n > 0) {
rc = n > Len;
if ((n = MY_MIN(n, Len))) {
......@@ -2095,7 +2096,7 @@ bool BINVAL::SetValue_char(char *p, int n)
{
bool rc;
if (p) {
if (p && n > 0) {
rc = n > Clen;
Len = MY_MIN(n, Clen);
memcpy(Binp, p, Len);
......@@ -2672,13 +2673,16 @@ bool DTVAL::SetValue_char(char *p, int n)
int ndv;
int dval[6];
if (n > 0) {
// Trim trailing blanks
for (p2 = p + n -1; p < p2 && *p2 == ' '; p2--) ;
for (p2 = p + n -1; p < p2 && *p2 == ' '; p2--);
if ((rc = (n = p2 - p + 1) > Len))
n = Len;
memcpy(Sdate, p, n);
} // endif n
Sdate[n] = '\0';
ndv = ExtractDate(Sdate, Pdtp, DefYear, dval);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment