diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
commit | 3f619478f796eddbba6e39502fe941b285dd97b1 (patch) | |
tree | e2c7b5777f728320e5b5542b6213fd3591ba51e2 /storage/connect/jsonudf.cpp | |
parent | Initial commit. (diff) | |
download | mariadb-upstream.tar.xz mariadb-upstream.zip |
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/connect/jsonudf.cpp')
-rw-r--r-- | storage/connect/jsonudf.cpp | 6646 |
1 files changed, 6646 insertions, 0 deletions
diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp new file mode 100644 index 00000000..f87ac091 --- /dev/null +++ b/storage/connect/jsonudf.cpp @@ -0,0 +1,6646 @@ +/****************** jsonudf C++ Program Source Code File (.CPP) ******************/ +/* PROGRAM NAME: jsonudf Version 1.8 */ +/* (C) Copyright to the author Olivier BERTRAND 2015-2019 */ +/* This program are the JSON User Defined Functions . */ +/*********************************************************************************/ + +/*********************************************************************************/ +/* Include relevant sections of the MariaDB header file. */ +/*********************************************************************************/ +#include <my_global.h> +#include <mysqld.h> +#include <mysqld_error.h> +#include <mysql.h> +#include <sql_error.h> +#include <m_string.h> + +#include "jsonudf.h" + +#if defined(UNIX) || defined(UNIV_LINUX) +#define _O_RDONLY O_RDONLY +#endif + +#define MEMFIX 4096 +#if defined(connect_EXPORTS) +#define PUSH_WARNING(M) push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, M) +#else +#define PUSH_WARNING(M) htrc(M) +#endif +#define M 9 + +static char *handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error); +static char *bin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error); +static PJSON JsonNew(PGLOBAL g, JTYP type); +static PJVAL JvalNew(PGLOBAL g, JTYP type, void *vp = NULL); +static PJSNX JsnxNew(PGLOBAL g, PJSON jsp, int type, int len = 64); +uint GetJsonGroupSize(void); +static void SetChanged(PBSON bsp); + +uint JsonGrpSize = 10; + +/*********************************************************************************/ +/* SubAlloc a new JSNX class with protection against memory exhaustion. */ +/*********************************************************************************/ +static PJSNX JsnxNew(PGLOBAL g, PJSON jsp, int type, int len) +{ + PJSNX jsx; + + try { + jsx = new(g) JSNX(g, jsp, type, len); + } catch (...) { + if (trace(1023)) + htrc("%s\n", g->Message); + + PUSH_WARNING(g->Message); + jsx = NULL; + } // end try/catch + + return jsx; +} /* end of JsnxNew */ + +/* ----------------------------------- JSNX ------------------------------------ */ + +/*********************************************************************************/ +/* JSNX public constructor. */ +/*********************************************************************************/ +JSNX::JSNX(PGLOBAL g, PJSON row, int type, int len, int prec, my_bool wr) +{ + Row = row; + Jvalp = NULL; + Jpnp = NULL; + Jp = NULL; + Nodes = NULL; + Value = AllocateValue(g, type, len, prec); + //MulVal = NULL; + Jpath = NULL; + Buf_Type = type; + Long = len; + Prec = prec; + Nod = 0; + Xnod = -1; + K = 0; + I = -1; + Imax = 9; + B = 0; + Xpd = false; + Parsed = false; + Found = false; + Wr = wr; + Jb = false; +} // end of JSNX constructor + +/*********************************************************************************/ +/* SetJpath: set and parse the json path. */ +/*********************************************************************************/ +my_bool JSNX::SetJpath(PGLOBAL g, char *path, my_bool jb) +{ + // Check Value was allocated + if (!Value) + return true; + + Value->SetNullable(true); + Jpath = path; + + // Parse the json path + Parsed = false; + Nod = 0; + Jb = jb; + return ParseJpath(g); +} // end of SetJpath + +/*********************************************************************************/ +/* 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 - 1] == ']') { + p[--n] = 0; + } else if (!IsNum(p)) { + // Wrong array specification + snprintf(g->Message, sizeof(g->Message), "Invalid array specification %s", p); + return true; + } // endif p + + } else + b = true; + + // To check whether a numeric Rank was specified + dg = IsNum(p); + + if (!n) { + // Default specifications + if (jnp->Op != OP_EXP) { + if (Wr) { + // Force append + jnp->Rank = INT_MAX32; + jnp->Op = OP_LE; + } else if (Jb) { + // Return a Json item + jnp->Op = OP_XX; + } else if (b) { + // Return 1st value (B is the index base) + jnp->Rank = B; + jnp->Op = OP_LE; + } else if (!Value->IsTypeNum()) { + jnp->CncVal = AllocateValue(g, PlugDup(g, ", "), 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 (Wr) { + snprintf(g->Message, sizeof(g->Message), "Invalid specification %s in a write path", p); + return true; + } else if (n == 1) { + // Set the Op value; + switch (*p) { + case '+': jnp->Op = OP_ADD; break; + case 'x': 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 '*': // Expand this array + snprintf(g->Message, sizeof(g->Message), "Expand not supported by this function"); + return true; + default: + snprintf(g->Message, sizeof(g->Message), "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; + + if (trace(1)) + htrc("Concat string=%s\n", p + 1); + + jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING); + } // endif n + + } else { + snprintf(g->Message, sizeof(g->Message), "Wrong array specification"); + return true; + } // endif's + + 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, *p1 = NULL, *p2 = NULL, *pbuf = NULL; + int i; + my_bool a; + + if (Parsed) + return false; // Already done + else if (!Jpath) + // Jpath = Name; + return true; + + if (trace(1)) + htrc("ParseJpath %s\n", SVP(Jpath)); + + if (!(pbuf = PlgDBDup(g, Jpath))) + return true; + + if (*pbuf == '$') pbuf++; + if (*pbuf == '.') pbuf++; + if (*pbuf == '[') p1 = pbuf++; + + // Estimate the required number of nodes + for (i = 0, p = pbuf; (p = NextChr(p, '.')); i++, p++) + Nod++; // One path node found + + if (!(Nodes = (PJNODE)PlgDBSubAlloc(g, NULL, (++Nod) * sizeof(JNODE)))) + return true; + + memset(Nodes, 0, (Nod)* sizeof(JNODE)); + + // Analyze the Jpath for this column + for (i = 0, p = pbuf; p && i < Nod; i++, p = (p2 ? p2 : NULL)) { + a = (p1 != NULL); + p1 = strchr(p, '['); + p2 = strchr(p, '.'); + + if (!p2) + p2 = p1; + else if (p1) { + if (p1 < p2) + p2 = p1; + else if (p1 == p2 + 1) + *p2++ = 0; // Old syntax .[ + else + p1 = NULL; + + } // endif p1 + + if (p2) + *p2++ = 0; + + // Jpath must be explicit + if (a || *p == 0 || *p == '[' || IsNum(p)) { + // Analyse intermediate array processing + if (SetArrayOptions(g, p, i, Nodes[i-1].Key)) + return true; + + } else if (*p == '*') { + if (Wr) { + snprintf(g->Message, sizeof(g->Message), "Invalid specification %c in a write path", *p); + return true; + } else // Return JSON + Nodes[i].Op = OP_XX; + + } else { + Nodes[i].Key = p; + Nodes[i].Op = OP_EXIST; + } // endif's + + } // endfor i, p + + Nod = i; + //MulVal = AllocateValue(g, Value); + + if (trace(1)) + for (i = 0; i < Nod; i++) + htrc("Node(%d) Key=%s Op=%d Rank=%d\n", + i, SVP(Nodes[i].Key), Nodes[i].Op, Nodes[i].Rank); + + Parsed = true; + return false; +} // end of ParseJpath + +/*********************************************************************************/ +/* SetValue: Set a value from a JVALUE contains. */ +/*********************************************************************************/ +void JSNX::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val) +{ + if (val) { + vp->SetNull(false); + + if (Jb) { + vp->SetValue_psz(Serialize(g, val->GetJsp(), NULL, 0)); + Jb = false; + } else switch (val->GetValType()) { + case TYPE_DTM: + case TYPE_STRG: + vp->SetValue_psz(val->GetString(g)); + break; + case TYPE_INTG: + vp->SetValue(val->GetInteger()); + break; + case TYPE_BINT: + vp->SetValue(val->GetBigint()); + break; + case TYPE_DBL: + if (vp->IsTypeNum()) + vp->SetValue(val->GetFloat()); + else // Get the proper number of decimals + vp->SetValue_psz(val->GetString(g)); + + 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: + vp->SetValue_psz(val->GetArray()->GetText(g, NULL)); + break; + case TYPE_JOB: + vp->SetValue_psz(val->GetObject()->GetText(g, NULL)); + break; + case TYPE_NULL: + vp->SetNull(true); + /* falls through */ + default: + vp->Reset(); + } // endswitch Type + + } else { + vp->SetNull(true); + vp->Reset(); + } // endif val + +} // end of SetJsonValue + +/*********************************************************************************/ +/* MakeJson: Serialize the json item and set value to it. */ +/*********************************************************************************/ +PJVAL JSNX::MakeJson(PGLOBAL g, PJSON jsp, int n) +{ + Jb = false; + + if (Value->IsTypeNum()) { + snprintf(g->Message, sizeof(g->Message), "Cannot make Json for a numeric value"); + return NULL; + } else if (jsp->GetType() != TYPE_JAR && jsp->GetType() != TYPE_JOB) { + snprintf(g->Message, sizeof(g->Message), "Target is not an array or object"); + return NULL; + } else if (n < Nod -1) { + if (jsp->GetType() == TYPE_JAR) { + int ars = jsp->GetSize(false); + PJNODE jnp = &Nodes[n]; + PJAR jarp = new(g) JARRAY; + + jnp->Op = OP_EQ; + + for (jnp->Rank = 0; jnp->Rank < ars; jnp->Rank++) + jarp->AddArrayValue(g, GetRowValue(g, jsp, n)); + + jarp->InitArray(g); + jnp->Op = OP_XX; + jnp->Rank = 0; + jsp = jarp; + } else if(jsp->GetType() == TYPE_JOB) { + PJSON jp; + PJOB jobp = new(g) JOBJECT; + + for (PJPR prp = ((PJOB)jsp)->GetFirst(); prp; prp = prp->Next) { + jp = (prp->Val->DataType == TYPE_JSON) ? prp->Val->Jsp : prp->Val; + jobp->SetKeyValue(g, GetRowValue(g, jp, n + 1), prp->Key); + } // endfor prp + + jsp = jobp; + } // endif Type + + } // endif + + Jb = true; + return new(g) JVALUE(jsp); +} // end of MakeJson + +/*********************************************************************************/ +/* GetJson: */ +/*********************************************************************************/ +PJVAL JSNX::GetJson(PGLOBAL g) +{ + return GetRowValue(g, Row, 0); +} // end of GetJson + +/*********************************************************************************/ +/* ReadValue: */ +/*********************************************************************************/ +void JSNX::ReadValue(PGLOBAL g) +{ + Value->SetValue_pval(GetColumnValue(g, Row, 0)); +} // end of ReadValue + +/*********************************************************************************/ +/* GetColumnValue: */ +/*********************************************************************************/ +PVAL JSNX::GetColumnValue(PGLOBAL g, PJSON row, int i) +{ + PJVAL val = NULL; + + val = GetRowValue(g, row, i); + SetJsonValue(g, Value, val); + return Value; +} // end of GetColumnValue + +/*********************************************************************************/ +/* GetRowValue: */ +/*********************************************************************************/ +PJVAL JSNX::GetRowValue(PGLOBAL g, PJSON row, int i, my_bool b) +{ + PJAR arp; + PJVAL val = NULL; + + for (; i < Nod && row; i++) { + if (Nodes[i].Op == OP_NUM) { + Value->SetValue(row->GetType() == TYPE_JAR ? ((PJAR)row)->size() : 1); + val = new(g) JVALUE(g, Value); + return val; + } else if (Nodes[i].Op == OP_XX) { + return MakeJson(g, row, i); + } else switch (row->GetType()) { + case TYPE_JOB: + if (!Nodes[i].Key) { + // Expected Array was not there + if (Nodes[i].Op == OP_LE) { + if (i < Nod-1) + continue; + else + val = new(g)JVALUE(row); + + } else { + snprintf(g->Message, sizeof(g->Message), "Unexpected object"); + val = NULL; + } //endif Op + + } else + val = ((PJOB)row)->GetKeyValue(Nodes[i].Key); + + break; + case TYPE_JAR: + arp = (PJAR)row; + + if (!Nodes[i].Key) { + if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE) + val = arp->GetArrayValue(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 { + // Unexpected array, unwrap it as [0] + val = arp->GetArrayValue(0); + i--; + } // endif's + + break; + case TYPE_JVAL: + val = (PJVAL)row; + break; + default: + snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->GetType()); + val = NULL; + } // endswitch Type + + if (i < Nod-1) + if (!(row = (val) ? val->GetJsp() : NULL)) + val = NULL; +// row = (val) ? val->GetJson() : NULL; + + } // endfor i + + // SetJsonValue(g, Value, val); + return val; +} // end of GetRowValue + +/*********************************************************************************/ +/* ExpandArray: */ +/*********************************************************************************/ +PVAL JSNX::ExpandArray(PGLOBAL g, PJAR arp, int n) +{ + snprintf(g->Message, sizeof(g->Message), "Expand cannot be done by this function"); + return NULL; +} // end of ExpandArray + +/*********************************************************************************/ +/* Get the value used for calculating the array. */ +/*********************************************************************************/ +PVAL JSNX::GetCalcValue(PGLOBAL g, PJAR jap, int n) +{ + // For calculated arrays, a local Value must be used + int lng = 0; + short type= 0, prec= 0; + bool b = n < Nod - 1; + PVAL valp; + PJVAL vlp, vp; + OPVAL op = Nodes[n].Op; + + switch (op) { + case OP_NUM: + type = TYPE_INT; + break; + case OP_ADD: + case OP_MULT: + if (!IsTypeNum(Buf_Type)) { + type = TYPE_INT; + prec = 0; + + for (vlp = jap->GetArrayValue(0); vlp; vlp = vlp->Next) { + vp = (b && vlp->GetJsp()) ? GetRowValue(g, vlp, n + 1) : vlp; + + switch (vp->DataType) { + case TYPE_BINT: + if (type == TYPE_INT) + type = TYPE_BIGINT; + + break; + case TYPE_DBL: + case TYPE_FLOAT: + type = TYPE_DOUBLE; + prec = MY_MAX(prec, vp->Nd); + break; + default: + break; + } // endswitch Type + + } // endfor vlp + + } else { + type = Buf_Type; + prec = GetPrecision(); + } // endif Buf_Type + + break; + case OP_SEP: + if (IsTypeChar(Buf_Type)) { + type = TYPE_DOUBLE; + prec = 2; + } else { + type = Buf_Type; + prec = GetPrecision(); + } // endif Buf_Type + + break; + case OP_MIN: + case OP_MAX: + type = Buf_Type; + lng = Long; + prec = GetPrecision(); + break; + case OP_CNC: + type = TYPE_STRING; + + if (IsTypeChar(Buf_Type)) { + lng = (Long) ? Long : 512; + prec = GetPrecision(); + } else + lng = 512; + + break; + default: + break; + } // endswitch Op + + return valp = AllocateValue(g, type, lng, prec); +} // end of GetCalcValue + +/*********************************************************************************/ +/* CalculateArray: */ +/*********************************************************************************/ +PVAL JSNX::CalculateArray(PGLOBAL g, PJAR arp, int n) +{ + int i, ars = arp->size(), nv = 0; + bool err; + OPVAL op = Nodes[n].Op; + PVAL val[2], vp = GetCalcValue(g, arp, n); + PVAL mulval = AllocateValue(g, vp); + PJVAL jvrp, jvp; + JVALUE jval; + + vp->Reset(); + + if (trace(1)) + htrc("CalculateArray size=%d op=%d\n", ars, op); + + for (i = 0; i < ars; i++) { + jvrp = arp->GetArrayValue(i); + + if (trace(1)) + htrc("i=%d nv=%d\n", i, nv); + + if (!jvrp->IsNull() || (op == OP_CNC && GetJsonNull())) { + if (jvrp->IsNull()) { + jvrp->SetString(g, GetJsonNull(), 0); + jvp = jvrp; + } else if (n < Nod - 1 && jvrp->GetJson()) { + jval.SetValue(g, GetColumnValue(g, jvrp->GetJson(), n + 1)); + jvp = &jval; + } else + jvp = jvrp; + + if (trace(1)) + htrc("jvp=%s null=%d\n", + jvp->GetString(g), jvp->IsNull() ? 1 : 0); + + if (!nv++) { + SetJsonValue(g, vp, jvp); + continue; + } else + SetJsonValue(g, mulval, jvp); + + if (!mulval->IsNull()) { + 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] = vp; + val[1] = mulval; + err = vp->Compute(g, val, 2, OP_ADD); + break; + default: + val[0] = vp; + val[1] = mulval; + err = vp->Compute(g, val, 2, op); + } // endswitch Op + + if (err) + vp->Reset(); + + if (trace(1)) { + char buf(32); + + htrc("vp='%s' err=%d\n", + vp->GetCharString(&buf), err ? 1 : 0); + } // endif trace + + } // endif Zero + + } // endif jvrp + + } // 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 + + return vp; +} // end of CalculateArray + +/*********************************************************************************/ +/* CheckPath: Checks whether the path exists in the document. */ +/*********************************************************************************/ +my_bool JSNX::CheckPath(PGLOBAL g) +{ + PJVAL val= NULL; + PJSON row = Row; + + for (int i = 0; i < Nod && row; i++) { + val = NULL; + + if (Nodes[i].Op == OP_NUM || Nodes[i].Op == OP_XX) { + } else switch (row->GetType()) { + case TYPE_JOB: + if (Nodes[i].Key) + val = ((PJOB)row)->GetKeyValue(Nodes[i].Key); + + break; + case TYPE_JAR: + if (!Nodes[i].Key) + if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE) + val = ((PJAR)row)->GetArrayValue(Nodes[i].Rank); + + break; + case TYPE_JVAL: + val = (PJVAL)row; + break; + default: + snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->GetType()); + } // endswitch Type + + if (i < Nod-1) + if (!(row = (val) ? val->GetJsp() : NULL)) + val = NULL; + + } // endfor i + + return (val != NULL); +} // end of CheckPath + +/***********************************************************************/ +/* GetRow: Set the complete path of the object to be set. */ +/***********************************************************************/ +PJSON JSNX::GetRow(PGLOBAL g) +{ + PJVAL val = NULL; + PJAR arp; + PJSON nwr, row = Row; + + for (int i = 0; i < Nod - 1 && row; i++) { + if (Nodes[i].Op == OP_XX) + break; + else switch (row->GetType()) { + case TYPE_JOB: + if (!Nodes[i].Key) + // Expected Array was not there, wrap the value + continue; + + val = ((PJOB)row)->GetKeyValue(Nodes[i].Key); + break; + case TYPE_JAR: + arp = (PJAR)row; + + if (!Nodes[i].Key) { + if (Nodes[i].Op == OP_EQ) + val = arp->GetArrayValue(Nodes[i].Rank); + else + val = arp->GetArrayValue(Nodes[i].Rx); + + } else { + // Unexpected array, unwrap it as [0] + val = arp->GetArrayValue(0); + i--; + } // endif Nodes + + break; + case TYPE_JVAL: + val = (PJVAL)row; + break; + default: + snprintf(g->Message, sizeof(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)->SetKeyValue(g, new(g)JVALUE(nwr), Nodes[i-1].Key); + } else if (row->GetType() == TYPE_JAR) { + ((PJAR)row)->AddArrayValue(g, new(g)JVALUE(nwr)); + ((PJAR)row)->InitArray(g); + } else { + snprintf(g->Message, sizeof(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 + +/***********************************************************************/ +/* WriteValue: */ +/***********************************************************************/ +my_bool JSNX::WriteValue(PGLOBAL g, PJVAL jvalp) +{ + PJOB objp = NULL; + PJAR arp = NULL; + PJVAL jvp = NULL; + PJSON row = GetRow(g); + + if (!row) + return true; + + 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: + snprintf(g->Message, sizeof(g->Message), "Invalid target type"); + return true; + } // endswitch Type + + if (arp) { + if (!Nodes[Nod-1].Key) { + if (Nodes[Nod-1].Op == OP_EQ) + arp->SetArrayValue(g, jvalp, Nodes[Nod-1].Rank); + else + arp->AddArrayValue(g, jvalp); + + arp->InitArray(g); + } // endif Key + + } else if (objp) { + if (Nodes[Nod-1].Key) + objp->SetKeyValue(g, jvalp, Nodes[Nod-1].Key); + + } else if (jvp) + jvp->SetValue(jvalp); + + return false; +} // end of WriteValue + +/*********************************************************************************/ +/* Locate a value in a JSON tree: */ +/*********************************************************************************/ +PSZ JSNX::Locate(PGLOBAL g, PJSON jsp, PJVAL jvp, int k) +{ + PSZ str = NULL; + my_bool err = true; + + g->Message[0] = 0; + + if (!jsp) { + snprintf(g->Message, sizeof(g->Message), "Null json tree"); + return NULL; + } // endif jsp + + try { + // Write to the path string + Jp = new(g) JOUTSTR(g); + Jp->WriteChr('$'); + Jvalp = jvp; + K = k; + + switch (jsp->GetType()) { + case TYPE_JAR: + err = LocateArray(g, (PJAR)jsp); + break; + case TYPE_JOB: + err = LocateObject(g, (PJOB)jsp); + break; + case TYPE_JVAL: + err = LocateValue(g, (PJVAL)jsp); + break; + default: + err = true; + } // endswitch Type + + if (err) { + if (!g->Message[0]) + snprintf(g->Message, sizeof(g->Message), "Invalid json tree"); + + } else if (Found) { + Jp->WriteChr('\0'); + PlugSubAlloc(g, NULL, Jp->N); + str = Jp->Strp; + } // endif's + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %s\n", n, g->Message); + + PUSH_WARNING(g->Message); + } catch (const char *msg) { + snprintf(g->Message, sizeof(g->Message), "%s", msg); + } // end catch + + return str; +} // end of Locate + +/*********************************************************************************/ +/* Locate in a JSON Array. */ +/*********************************************************************************/ +my_bool JSNX::LocateArray(PGLOBAL g, PJAR jarp) +{ + char s[16]; + size_t m = Jp->N; + + for (int i = 0; i < jarp->size() && !Found; i++) { + Jp->N = m; + snprintf(s, sizeof(s), "[%d]", i + B); + + if (Jp->WriteStr(s)) + return true; + + if (LocateValue(g, jarp->GetArrayValue(i))) + return true; + + } // endfor i + + return false; +} // end of LocateArray + +/*********************************************************************************/ +/* Locate in a JSON Object. */ +/*********************************************************************************/ +my_bool JSNX::LocateObject(PGLOBAL g, PJOB jobp) +{ + size_t m; + + if (Jp->WriteChr('.')) + return true; + + m = Jp->N; + + for (PJPR pair = jobp->First; pair && !Found; pair = pair->Next) { + Jp->N = m; + + if (Jp->WriteStr(pair->Key)) + return true; + + if (LocateValue(g, pair->Val)) + return true; + + } // endfor i + + return false; +} // end of LocateObject + +/*********************************************************************************/ +/* Locate a JSON Value. */ +/*********************************************************************************/ +my_bool JSNX::LocateValue(PGLOBAL g, PJVAL jvp) +{ + if (CompareTree(g, Jvalp, jvp)) + Found = (--K == 0); + else if (jvp->GetArray()) + return LocateArray(g, jvp->GetArray()); + else if (jvp->GetObject()) + return LocateObject(g, jvp->GetObject()); + + return false; +} // end of LocateValue + +/*********************************************************************************/ +/* Locate all occurrences of a value in a JSON tree: */ +/*********************************************************************************/ +PSZ JSNX::LocateAll(PGLOBAL g, PJSON jsp, PJVAL jvp, int mx) +{ + PSZ str = NULL; + my_bool err = true; + PJPN jnp; + + if (!jsp) { + snprintf(g->Message, sizeof(g->Message), "Null json tree"); + return NULL; + } // endif jsp + + try { + jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx); + memset(jnp, 0, sizeof(JPN) * mx); + g->Message[0] = 0; + + // Write to the path string + Jp = new(g)JOUTSTR(g); + Jvalp = jvp; + Imax = mx - 1; + Jpnp = jnp; + Jp->WriteChr('['); + + switch (jsp->GetType()) { + case TYPE_JAR: + err = LocateArrayAll(g, (PJAR)jsp); + break; + case TYPE_JOB: + err = LocateObjectAll(g, (PJOB)jsp); + break; + case TYPE_JVAL: + err = LocateValueAll(g, (PJVAL)jsp); + break; + default: + err = true; + } // endswitch Type + + if (!err) { + if (Jp->N > 1) + Jp->N--; + + Jp->WriteChr(']'); + Jp->WriteChr('\0'); + PlugSubAlloc(g, NULL, Jp->N); + str = Jp->Strp; + } else if (!g->Message[0]) + snprintf(g->Message, sizeof(g->Message), "Invalid json tree"); + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %s\n", n, g->Message); + + PUSH_WARNING(g->Message); + } catch (const char *msg) { + snprintf(g->Message, sizeof(g->Message), "%s", msg); + } // end catch + + return str; +} // end of LocateAll + +/*********************************************************************************/ +/* Locate in a JSON Array. */ +/*********************************************************************************/ +my_bool JSNX::LocateArrayAll(PGLOBAL g, PJAR jarp) +{ + if (I < Imax) { + Jpnp[++I].Type = TYPE_JAR; + + for (int i = 0; i < jarp->size(); i++) { + Jpnp[I].N = i; + + if (LocateValueAll(g, jarp->GetArrayValue(i))) + return true; + + } // endfor i + + I--; + } // endif I + + return false; +} // end of LocateArrayAll + +/*********************************************************************************/ +/* Locate in a JSON Object. */ +/*********************************************************************************/ +my_bool JSNX::LocateObjectAll(PGLOBAL g, PJOB jobp) +{ + if (I < Imax) { + Jpnp[++I].Type = TYPE_JOB; + + for (PJPR pair = jobp->First; pair; pair = pair->Next) { + Jpnp[I].Key = pair->Key; + + if (LocateValueAll(g, pair->Val)) + return true; + + } // endfor i + + I--; + } // endif I + + return false; +} // end of LocateObjectAll + +/*********************************************************************************/ +/* Locate a JSON Value. */ +/*********************************************************************************/ +my_bool JSNX::LocateValueAll(PGLOBAL g, PJVAL jvp) +{ + if (CompareTree(g, Jvalp, jvp)) + return AddPath(); + else if (jvp->GetArray()) + return LocateArrayAll(g, jvp->GetArray()); + else if (jvp->GetObject()) + return LocateObjectAll(g, jvp->GetObject()); + + return false; +} // end of LocateValueAll + +/*********************************************************************************/ +/* Compare two JSON trees. */ +/*********************************************************************************/ +my_bool JSNX::CompareTree(PGLOBAL g, PJSON jp1, PJSON jp2) +{ + if (!jp1 || !jp2 || jp1->GetType() != jp2->GetType() + || jp1->size() != jp2->size()) + return false; + + my_bool found = true; + + if (jp1->GetType() == TYPE_JVAL) { +// PVL v1 = ((PJVAL)jp1)->GetVal(), v2 = ((PJVAL)jp2)->GetVal(); + + if (((PJVAL)jp1)->DataType == TYPE_JSON && ((PJVAL)jp2)->DataType == TYPE_JSON) + found = CompareTree(g, jp1->GetJsp(), jp2->GetJsp()); + else + found = CompareValues(((PJVAL)jp1), ((PJVAL)jp2)); + + } else if (jp1->GetType() == TYPE_JAR) { + for (int i = 0; found && i < jp1->size(); i++) + found = (CompareTree(g, jp1->GetArrayValue(i), jp2->GetArrayValue(i))); + + } else if (jp1->GetType() == TYPE_JOB) { + PJPR p1 = jp1->GetFirst(), p2 = jp2->GetFirst(); + + for (; found && p1 && p2; p1 = p1->Next, p2 = p2->Next) + found = CompareTree(g, p1->Val, p2->Val); + + } else + found = false; + + return found; +} // end of CompareTree + +/*********************************************************************************/ +/* Compare two VAL values and return true if they are equal. */ +/*********************************************************************************/ +my_bool JSNX::CompareValues(PJVAL v1, PJVAL v2) +{ + my_bool b = false; + + switch (v1->DataType) { + case TYPE_STRG: + if (v2->DataType == TYPE_STRG) { + if (v1->Nd || v2->Nd) // Case insensitive + b = (!stricmp(v1->Strp, v2->Strp)); + else + b = (!strcmp(v1->Strp, v2->Strp)); + + } // endif Type + + break; + case TYPE_DTM: + if (v2->DataType == TYPE_DTM) + b = (!strcmp(v1->Strp, v2->Strp)); + + break; + case TYPE_INTG: + if (v2->DataType == TYPE_INTG) + b = (v1->N == v2->N); + else if (v2->DataType == TYPE_BINT) + b = (v1->N == v2->LLn); + + break; + case TYPE_BINT: + if (v2->DataType == TYPE_INTG) + b = (v1->LLn == v2->N); + else if (v2->DataType == TYPE_BINT) + b = (v1->LLn == v2->LLn); + + break; + case TYPE_DBL: + if (v2->DataType == TYPE_DBL) + b = (v1->F == v2->F); + + break; + case TYPE_BOOL: + if (v2->DataType == TYPE_BOOL) + b = (v1->B == v2->B); + + break; + case TYPE_NULL: + if (v2->DataType == TYPE_NULL) + b = true; + + break; + default: + break; + } // endswitch Type + + return b; +} // end of CompareValues + +/*********************************************************************************/ +/* Add the found path to the list. */ +/*********************************************************************************/ +my_bool JSNX::AddPath(void) { + char s[16]; + + if (Jp->WriteStr("\"$")) + return true; + + for (int i = 0; i <= I; i++) { + if (Jpnp[i].Type == TYPE_JAR) { + snprintf(s, sizeof(s), "[%d]", Jpnp[i].N + B); + + if (Jp->WriteStr(s)) + return true; + + } else { + if (Jp->WriteChr('.')) + return true; + + if (Jp->WriteStr(Jpnp[i].Key)) + return true; + + } // endif's + + } // endfor i + + if (Jp->WriteStr("\",")) + return true; + + return false; +} // end of AddPath + +/* --------------------------------- JSON UDF ---------------------------------- */ + +/*********************************************************************************/ +/* Allocate and initialize a BSON structure. */ +/*********************************************************************************/ +PBSON JbinAlloc(PGLOBAL g, UDF_ARGS *args, ulong len, PJSON jsp) +{ + PBSON bsp = (PBSON)PlgDBSubAlloc(g, NULL, sizeof(BSON)); + + if (bsp) { + strcpy(bsp->Msg, "Binary Json"); + bsp->Msg[BMX] = 0; + bsp->Filename = NULL; + bsp->G = g; + bsp->Pretty = 2; + bsp->Reslen = len; + bsp->Changed = false; + bsp->Top = bsp->Jsp = jsp; + bsp->Bsp = (args && IsJson(args, 0) == 3) ? (PBSON)args->args[0] : NULL; + } else + PUSH_WARNING(g->Message); + + return bsp; +} /* end of JbinAlloc */ + +/*********************************************************************************/ +/* Set the BSON chain as changed. */ +/*********************************************************************************/ +static void SetChanged(PBSON bsp) +{ + if (bsp->Bsp) + SetChanged(bsp->Bsp); + + bsp->Changed = true; +} /* end of SetChanged */ + +/*********************************************************************************/ +/* Replaces GetJsonGrpSize not usable when CONNECT is not installed. */ +/*********************************************************************************/ +uint GetJsonGroupSize(void) +{ + return (JsonGrpSize) ? JsonGrpSize : GetJsonGrpSize(); +} // end of GetJsonGroupSize + +/*********************************************************************************/ +/* Program for SubSet re-initialization of the memory pool. */ +/*********************************************************************************/ +my_bool JsonSubSet(PGLOBAL g, my_bool b) +{ + PPOOLHEADER pph = (PPOOLHEADER)g->Sarea; + + pph->To_Free = (g->Saved_Size) ? g->Saved_Size : sizeof(POOLHEADER); + pph->FreeBlk = g->Sarea_Size - pph->To_Free; + + if (b) + g->Saved_Size = 0; + + return FALSE; +} /* end of JsonSubSet */ + +/*********************************************************************************/ +/* Program for saving the status of the memory pools. */ +/*********************************************************************************/ +inline void JsonMemSave(PGLOBAL g) +{ + g->Saved_Size = ((PPOOLHEADER)g->Sarea)->To_Free; +} /* end of JsonMemSave */ + +/*********************************************************************************/ +/* Program for freeing the memory pools. */ +/*********************************************************************************/ +inline void JsonFreeMem(PGLOBAL g) +{ + g->Activityp = NULL; + g = PlugExit(g); +} /* end of JsonFreeMem */ + +/*********************************************************************************/ +/* SubAlloc a new JSON item with protection against memory exhaustion. */ +/*********************************************************************************/ +static PJSON JsonNew(PGLOBAL g, JTYP type) +{ + PJSON jsp = NULL; + + try { + switch (type) { + case TYPE_JAR: + jsp = new(g) JARRAY; + break; + case TYPE_JOB: + jsp = new(g) JOBJECT; + break; + default: + break; + } // endswitch type + + } catch (...) { + if (trace(1023)) + htrc("%s\n", g->Message); + + PUSH_WARNING(g->Message); + } // end try/catch + + return jsp; +} /* end of JsonNew */ + +/*********************************************************************************/ +/* SubAlloc a new JValue with protection against memory exhaustion. */ +/*********************************************************************************/ +static PJVAL JvalNew(PGLOBAL g, JTYP type, void *vp) +{ + PJVAL jvp = NULL; + + try { + if (!vp) + jvp = new (g) JVALUE; + else switch (type) { + case TYPE_JSON: + case TYPE_JVAL: + case TYPE_JAR: + case TYPE_JOB: + jvp = new(g) JVALUE((PJSON)vp); + break; +// case TYPE_VAL: +// jvp = new(g) JVALUE(g, (PVAL)vp); +// break; + case TYPE_DTM: + case TYPE_STRG: + jvp = new(g) JVALUE(g, (PCSZ)vp); + break; + default: + break; + } // endswitch type + + } catch (...) { + if (trace(1023)) + htrc("%s\n", g->Message); + + PUSH_WARNING(g->Message); + } // end try/catch + + return jvp; +} /* end of JvalNew */ + +/*********************************************************************************/ +/* Allocate and initialise the memory area. */ +/*********************************************************************************/ +my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args, char *message, my_bool mbn, + unsigned long reslen, unsigned long memlen, unsigned long more) +{ + PGLOBAL g = PlugInit(NULL, (size_t)memlen + more + 500); // +500 to avoid CheckMem + + if (!g) { + strcpy(message, "Allocation error"); + return true; + } else if (g->Sarea_Size == 0) { + strcpy(message, g->Message); + g = PlugExit(g); + return true; + } // endif g + + g->Mrr = (args->arg_count && args->args[0]) ? 1 : 0; + g->More = more; + initid->maybe_null = mbn; + initid->max_length = reslen; + initid->ptr = (char*)g; + return false; +} // end of JsonInit + +/*********************************************************************************/ +/* Check if a path was specified and set jvp according to it. */ +/*********************************************************************************/ +static my_bool CheckPath(PGLOBAL g, UDF_ARGS *args, PJSON jsp, PJVAL& jvp, int n) +{ + for (uint i = n; i < args->arg_count; i++) + if (args->arg_type[i] == STRING_RESULT && args->args[i]) { + // A path to a subset of the json tree is given + char *path = MakePSZ(g, args, i); + + if (path) { + PJSNX jsx = new(g)JSNX(g, jsp, TYPE_STRING); + + if (jsx->SetJpath(g, path)) + return true; + + if (!(jvp = jsx->GetJson(g))) { + snprintf(g->Message, sizeof(g->Message), "No sub-item at '%s'", path); + return true; + } // endif jvp + + } else { + snprintf(g->Message, sizeof(g->Message), "Path argument is null"); + return true; + } // endif path + + 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, uint n = 2) +{ + char *str; + + if (IsJson(args, 0) == 2) { + // Make the change in the json file + 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 (!Serialize(g, top, MakePSZ(g, args, 0), pretty)) + PUSH_WARNING(g->Message); + + str = NULL; + } else if (IsJson(args, 0) == 3) { + PBSON bsp = (PBSON)args->args[0]; + + if (bsp->Filename) { + // Make the change in the json file + if (!Serialize(g, top, bsp->Filename, bsp->Pretty)) + PUSH_WARNING(g->Message); + + str = bsp->Filename; + } else if (!(str = Serialize(g, top, NULL, 0))) + PUSH_WARNING(g->Message); + + SetChanged(bsp); + } else if (!(str = Serialize(g, top, NULL, 0))) + PUSH_WARNING(g->Message); + + return str; +} // end of MakeResult + +/*********************************************************************************/ +/* Make the binary result according to the first argument type. */ +/*********************************************************************************/ +static PBSON MakeBinResult(PGLOBAL g, UDF_ARGS *args, PJSON top, ulong len, int n = 2) +{ + PBSON bsnp = JbinAlloc(g, args, len, top); + + if (!bsnp) + return NULL; + + if (IsJson(args, 0) == 2) { + int pretty = 0; + + 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 + + bsnp->Pretty = pretty; + + if ((bsnp->Filename = (char*)args->args[0])) { + bsnp->Filename = MakePSZ(g, args, 0); + strmake(bsnp->Msg, bsnp->Filename, BMX-1); + } else + strmake(bsnp->Msg, "null filename", BMX-1); + + } else if (IsJson(args, 0) == 3) { + PBSON bsp = (PBSON)args->args[0]; + + if (bsp->Filename) { + bsnp->Filename = bsp->Filename; + strmake(bsnp->Msg, bsp->Filename, BMX-1); + bsnp->Pretty = bsp->Pretty; + } else + strcpy(bsnp->Msg, "Json Binary item"); + + } else + strcpy(bsnp->Msg, "Json Binary item"); + + return bsnp; +} // end of MakeBinResult + +/*********************************************************************************/ +/* Returns a pointer to the first integer argument found from the nth argument. */ +/*********************************************************************************/ +static int *GetIntArgPtr(PGLOBAL g, UDF_ARGS *args, uint& n) +{ + int *x = NULL; + + for (uint i = n; i < args->arg_count; i++) + if (args->arg_type[i] == INT_RESULT) { + if (args->args[i]) { + if ((x = (int*)PlgDBSubAlloc(g, NULL, sizeof(int)))) + *x = (int)*(longlong*)args->args[i]; + else + PUSH_WARNING(g->Message); + + } // endif args + + n = i + 1; + break; + } // endif arg_type + + return x; +} // end of GetIntArgPtr + +/*********************************************************************************/ +/* Returns not 0 if the argument is a JSON item or file name. */ +/*********************************************************************************/ +int IsJson(UDF_ARGS *args, uint i, bool b) +{ + const char *pat = args->attributes[i]; + int n = 0; + + if (*pat == '@') { + pat++; + + if (*pat == '\'' || *pat == '"') + pat++; + + } // endif pat + + if (i >= args->arg_count || args->arg_type[i] != STRING_RESULT) { + } else if (!strnicmp(pat, "Json_", 5)) { + if (!args->args[i] || strchr("[{ \t\r\n", *args->args[i])) + n = 1; // arg should be is a json item + else + n = 2; // A file name may have been returned + + } else if (!strnicmp(pat, "Jbin_", 5)) { + if (args->lengths[i] == sizeof(BSON)) + n = 3; // arg is a binary json item + else + n = 2; // A file name may have been returned + + } else if (!strnicmp(pat, "Jfile_", 6)) { + n = 2; // arg is a json file name + } else if (b) { + char *sap; + PGLOBAL g = PlugInit(NULL, (size_t)args->lengths[i] * M + 1024); + +// JsonSubSet(g); + sap = MakePSZ(g, args, i); + + if (ParseJson(g, sap, strlen(sap))) + n = 4; + + JsonFreeMem(g); + } // endif's + + return n; +} // end of IsJson + +/*********************************************************************************/ +/* GetMemPtr: returns the memory pointer used by this argument. */ +/*********************************************************************************/ +static PGLOBAL GetMemPtr(PGLOBAL g, UDF_ARGS *args, uint i) +{ + return (IsJson(args, i) == 3) ? ((PBSON)args->args[i])->G : g; +} // end of GetMemPtr + +/*********************************************************************************/ +/* 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. */ +/*********************************************************************************/ +my_bool CalcLen(UDF_ARGS *args, my_bool obj, unsigned long& reslen, + unsigned long& memlen, my_bool mod) +{ + char fn[_MAX_PATH]; + unsigned long i, k, m, n; + long fl = 0, j = -1; + + reslen = args->arg_count + 2; + + // Calculate the result max length + for (i = 0; i < args->arg_count; i++) { + n = IsJson(args, i); + + if (obj) { + if (!(k = args->attribute_lengths[i])) + k = strlen(args->attributes[i]); + + reslen += (k + 3); // For quotes and : + } // endif obj + + switch (args->arg_type[i]) { + case STRING_RESULT: + if (n == 2 && args->args[i]) { + if (!mod) { + m = MY_MIN(args->lengths[i], sizeof(fn) - 1); + memcpy(fn, args->args[i], m); + fn[m] = 0; + j = i; + fl = GetFileLength(fn); + reslen += fl; + } else + reslen += args->lengths[i]; + + } else if (n == 3 && args->args[i]) + reslen += ((PBSON)args->args[i])->Reslen; + else if (n == 1) + reslen += args->lengths[i]; + else + reslen += (args->lengths[i] + 1) * 2; // Pessimistic ! + + break; + case INT_RESULT: + reslen += 20; + break; + case REAL_RESULT: + reslen += 31; + break; + case DECIMAL_RESULT: + reslen += (args->lengths[i] + 7); // 6 decimals + break; + case TIME_RESULT: + case ROW_RESULT: + default: + // What should we do here ? + break; + } // endswitch arg_type + + } // endfor i + + // Calculate the amount of memory needed + memlen = MEMFIX + sizeof(JOUTSTR) + reslen; + + for (i = 0; i < args->arg_count; i++) { + n = IsJson(args, i); + memlen += (args->lengths[i] + sizeof(JVALUE)); + + if (obj) { + if (!(k = args->attribute_lengths[i])) + k = strlen(args->attributes[i]); + + memlen += (k + sizeof(JOBJECT) + sizeof(JPAIR)); + } else + memlen += sizeof(JARRAY); + + switch (args->arg_type[i]) { + case STRING_RESULT: + if (n == 2 && args->args[i]) { + if ((signed)i != j) { + m = MY_MIN(args->lengths[i], sizeof(fn) - 1); + memcpy(fn, args->args[i], m); + fn[m] = 0; + j = -1; + fl = GetFileLength(fn); + } // endif i + + memlen += fl * M; + } else if (n == 1) { + if (i == 0) + memlen += sizeof(BSON); // For Jbin functions + + memlen += args->lengths[i] * M; // Estimate parse memory + } else if (n == 3) + memlen += sizeof(JVALUE); + + memlen += sizeof(TYPVAL<PSZ>); + break; + case INT_RESULT: + memlen += sizeof(TYPVAL<int>); + break; + case REAL_RESULT: + case DECIMAL_RESULT: + memlen += sizeof(TYPVAL<double>); + break; + case TIME_RESULT: + case ROW_RESULT: + default: + // What should we do here ? + break; + } // endswitch arg_type + + } // endfor i + + return false; +} // end of CalcLen + +/*********************************************************************************/ +/* Check if the calculated memory is enough. */ +/*********************************************************************************/ +my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n, + my_bool m, my_bool obj, my_bool mod) +{ + unsigned long rl, ml; + my_bool b = false; + + n = MY_MIN(n, args->arg_count); + + for (uint i = 0; i < n; i++) + if (IsJson(args, i) == 2 || + (b = (m && !i && args->arg_type[0] == STRING_RESULT && !IsJson(args, 0)))) { + if (CalcLen(args, obj, rl, ml, mod)) + return true; + else if (b) { + ulong len; + char *p = args->args[0]; + + // Is this a file name? + if (p && !strchr("[{ \t\r\n", *p) && (len = GetFileLength(p))) + ml += len * (M + 1); + else + ml += args->lengths[0] * M; + + } // endif b + + ml += g->More; + + if (ml > g->Sarea_Size) { + FreeSarea(g); + + if (AllocSarea(g, ml)) { + char errmsg[MAX_STR]; + + snprintf(errmsg, sizeof(errmsg) - 1, MSG(WORK_AREA), g->Message); + snprintf(g->Message, sizeof(g->Message), "%s", errmsg); + return true; + } // endif SareaAlloc + + g->Saved_Size = 0; + g->Xchk = NULL; + initid->max_length = rl; + } // endif Size + + break; + } // endif IsJson + + JsonSubSet(g); + return false; +} // end of CheckMemory + +/*********************************************************************************/ +/* Make a zero terminated string from the passed argument. */ +/*********************************************************************************/ +PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i) +{ + if (args->arg_count > (unsigned)i && args->args[i]) { + int n = args->lengths[i]; + PSZ s = (PSZ)PlgDBSubAlloc(g, NULL, n + 1); + + if (s) { + memcpy(s, args->args[i], n); + s[n] = 0; + } else + PUSH_WARNING(g->Message); + + return s; + } else + return NULL; + +} // end of MakePSZ + +/*********************************************************************************/ +/* Make a valid key from the passed argument. */ +/*********************************************************************************/ +static PCSZ MakeKey(PGLOBAL g, UDF_ARGS *args, int i) +{ + if (args->arg_count > (unsigned)i) { + int j = 0, n = args->attribute_lengths[i]; + my_bool b; // true if attribute is zero terminated + PSZ p; + PCSZ s = args->attributes[i]; + + if (s && *s && (n || *s == '\'')) { + if ((b = (!n || !s[n]))) + n = strlen(s); + + if (IsJson(args, i)) + j = (int)(strchr(s, '_') - s + 1); + + if (j && n > j) { + s += j; + n -= j; + } else if (*s == '\'' && s[n-1] == '\'') { + s++; + n -= 2; + b = false; + } // endif *s + + if (n < 1) + return (PCSZ) "Key"; + + if (!b) { + if ((p = (PSZ)PlgDBSubAlloc(g, NULL, n + 1))) { + memcpy(p, s, n); + p[n] = 0; + } else + PUSH_WARNING(g->Message); + + s = p; + } // endif b + + } // endif s + + return s; + } // endif count + + return (PCSZ) "Key"; +} // end of MakeKey + +/*********************************************************************************/ +/* Parse a json file. */ +/*********************************************************************************/ +static PJSON ParseJsonFile(PGLOBAL g, char *fn, int *pretty, size_t& len) +{ + char *memory; + HANDLE hFile; + MEMMAP mm; + PJSON jsp; + + /*******************************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************************/ + hFile = CreateFileMap(g, fn, &mm, MODE_READ, false); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR), "map", (int)rc, fn); + + return NULL; + } // endif hFile + + /*******************************************************************************/ + /* Get the file size. */ + /*******************************************************************************/ + len = (size_t)mm.lenL; + + if (mm.lenH) + len += ((size_t)mm.lenH * 0x000000001LL); + + memory = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + return NULL; + } // endif len + + if (!memory) { + CloseFileHandle(hFile); + snprintf(g->Message, sizeof(g->Message), MSG(MAP_VIEW_ERROR), fn, GetLastError()); + return NULL; + } // endif Memory + + CloseFileHandle(hFile); // Not used anymore + + /*********************************************************************************/ + /* Parse the json file and allocate its tree structure. */ + /*********************************************************************************/ + g->Message[0] = 0; + jsp = ParseJson(g, memory, len, pretty); + CloseMemMap(memory, len); + return jsp; +} // end of ParseJsonFile + +/*********************************************************************************/ +/* Return a json file contains. */ +/*********************************************************************************/ +char *GetJsonFile(PGLOBAL g, char *fn) +{ + char *str; + int h, n, len; + +#if defined(UNIX) || defined(UNIV_LINUX) + h= open(fn, O_RDONLY); +#else + h= open(fn, _O_RDONLY, _O_TEXT); +#endif + + if (h == -1) { + snprintf(g->Message, sizeof(g->Message), "Error %d opening %-.1024s", errno, fn); + return NULL; + } // endif h + + if ((len = _filelength(h)) < 0) { + snprintf(g->Message, sizeof(g->Message), MSG(FILELEN_ERROR), "_filelength", fn); + close(h); + return NULL; + } // endif len + + if ((str = (char*)PlgDBSubAlloc(g, NULL, len + 1))) { + if ((n = read(h, str, len)) < 0) { + snprintf(g->Message, sizeof(g->Message), "Error %d reading %d bytes from %-.1024s", errno, len, fn); + return NULL; + } // endif n + + str[n] = 0; + close(h); + } // endif str + + return str; +} // end of GetJsonFile + +/*********************************************************************************/ +/* Make a JSON value from the passed argument. */ +/*********************************************************************************/ +static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, uint i, PJSON *top = NULL) +{ + char *sap = (args->arg_count > i) ? args->args[i] : NULL; + int n, len; + int ci; + long long bigint; + PJSON jsp; + PJVAL jvp = new(g) JVALUE; + + if (top) + *top = NULL; + + if (sap) switch (args->arg_type[i]) { + case STRING_RESULT: + if ((len = args->lengths[i])) { + if ((n = IsJson(args, i)) < 3) + sap = MakePSZ(g, args, i); + + if (n) { + if (n == 3) { + if (top) + *top = ((PBSON)sap)->Top; + + jsp = ((PBSON)sap)->Jsp; + } else { + if (n == 2) { + if (!(sap = GetJsonFile(g, sap))) { + PUSH_WARNING(g->Message); + return jvp; + } // endif sap + + len = strlen(sap); + } // endif n + + if (!(jsp = ParseJson(g, sap, strlen(sap)))) + PUSH_WARNING(g->Message); + else if (top) + *top = jsp; + + } // endif's n + + if (jsp && jsp->GetType() == TYPE_JVAL) + jvp = (PJVAL)jsp; + else + jvp->SetValue(jsp); + + } else { + ci = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1; + jvp->SetString(g, sap, ci); + } // endif n + + } // endif len + + break; + case INT_RESULT: + bigint = *(long long*)sap; + + if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) || + (bigint == 1LL && !strcmp(args->attributes[i], "TRUE"))) + jvp->SetBool(g, (char)bigint); + else + jvp->SetBigint(g, bigint); + + break; + case REAL_RESULT: + jvp->SetFloat(g, *(double*)sap); + break; + case DECIMAL_RESULT: + jvp->SetFloat(g, atof(MakePSZ(g, args, i))); + break; + case TIME_RESULT: + case ROW_RESULT: + default: + break; + } // endswitch arg_type + + return jvp; +} // end of MakeValue + +/*********************************************************************************/ +/* Try making a JSON value of the passed type from the passed argument. */ +/*********************************************************************************/ +static PJVAL MakeTypedValue(PGLOBAL g, UDF_ARGS *args, uint i, + JTYP type, PJSON *top = NULL) +{ + char *sap; + PJSON jsp; + PJVAL jvp = MakeValue(g, args, i, top); + + //if (type == TYPE_JSON) { + // if (jvp->GetValType() >= TYPE_JSON) + // return jvp; + + //} else if (jvp->GetValType() == type) + // return jvp; + + if (jvp->GetValType() == TYPE_STRG) { + sap = jvp->GetString(g); + + if ((jsp = ParseJson(g, sap, strlen(sap)))) { + if ((type == TYPE_JSON && jsp->GetType() != TYPE_JVAL) || jsp->GetType() == type) { + if (top) + *top = jsp; + + jvp->SetValue(jsp); + } // endif Type + + } // endif jsp + + } // endif Type + + return jvp; +} // end of MakeTypedValue + +/* ------------------------------ The JSON UDF's ------------------------------- */ + +/*********************************************************************************/ +/* Make a Json value containing the parameter. */ +/*********************************************************************************/ +my_bool jsonvalue_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count > 1) { + strcpy(message, "Cannot accept more than 1 argument"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jsonvalue_init + +char *jsonvalue(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + char *str; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + 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); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); + return str; +} // end of JsonValue + +void jsonvalue_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonvalue_deinit + +/*********************************************************************************/ +/* Make a Json array containing all the parameters. */ +/*********************************************************************************/ +my_bool json_make_array_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, false, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_make_array_init + +char *json_make_array(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + char *str; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, false)) { + PJAR arp = new(g)JARRAY; + + for (uint i = 0; i < args->arg_count; i++) + arp->AddArrayValue(g, MakeValue(g, args, i)); + + arp->InitArray(g); + + if (!(str = Serialize(g, arp, NULL, 0))) + str = strcpy(result, g->Message); + + } else + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); + return str; +} // end of json_make_array + +void json_make_array_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_make_array_deinit + +/*********************************************************************************/ +/* Add one or several values to a Json array. */ +/*********************************************************************************/ +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, "This function must have at least 2 arguments"); + return true; + //} else if (!IsJson(args, 0, true)) { + // strcpy(message, "First argument must be a valid json string or item"); + // return true; + } else + CalcLen(args, false, reslen, memlen); + + if (!JsonInit(initid, args, message, true, reslen, memlen)) { + PGLOBAL g = (PGLOBAL)initid->ptr; + + // This is a constant function + g->N = (initid->const_item) ? 1 : 0; + + // This is to avoid double execution when using prepared statements + if (IsJson(args, 0) > 1) + initid->const_item = 0; + + return false; + } else + return true; + +} // end of json_array_add_values_init + +char *json_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, true)) { + PJSON top; + PJAR arp; + PJVAL jvp = MakeTypedValue(g, args, 0, TYPE_JAR, &top); + + if (jvp->GetValType() != TYPE_JAR) { + arp = new(g)JARRAY; + arp->AddArrayValue(g, jvp); + top = arp; + } else + arp = jvp->GetArray(); + + for (uint i = 1; i < args->arg_count; i++) + arp->AddArrayValue(g, MakeValue(g, args, i)); + + arp->InitArray(g); + str = MakeResult(g, args, top, args->arg_count); + } // endif CheckMemory + + if (!str) { + PUSH_WARNING(g->Message); + str = args->args[0]; + } // endif str + + // Keep result of constant function + g->Xchk = (g->N) ? str : NULL; + } else + str = (char*)g->Xchk; + + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of json_array_add_values + +void json_array_add_values_deinit(UDF_INIT* initid) +{ + JsonFreeMem((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, "This function must have at least 2 arguments"); + return true; + //} else if (!IsJson(args, 0, true)) { + // strcpy(message, "First argument is not a valid Json item"); + // return true; + } else + CalcLen(args, false, reslen, memlen, true); + + if (!JsonInit(initid, args, message, true, reslen, memlen)) { + PGLOBAL g = (PGLOBAL)initid->ptr; + + // This is a constant function + g->N = (initid->const_item) ? 1 : 0; + + // This is to avoid double execution when using prepared statements + if (IsJson(args, 0) > 1) + initid->const_item = 0; + + return false; + } else + return true; + +} // end of json_array_add_init + +char *json_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + goto fin; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 2, false, false, true)) { + int *x; + uint n = 2; + PJSON jsp, top; + PJVAL jvp; + PJAR arp; + + jvp = MakeTypedValue(g, args, 0, TYPE_JSON, &top); + jsp = jvp->GetJson(); + x = GetIntArgPtr(g, args, n); + + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp) { + PGLOBAL gb = GetMemPtr(g, args, 0); + + if (jvp->GetValType() != TYPE_JAR) { + if ((arp = (PJAR)JsonNew(gb, TYPE_JAR))) { + arp->AddArrayValue(gb, JvalNew(gb, TYPE_JVAL, jvp)); + jvp->SetValue(arp); + + if (!top) + top = arp; + + } // endif arp + + } else + arp = jvp->GetArray(); + + if (arp) { + arp->AddArrayValue(gb, MakeValue(gb, args, 1), x); + arp->InitArray(gb); + str = MakeResult(g, args, top, n); + } else + PUSH_WARNING(gb->Message); + + } else { + PUSH_WARNING("Target is not an array"); + // if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (g->N) + // Keep result of constant function + g->Xchk = str; + + fin: + if (!str) { + *res_length = 0; + *is_null = 1; + *error = 1; + } else + *res_length = strlen(str); + + return str; +} // end of json_array_add + +void json_array_add_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_add_deinit + +/*********************************************************************************/ +/* Delete a value from a Json array. */ +/*********************************************************************************/ +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, "This function must have at least 2 arguments"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + if (!JsonInit(initid, args, message, true, reslen, memlen)) { + PGLOBAL g = (PGLOBAL)initid->ptr; + + // This is a constant function + g->N = (initid->const_item) ? 1 : 0; + + // This is to avoid double execution when using prepared statements + if (IsJson(args, 0) > 1) + initid->const_item = 0; + + return false; + } else + return true; + +} // end of json_array_delete_init + +char *json_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + goto fin; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 1, false, false, true)) { + int *x; + uint n = 1; + PJSON top; + PJAR arp; + PJVAL jvp = MakeTypedValue(g, args, 0, TYPE_JSON, &top); + + if (!(x = GetIntArgPtr(g, args, n))) + PUSH_WARNING("Missing or null array index"); + else if (CheckPath(g, args, jvp->GetJson(), jvp, 1)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JAR) { + arp = jvp->GetArray(); + arp->DeleteValue(*x); + arp->InitArray(GetMemPtr(g, args, 0)); + str = MakeResult(g, args, top, n); + } else { + PUSH_WARNING("First argument target is not an array"); +// if (g->Mrr) *error = 1; + } // endif jvp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (g->N) + // Keep result of constant function + g->Xchk = str; + + fin: + if (!str) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_array_delete + +void json_array_delete_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_delete_deinit + +/*********************************************************************************/ +/* Sum big integer values from a Json array. */ +/*********************************************************************************/ +my_bool jsonsum_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more; + + if (args->arg_count != 1) { + strcpy(message, "This function must have 1 argument"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + // TODO: calculate this + more = (IsJson(args, 0) != 3) ? 1000 : 0; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jsonsum_int_init + +long long jsonsum_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) +{ + long long n = 0LL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + *is_null = 1; + return 0LL; + } else + return *(long long*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false, false, true)) { + PJVAL jvp = MakeValue(g, args, 0); + + if (jvp && jvp->GetValType() == TYPE_JAR) { + PJAR arp = jvp->GetArray(); + + for (int i = 0; i < arp->size(); i++) + n += arp->GetArrayValue(i)->GetBigint(); + + } else { + PUSH_WARNING("First argument target is not an array"); + } // endif jvp + + } else { + *error = 1; + n = -1LL; + } // end of CheckMemory + + if (g->N) { + // Keep result of constant function + long long *np; + + if ((np = (long long*)PlgDBSubAlloc(g, NULL, sizeof(long long)))) { + *np = n; + g->Activityp = (PACTIVITY)np; + } else + PUSH_WARNING(g->Message); + + } // endif const_item + + return n; +} // end of jsonsum_int + +void jsonsum_int_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonsum_int_deinit + +/*********************************************************************************/ +/* Sum big integer values from a Json array. */ +/*********************************************************************************/ +my_bool jsonsum_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more; + + if (args->arg_count != 1) { + strcpy(message, "This function must have 1 argument"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + // TODO: calculate this + more = (IsJson(args, 0) != 3) ? 1000 : 0; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jsonsum_real_init + +double jsonsum_real(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) +{ + double n = 0.0; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + *is_null = 1; + return 0.0; + } else + return *(double*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false, false, true)) { + PJVAL jvp = MakeValue(g, args, 0); + + if (jvp && jvp->GetValType() == TYPE_JAR) { + PJAR arp = jvp->GetArray(); + + for (int i = 0; i < arp->size(); i++) + n += arp->GetArrayValue(i)->GetFloat(); + + } else { + PUSH_WARNING("First argument target is not an array"); + } // endif jvp + + } else { + *error = 1; + n = -1.0; + } // endif CheckMemory + + if (g->N) { + // Keep result of constant function + double *np; + + if ((np = (double*)PlgDBSubAlloc(g, NULL, sizeof(double)))) { + *np = n; + g->Activityp = (PACTIVITY)np; + } else { + PUSH_WARNING(g->Message); + *error = 1; + n = -1.0; + } // endif np + + } // endif const_item + + return n; +} // end of jsonsum_real + +void jsonsum_real_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonsum_real_deinit + +/*********************************************************************************/ +/* Returns the average of big integer values from a Json array. */ +/*********************************************************************************/ +my_bool jsonavg_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return jsonsum_real_init(initid, args, message); +} // end of jsonavg_real_init + +double jsonavg_real(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) +{ + double n = 0.0; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + *is_null = 1; + return 0.0; + } else + return *(double*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false, false, true)) { + PJVAL jvp = MakeValue(g, args, 0); + + if (jvp && jvp->GetValType() == TYPE_JAR) { + PJAR arp = jvp->GetArray(); + + if (arp->size()) { + for (int i = 0; i < arp->size(); i++) + n += arp->GetArrayValue(i)->GetFloat(); + + n /= arp->size(); + } // endif size + + } else { + PUSH_WARNING("First argument target is not an array"); + } // endif jvp + + } else { + *error = 1; + n = -1.0; + } // endif CheckMemory + + if (g->N) { + // Keep result of constant function + double *np; + + if ((np = (double*)PlgDBSubAlloc(g, NULL, sizeof(double)))) { + *np = n; + g->Activityp = (PACTIVITY)np; + } else { + *error = 1; + n = -1.0; + } // endif np + + } // endif const_item + + return n; +} // end of jsonavg_real + +void jsonavg_real_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonavg_real_deinit + +/*********************************************************************************/ +/* Make a Json Object containing all the parameters. */ +/*********************************************************************************/ +my_bool json_make_object_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_make_object_init + +char *json_make_object(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, false, false, true)) { + PJOB objp; + + if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) { + for (uint i = 0; i < args->arg_count; i++) + objp->SetKeyValue(g, MakeValue(g, args, i), MakeKey(g, args, i)); + + str = Serialize(g, objp, NULL, 0); + } // endif objp + + } // endif CheckMemory + + if (!str) + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); + return str; +} // end of json_make_object + +void json_make_object_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_make_object_deinit + +/*********************************************************************************/ +/* Make a Json Object 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, args, 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 = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, false, true)) { + PJVAL jvp; + PJOB objp; + + if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) { + for (uint i = 0; i < args->arg_count; i++) + if (!(jvp = MakeValue(g, args, i))->IsNull()) + objp->SetKeyValue(g, jvp, MakeKey(g, args, i)); + + str = Serialize(g, objp, NULL, 0); + } // endif objp + + } // endif CheckMemory + + if (!str) + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); + return str; +} // end of json_object_nonull + +void json_object_nonull_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_nonull_deinit + +/*********************************************************************************/ +/* Make a Json Object containing all the key/value parameters. */ +/*********************************************************************************/ +my_bool json_object_key_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count % 2) { + strcpy(message, "This function must have an even number of arguments"); + return true; + } // endif arg_count + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_object_key_init + +char *json_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, false, true)) { + PJOB objp; + + if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) { + for (uint i = 0; i < args->arg_count; i += 2) + objp->SetKeyValue(g, MakeValue(g, args, i + 1), MakePSZ(g, args, i)); + + str = Serialize(g, objp, NULL, 0); + } // endif objp + + } // endif CheckMemory + + if (!str) + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); + return str; +} // end of json_object_key + +void json_object_key_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_key_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, "This function must have at least 2 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, true, reslen, memlen, true); + + if (!JsonInit(initid, args, message, true, reslen, memlen)) { + PGLOBAL g = (PGLOBAL)initid->ptr; + + // This is a constant function + g->N = (initid->const_item) ? 1 : 0; + + // This is to avoid double execution when using prepared statements + if (IsJson(args, 0) > 1) + initid->const_item = 0; + + return false; + } else + return true; + +} // end of json_object_add_init + +char *json_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PCSZ key; + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + goto fin; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 2, false, true, true)) { + PJOB jobp; + PJVAL jvp; + PJSON jsp, top; + PGLOBAL gb = GetMemPtr(g, args, 0); + + jvp = MakeValue(g, args, 0, &top); + jsp = jvp->GetJson(); + + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JOB) { + jobp = jvp->GetObject(); + jvp = MakeValue(gb, args, 1); + key = MakeKey(gb, args, 1); + jobp->SetKeyValue(gb, jvp, key); + str = MakeResult(g, args, top); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (g->N) + // Keep result of constant function + g->Xchk = str; + + fin: + if (!str) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_object_add + +void json_object_add_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_add_deinit + +/*********************************************************************************/ +/* Delete a value from a Json object. */ +/*********************************************************************************/ +my_bool json_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have 2 or 3 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument must be a key string"); + return true; + } else + CalcLen(args, true, reslen, memlen, true); + + if (!JsonInit(initid, args, message, true, reslen, memlen)) { + PGLOBAL g = (PGLOBAL)initid->ptr; + + // This is a constant function + g->N = (initid->const_item) ? 1 : 0; + + // This is to avoid double execution when using prepared statements + if (IsJson(args, 0) > 1) + initid->const_item = 0; + + return false; + } else + return true; + +} // end of json_object_delete_init + +char *json_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + goto fin; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 1, false, true, true)) { + PCSZ key; + PJOB jobp; + PJSON jsp, top; + PJVAL jvp = MakeValue(g, args, 0, &top); + + jsp = jvp->GetJson(); + + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JOB) { + key = MakeKey(GetMemPtr(g, args, 0), args, 1); + jobp = jvp->GetObject(); + jobp->DeleteKey(key); + str = MakeResult(g, args, top); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (g->N) + // Keep result of constant function + g->Xchk = str; + + fin: + if (!str) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_object_delete + +void json_object_delete_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_delete_deinit + +/*********************************************************************************/ +/* Returns an array of the Json object keys. */ +/*********************************************************************************/ +my_bool json_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count != 1) { + strcpy(message, "This function must have 1 argument"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "Argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_object_list_init + +char *json_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->N) { + if (!CheckMemory(g, initid, args, 1, true, true)) { + char *p; + PJSON jsp; + PJVAL jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (jsp->GetType() == TYPE_JOB) { + PJAR jarp = ((PJOB)jsp)->GetKeyList(g); + + if (!(str = Serialize(g, jarp, NULL, 0))) + PUSH_WARNING(g->Message); + + } else { + PUSH_WARNING("First argument is not an object"); + if (g->Mrr) *error = 1; + } // endif jvp + + } // endif CheckMemory + + if (initid->const_item) { + // Keep result of constant function + g->Xchk = str; + g->N = 1; // str can be NULL + } // endif const_item + + } else + str = (char*)g->Xchk; + + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_object_list + +void json_object_list_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_list_deinit + +/*********************************************************************************/ +/* Returns an array of the Json object values. */ +/*********************************************************************************/ +my_bool json_object_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count != 1) { + strcpy(message, "This function must have 1 argument"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "Argument must be a json object"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_object_list_init + +char *json_object_values(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->N) { + if (!CheckMemory(g, initid, args, 1, true, true)) { + char *p; + PJSON jsp; + PJVAL jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (jsp->GetType() == TYPE_JOB) { + PJAR jarp = ((PJOB)jsp)->GetValList(g); + + if (!(str = Serialize(g, jarp, NULL, 0))) + PUSH_WARNING(g->Message); + + } else { + PUSH_WARNING("First argument is not an object"); + if (g->Mrr) *error = 1; + } // endif jvp + + } // endif CheckMemory + + if (initid->const_item) { + // Keep result of constant function + g->Xchk = str; + g->N = 1; // str can be NULL + } // endif const_item + + } else + str = (char*)g->Xchk; + + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_object_values + +void json_object_values_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_values_deinit + +/*********************************************************************************/ +/* Set the value of JsonGrpSize. */ +/*********************************************************************************/ +my_bool jsonset_grp_size_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1 || args->arg_type[0] != INT_RESULT) { + strcpy(message, "This function must have 1 integer argument"); + return true; + } else + return false; + +} // end of jsonset_grp_size_init + +long long jsonset_grp_size(UDF_INIT *initid, UDF_ARGS *args, char *, char *) +{ + long long n = *(long long*)args->args[0]; + + JsonGrpSize = (uint)n; + return (long long)GetJsonGroupSize(); +} // end of jsonset_grp_size + +/*********************************************************************************/ +/* Get the value of JsonGrpSize. */ +/*********************************************************************************/ +my_bool jsonget_grp_size_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 0) { + strcpy(message, "This function must have no arguments"); + return true; + } else + return false; + +} // end of jsonget_grp_size_init + +long long jsonget_grp_size(UDF_INIT *initid, UDF_ARGS *args, char *, char *) +{ + return (long long)GetJsonGroupSize(); +} // end of jsonget_grp_size + +/*********************************************************************************/ +/* Make a Json array from values coming from rows. */ +/*********************************************************************************/ +my_bool json_array_grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, n = GetJsonGroupSize(); + + if (args->arg_count != 1) { + strcpy(message, "This function can only accept 1 argument"); + return true; + } else if (IsJson(args, 0) == 3) { + strcpy(message, "This function does not support Jbin arguments"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + reslen *= n; + memlen += ((memlen - MEMFIX) * (n - 1)); + + if (JsonInit(initid, args, message, false, reslen, memlen)) + return true; + + PGLOBAL g = (PGLOBAL)initid->ptr; + + PlugSubSet(g->Sarea, g->Sarea_Size); + g->Activityp = (PACTIVITY)JsonNew(g, TYPE_JAR); + g->N = (int)n; + return false; +} // end of json_array_grp_init + +void json_array_grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PJAR arp = (PJAR)g->Activityp; + + if (arp && g->N-- > 0) + arp->AddArrayValue(g, MakeValue(g, args, 0)); + +} // end of json_array_grp_add + +char *json_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result, + unsigned long *res_length, char *, char *) +{ + char *str; + PGLOBAL g = (PGLOBAL)initid->ptr; + PJAR arp = (PJAR)g->Activityp; + + if (g->N < 0) + PUSH_WARNING("Result truncated to json_grp_size values"); + + if (arp) { + arp->InitArray(g); + str = Serialize(g, arp, NULL, 0); + } else + str = NULL; + + if (!str) + str = strcpy(result, g->Message); + + *res_length = strlen(str); + return str; +} // end of json_array_grp + +void json_array_grp_clear(UDF_INIT *initid, char*, char*) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + + PlugSubSet(g->Sarea, g->Sarea_Size); + g->Activityp = (PACTIVITY)JsonNew(g, TYPE_JAR); + g->N = GetJsonGroupSize(); +} // end of json_array_grp_clear + +void json_array_grp_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_grp_deinit + +/*********************************************************************************/ +/* Make a Json object from values coming from rows. */ +/*********************************************************************************/ +my_bool json_object_grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, n = GetJsonGroupSize(); + + if (args->arg_count != 2) { + strcpy(message, "This function requires 2 arguments (key, value)"); + return true; + } else if (IsJson(args, 0) == 3) { + strcpy(message, "This function does not support Jbin arguments"); + return true; + } else + CalcLen(args, true, reslen, memlen); + + reslen *= n; + memlen += ((memlen - MEMFIX) * (n - 1)); + + if (JsonInit(initid, args, message, false, reslen, memlen)) + return true; + + PGLOBAL g = (PGLOBAL)initid->ptr; + + PlugSubSet(g->Sarea, g->Sarea_Size); + g->Activityp = (PACTIVITY)JsonNew(g, TYPE_JOB); + g->N = (int)n; + return false; +} // end of json_object_grp_init + +void json_object_grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PJOB objp = (PJOB)g->Activityp; + + if (g->N-- > 0) + objp->SetKeyValue(g, MakeValue(g, args, 1), MakePSZ(g, args, 0)); + +} // end of json_object_grp_add + +char *json_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result, + unsigned long *res_length, char *, char *) +{ + char *str; + PGLOBAL g = (PGLOBAL)initid->ptr; + PJOB objp = (PJOB)g->Activityp; + + if (g->N < 0) + PUSH_WARNING("Result truncated to json_grp_size values"); + + if (!objp || !(str = Serialize(g, objp, NULL, 0))) + str = strcpy(result, g->Message); + + *res_length = strlen(str); + return str; +} // end of json_object_grp + +void json_object_grp_clear(UDF_INIT *initid, char*, char*) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + + PlugSubSet(g->Sarea, g->Sarea_Size); + g->Activityp = (PACTIVITY)JsonNew(g, TYPE_JOB); + g->N = GetJsonGroupSize(); +} // end of json_object_grp_clear + +void json_object_grp_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_grp_deinit + +/*********************************************************************************/ +/* Merge two arrays or objects. */ +/*********************************************************************************/ +my_bool json_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (!IsJson(args, 1)) { + strcpy(message, "Second argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + if (!JsonInit(initid, args, message, true, reslen, memlen)) { + PGLOBAL g = (PGLOBAL)initid->ptr; + + // This is a constant function + g->N = (initid->const_item) ? 1 : 0; + + // This is to avoid double execution when using prepared statements + if (IsJson(args, 0) > 1) + initid->const_item = 0; + + return false; + } else + return true; + +} // end of json_item_merge_init + +char *json_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + goto fin; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 2, false, false, true)) { + PJSON top = NULL; + PJVAL jvp; + PJSON jsp[2] = {NULL, NULL}; + + for (int i = 0; i < 2; i++) { + jvp = MakeValue(g, args, i); + if (!i) top = jvp->GetJson(); + + if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) { + snprintf(g->Message, sizeof(g->Message), "Argument %d is not an array or object", i); + PUSH_WARNING(g->Message); + } else + jsp[i] = jvp->GetJsp(); + + } // endfor i + + if (jsp[0]) { + if (jsp[0]->Merge(GetMemPtr(g, args, 0), jsp[1])) + PUSH_WARNING(GetMemPtr(g, args, 0)->Message); + else + str = MakeResult(g, args, top); + + } // endif jsp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (g->N) + // Keep result of constant function + g->Xchk = str; + + fin: + if (!str) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_item_merge + +void json_item_merge_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_item_merge_deinit + +/*********************************************************************************/ +/* Get a Json item from a Json document. */ +/*********************************************************************************/ +my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more; + int n = IsJson(args, 0); + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string (jpath)"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + if (n == 2 && args->args[0]) { + char fn[_MAX_PATH]; + long fl; + + memcpy(fn, args->args[0], args->lengths[0]); + fn[args->lengths[0]] = 0; + fl = GetFileLength(fn); + more = fl * 3; + } else if (n != 3) { + more = args->lengths[0] * 3; + } else + more = 0; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of json_get_item_init + +char *json_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *path, *str = NULL; + PJSON jsp; + PJVAL jvp; + PJSNX jsx; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true, true)) { + PUSH_WARNING("CheckMemory error"); + goto fin; + } // endif CheckMemory + + jvp = MakeTypedValue(g, args, 0, TYPE_JSON); + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = JsnxNew(g, jsp, TYPE_STRING, initid->max_length); + + if (!jsx || jsx->SetJpath(g, path, true)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return NULL; + } // endif SetJpath + + jsx->ReadValue(g); + + if (!jsx->GetValue()->IsNull()) + str = jsx->GetValue()->GetCharValue(); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + fin: + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_get_item + +void json_get_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_get_item_deinit + +/*********************************************************************************/ +/* Get a string value from a Json item. */ +/*********************************************************************************/ +my_bool jsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1024; + int n = IsJson(args, 0); + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string (jpath)"); + return true; + } else if (args->arg_count > 2) { + if (args->arg_type[2] == INT_RESULT && args->args[2]) + more += (unsigned long)*(long long*)args->args[2]; + else + strcpy(message, "Third argument is not an integer (memory)"); + + } // endif's + + CalcLen(args, false, reslen, memlen); +//memlen += more; + + if (n == 2 && args->args[0]) { + char fn[_MAX_PATH]; + long fl; + + memcpy(fn, args->args[0], args->lengths[0]); + fn[args->lengths[0]] = 0; + fl = GetFileLength(fn); + more += fl * 3; + } else if (n != 3) + more += args->lengths[0] * 3; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jsonget_string_init + +char *jsonget_string(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *p, *path, *str = NULL; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto err; + } else if (initid->const_item) + g->N = 1; + + try { + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true)) { + PUSH_WARNING("CheckMemory error"); + goto err; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto err; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = JsnxNew(g, jsp, TYPE_STRING, initid->max_length); + + if (!jsx || jsx->SetJpath(g, path)) { + PUSH_WARNING(g->Message); + goto err; + } // endif SetJpath + + jsx->ReadValue(g); + + if (!jsx->GetValue()->IsNull()) + str = jsx->GetValue()->GetCharValue(); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %-.256s\n", n, g->Message); + + PUSH_WARNING(g->Message); + str = NULL; + } catch (const char *msg) { + snprintf(g->Message, sizeof(g->Message), "%s", msg); + PUSH_WARNING(g->Message); + str = NULL; + } // end catch + + err: + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of jsonget_string + +void jsonget_string_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonget_string_deinit + +/*********************************************************************************/ +/* Get an integer value from a Json item. */ +/*********************************************************************************/ +my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more; + + if (args->arg_count != 2) { + strcpy(message, "This function must have 2 arguments"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "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); + + // TODO: calculate this + more = (IsJson(args, 0) != 3) ? 1000 : 0; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jsonget_int_init + +long long jsonget_int(UDF_INIT *initid, UDF_ARGS *args, + char *is_null, char *error) +{ + char *p, *path; + long long n; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + *is_null = 1; + return 0LL; + } else + return *(long long*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true)) { + PUSH_WARNING("CheckMemory error"); + if (g->Mrr) *error = 1; + *is_null = 1; + return 0LL; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + if (g->Mrr) *error = 1; + *is_null = 1; + return 0; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = JsnxNew(g, jsp, TYPE_BIGINT); + + if (!jsx || jsx->SetJpath(g, path)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return 0; + } // endif SetJpath + + jsx->ReadValue(g); + + if (jsx->GetValue()->IsNull()) { + *is_null = 1; + return 0; + } // endif IsNull + + n = jsx->GetValue()->GetBigintValue(); + + if (initid->const_item) { + // Keep result of constant function + long long *np = (long long*)PlgDBSubAlloc(g, NULL, sizeof(long long)); + + if (np) { + *np = n; + g->Activityp = (PACTIVITY)np; + } else + PUSH_WARNING(g->Message); + + } // endif const_item + + return n; +} // end of jsonget_int + +void jsonget_int_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonget_int_deinit + +/*********************************************************************************/ +/* Get a double value from a Json item. */ +/*********************************************************************************/ +my_bool jsonget_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more; + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "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); + + // TODO: calculate this + more = (IsJson(args, 0) != 3) ? 1000 : 0; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jsonget_real_init + +double jsonget_real(UDF_INIT *initid, UDF_ARGS *args, + char *is_null, char *error) +{ + char *p, *path; + double d; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + *is_null = 1; + return 0.0; + } else + return *(double*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true)) { + PUSH_WARNING("CheckMemory error"); + if (g->Mrr) *error = 1; + *is_null = 1; + return 0.0; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + *is_null = 1; + return 0.0; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = JsnxNew(g, jsp, TYPE_DOUBLE); + + if (!jsx || jsx->SetJpath(g, path)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return 0.0; + } // endif SetJpath + + jsx->ReadValue(g); + + if (jsx->GetValue()->IsNull()) { + *is_null = 1; + return 0.0; + } // endif IsNull + + d = jsx->GetValue()->GetFloatValue(); + + if (initid->const_item) { + // Keep result of constant function + double *dp; + + if ((dp = (double*)PlgDBSubAlloc(g, NULL, sizeof(double)))) { + *dp = d; + g->Activityp = (PACTIVITY)dp; + } else { + PUSH_WARNING(g->Message); + *is_null = 1; + return 0.0; + } // endif dp + + } // endif const_item + + return d; +} // end of jsonget_real + +void jsonget_real_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonget_real_deinit + +/*********************************************************************************/ +/* Locate a value in a Json tree. */ +/*********************************************************************************/ +my_bool jsonlocate_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1000; + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third argument is not an integer (rank)"); + 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]; + } + CalcLen(args, false, reslen, memlen); + + // TODO: calculate this + if (IsJson(args, 0) == 3) + more = 0; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jsonlocate_init + +char *jsonlocate(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *path = NULL; + int k; + PJVAL jvp, jvp2; + PJSON jsp; + PJSNX jsx; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (g->Activityp) { + path = (char*)g->Activityp; + *res_length = strlen(path); + return path; + } else { + *res_length = 0; + *is_null = 1; + return NULL; + } // endif Activityp + + } else if (initid->const_item) + g->N = 1; + + try { + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, !g->Xchk)) { + PUSH_WARNING("CheckMemory error"); + *error = 1; + goto err; + } else + jvp = MakeTypedValue(g, args, 0, TYPE_JSON); + + //if ((p = jvp->GetString(g))) { + // if (!(jsp = ParseJson(g, p, strlen(p)))) { + // PUSH_WARNING(g->Message); + // goto err; + // } // endif jsp + //} else + // jsp = jvp->GetJson(); + + if (!(jsp = jvp->GetJson())) { + PUSH_WARNING("First argument is not a valid JSON item"); + goto err; + } // endif jsp + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + // The item to locate + jvp2 = MakeValue(g, args, 1); + + k = (args->arg_count > 2) ? (int)*(long long*)args->args[2] : 1; + + jsx = new(g) JSNX(g, jsp, TYPE_STRING); + path = jsx->Locate(g, jsp, jvp2, k); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)path; + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %-.256s\n", n, g->Message); + + PUSH_WARNING(g->Message); + *error = 1; + path = NULL; + } catch (const char *msg) { + snprintf(g->Message, sizeof(g->Message), "%s", msg); + PUSH_WARNING(g->Message); + *error = 1; + path = NULL; + } // end catch + + err: + if (!path) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(path); + + return path; +} // end of jsonlocate + +void jsonlocate_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonlocate_deinit + +/*********************************************************************************/ +/* Locate all occurences of a value in a Json tree. */ +/*********************************************************************************/ +my_bool json_locate_all_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1000; + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third argument is not an integer (Depth)"); + 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]; + } + CalcLen(args, false, reslen, memlen); + + // TODO: calculate this + if (IsJson(args, 0) == 3) + more = 0; + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of json_locate_all_init + +char *json_locate_all(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *p, *path = NULL; + int mx = 10; + PJVAL jvp, jvp2; + PJSON jsp; + PJSNX jsx; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (g->Activityp) { + path = (char*)g->Activityp; + *res_length = strlen(path); + return path; + } else { + *error = 1; + *res_length = 0; + *is_null = 1; + return NULL; + } // endif Activityp + + } else if (initid->const_item) + g->N = 1; + + try { + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true)) { + PUSH_WARNING("CheckMemory error"); + *error = 1; + goto err; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto err; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + // The item to locate + jvp2 = MakeValue(g, args, 1); + + if (args->arg_count > 2) + mx = (int)*(long long*)args->args[2]; + + jsx = new(g) JSNX(g, jsp, TYPE_STRING); + path = jsx->LocateAll(g, jsp, jvp2, mx); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)path; + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %-.256s\n", n, g->Message); + + PUSH_WARNING(g->Message); + *error = 1; + path = NULL; + } catch (const char *msg) { + snprintf(g->Message, sizeof(g->Message), "%s", msg); + PUSH_WARNING(g->Message); + *error = 1; + path = NULL; + } // end catch + + err: + if (!path) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(path); + + return path; +} // end of json_locate_all + +void json_locate_all_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_locate_all_deinit + +/*********************************************************************************/ +/* Check whether the document contains a value or item. */ +/*********************************************************************************/ +my_bool jsoncontains_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1024; + int n = IsJson(args, 0); + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third argument is not an integer (index)"); + return true; + } else if (args->arg_count > 3) { + if (args->arg_type[3] == INT_RESULT && args->args[3]) + more += (unsigned long)*(long long*)args->args[3]; + else + strcpy(message, "Fourth argument is not an integer (memory)"); + + } // endif's + + CalcLen(args, false, reslen, memlen); +//memlen += more; + + // TODO: calculate this + more += (IsJson(args, 0) != 3 ? 1000 : 0); + + return JsonInit(initid, args, message, false, reslen, memlen, more); +} // end of jsoncontains_init + +long long jsoncontains(UDF_INIT *initid, UDF_ARGS *args, char *, char *error) +{ + char isn, res[256]; + unsigned long reslen; + + isn = 0; + jsonlocate(initid, args, res, &reslen, &isn, error); + return (isn) ? 0LL : 1LL; +} // end of jsoncontains + +void jsoncontains_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsoncontains_deinit + +/*********************************************************************************/ +/* Check whether the document contains a path. */ +/*********************************************************************************/ +my_bool jsoncontains_path_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1024; + int n = IsJson(args, 0); + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string (path)"); + return true; + } else if (args->arg_count > 2) { + if (args->arg_type[2] == INT_RESULT && args->args[2]) + more += (unsigned long)*(long long*)args->args[2]; + else + strcpy(message, "Third argument is not an integer (memory)"); + + } // endif's + + CalcLen(args, false, reslen, memlen); +//memlen += more; + + // TODO: calculate this + more += (IsJson(args, 0) != 3 ? 1000 : 0); + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jsoncontains_path_init + +long long jsoncontains_path(UDF_INIT *initid, UDF_ARGS *args, char *, char *error) +{ + char *p, *path; + long long n; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + return 0LL; + } else + return *(long long*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true)) { + PUSH_WARNING("CheckMemory error"); + goto err; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto err; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = JsnxNew(g, jsp, TYPE_BIGINT); + + if (!jsx || jsx->SetJpath(g, path)) { + PUSH_WARNING(g->Message); + goto err; + } // endif SetJpath + + n = (jsx->CheckPath(g)) ? 1LL : 0LL; + + if (initid->const_item) { + // Keep result of constant function + long long *np = (long long*)PlgDBSubAlloc(g, NULL, sizeof(long long)); + + if (np) { + *np = n; + g->Activityp = (PACTIVITY)np; + } else + PUSH_WARNING(g->Message); + + } // endif const_item + + return n; + + err: + if (g->Mrr) *error = 1; + return 0LL; +} // end of jsoncontains_path + +void jsoncontains_path_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsoncontains_path_deinit + +/*********************************************************************************/ +/* This function is used by the json_set/insert/update_item functions. */ +/*********************************************************************************/ +char *handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *p, *path, *str = NULL; + int w; + my_bool b = true; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + PGLOBAL gb = GetMemPtr(g, args, 0); + + if (g->Alchecked) { + str = (char*)g->Activityp; + goto fin; + } else if (g->N) + g->Alchecked = 1; + + if (!strcmp(result, "$set")) + w = 0; + else if (!strcmp(result, "$insert")) + w = 1; + else if (!strcmp(result, "$update")) + w = 2; + else { + PUSH_WARNING("Logical error, please contact CONNECT developer"); + goto fin; + } // endelse + + try { + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true, false, true)) { + PUSH_WARNING("CheckMemory error"); + throw 1; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + throw 2; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + jsx = new(g)JSNX(g, jsp, TYPE_STRING, initid->max_length, 0, true); + + for (uint i = 1; i + 1 < args->arg_count; i += 2) { + jvp = MakeValue(gb, args, i); + path = MakePSZ(g, args, i + 1); + + if (jsx->SetJpath(g, path, false)) { + PUSH_WARNING(g->Message); + continue; + } // endif SetJpath + + if (w) { + jsx->ReadValue(g); + b = jsx->GetValue()->IsNull(); + b = (w == 1) ? b : !b; + } // endif w + + if (b && jsx->WriteValue(gb, jvp)) + PUSH_WARNING(g->Message); + + } // endfor i + + // In case of error or file, return unchanged argument + if (!(str = MakeResult(g, args, jsp, INT_MAX32))) + str = MakePSZ(g, args, 0); + + if (g->N) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %-.256s\n", n, g->Message); + + PUSH_WARNING(g->Message); + str = NULL; + } catch (const char *msg) { + snprintf(g->Message, sizeof(g->Message), "%s", msg); + PUSH_WARNING(g->Message); + str = NULL; + } // end catch + +fin: + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of handle_item + +/*********************************************************************************/ +/* Set Json items of a Json document according to path. */ +/*********************************************************************************/ +my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 0; + int n = IsJson(args, 0); + + if (!(args->arg_count % 2)) { + strcpy(message, "This function must have an odd number of arguments"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + if (n == 2 && args->args[0]) { + char fn[_MAX_PATH]; + long fl; + + memcpy(fn, args->args[0], args->lengths[0]); + fn[args->lengths[0]] = 0; + fl = GetFileLength(fn); + more += fl * 3; + } else if (n != 3) + more += args->lengths[0] * 3; + + if (!JsonInit(initid, args, message, true, reslen, memlen, more)) { + PGLOBAL g = (PGLOBAL)initid->ptr; + + // This is a constant function + g->N = (initid->const_item) ? 1 : 0; + + // This is to avoid double execution when using prepared statements + if (IsJson(args, 0) > 1) + initid->const_item = 0; + + g->Alchecked = 0; + return false; + } else + return true; + +} // end of json_set_item_init + +char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *p) +{ + strcpy(result, "$set"); + return handle_item(initid, args, result, res_length, is_null, p); +} // end of json_set_item + +void json_set_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_set_item_deinit + +/*********************************************************************************/ +/* Insert Json items of a Json document according to path. */ +/*********************************************************************************/ +my_bool json_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_set_item_init(initid, args, message); +} // end of json_insert_item_init + +char *json_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *p) +{ + strcpy(result, "$insert"); + return handle_item(initid, args, result, res_length, is_null, p); +} // end of json_insert_item + +void json_insert_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_insert_item_deinit + +/*********************************************************************************/ +/* Update Json items of a Json document according to path. */ +/*********************************************************************************/ +my_bool json_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_set_item_init(initid, args, message); +} // end of json_update_item_init + +char *json_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *p) +{ + strcpy(result, "$update"); + return handle_item(initid, args, result, res_length, is_null, p); +} // end of json_update_item + +void json_update_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_update_item_deinit + +/*********************************************************************************/ +/* Returns a json file as a json string. */ +/*********************************************************************************/ +my_bool json_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, fl, more = 1024; + + if (args->arg_count < 1 || args->arg_count > 4) { + strcpy(message, "This function only accepts 1 to 4 arguments"); + return true; + } else if (args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a string (file name)"); + return true; + } // endif's args[0] + + for (unsigned int i = 1; i < args->arg_count; i++) { + if (!(args->arg_type[i] == INT_RESULT || args->arg_type[i] == STRING_RESULT)) { + sprintf(message, "Argument %d is not an integer or a string (pretty or path)", i); + return true; + } // endif arg_type + + // Take care of eventual memory argument + if (args->arg_type[i] == INT_RESULT && args->args[i]) + more += (ulong)*(longlong*)args->args[i]; + + } // endfor i + + initid->maybe_null = 1; + CalcLen(args, false, reslen, memlen); + + if (args->args[0]) + fl = GetFileLength(args->args[0]); + else + fl = 100; // What can be done here? + + reslen += fl; + + if (initid->const_item) + more += fl; + + if (args->arg_count > 1) + more += fl * M; + + memlen += more; + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_file_init + +char *json_file(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str, *fn; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Xchk; + goto fin; + } else if (initid->const_item) + g->N = 1; + + PlugSubSet(g->Sarea, g->Sarea_Size); + fn = MakePSZ(g, args, 0); + + if (args->arg_count > 1) { + int pretty = 3, pty = 3; + size_t len; + PJSON jsp; + PJVAL jvp = NULL; + + for (unsigned int i = 1; i < args->arg_count; i++) + if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) { + pretty = (int) * (longlong*)args->args[i]; + break; + } // endif type + + /*******************************************************************************/ + /* Parse the json file and allocate its tree structure. */ + /*******************************************************************************/ + if (!(jsp = ParseJsonFile(g, fn, &pty, len))) { + PUSH_WARNING(g->Message); + str = NULL; + goto fin; + } // endif jsp + + if (pty == 3) + PUSH_WARNING("File pretty format cannot be determined"); + else if (pretty != 3 && pty != pretty) + PUSH_WARNING("File pretty format doesn't match the specified pretty value"); + else if (pretty == 3) + pretty = pty; + + // Check whether a path was specified + if (CheckPath(g, args, jsp, jvp, 1)) { + PUSH_WARNING(g->Message); + str = NULL; + goto fin; + } else if (jvp) + jsp = jvp->GetJson(); + + if (!(str = Serialize(g, jsp, NULL, 0))) + PUSH_WARNING(g->Message); + + } else + if (!(str = GetJsonFile(g, fn))) + PUSH_WARNING(g->Message); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = str; + + fin: + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of json_file + +void json_file_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_file_deinit + +/*********************************************************************************/ +/* Make a json file from a json item. */ +/*********************************************************************************/ +my_bool jfile_make_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 1 || args->arg_count > 3) { + strcpy(message, "Wrong number of arguments"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } // endif + + CalcLen(args, false, reslen, memlen); + memlen = memlen + 5000; // To take care of not pretty files + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jfile_make_init + +char *jfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *p, *str = NULL, *fn = NULL; + int n, pretty = 2; + PJSON jsp; + PJVAL jvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if ((n = IsJson(args, 0)) == 3) { + // Get default file name and pretty + PBSON bsp = (PBSON)args->args[0]; + + fn = bsp->Filename; + pretty = bsp->Pretty; + } else if (n == 2) + fn = args->args[0]; + + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true)) { + PUSH_WARNING("CheckMemory error"); + goto fin; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!strchr("[{ \t\r\n", *p)) { + // Is this a file name? + if (!(p = GetJsonFile(g, p))) { + PUSH_WARNING(g->Message); + goto fin; + } else + fn = jvp->GetString(g); + + } // endif p + + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto fin; + } // endif jsp + + jvp->SetValue(jsp); + } // endif p + + if (g->Mrr) { // First argument is a constant + g->Xchk = jvp; + JsonMemSave(g); + } // endif Mrr + + } else + jvp = (PJVAL)g->Xchk; + + for (uint i = 1; i < args->arg_count; i++) + switch (args->arg_type[i]) { + case STRING_RESULT: + fn = MakePSZ(g, args, i); + break; + case INT_RESULT: + pretty = (int)*(longlong*)args->args[i]; + break; + default: + PUSH_WARNING("Unexpected argument type in jfile_make"); + } // endswitch arg_type + + if (fn) { + if (!Serialize(g, jvp->GetJson(), fn, pretty)) + PUSH_WARNING(g->Message); + } else + PUSH_WARNING("Missing file name"); + + str= fn; + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + fin: + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of jfile_make + +void jfile_make_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jfile_make_deinit + +/*********************************************************************************/ +/* Make and return a binary Json array containing all the parameters. */ +/*********************************************************************************/ +my_bool jbin_array_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, false, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jbin_array_init + +char *jbin_array(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, false)) { + PJAR arp; + + if ((arp = (PJAR)JsonNew(g, TYPE_JAR)) && + (bsp = JbinAlloc(g, args, initid->max_length, arp))) { + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array"); + + for (uint i = 0; i < args->arg_count; i++) + arp->AddArrayValue(g, MakeValue(g, args, i)); + + arp->InitArray(g); + } // endif arp && bsp + + } else + bsp = NULL; + + if (!bsp && (bsp = JbinAlloc(g, args, initid->max_length, NULL))) + strmake(bsp->Msg, g->Message, BMX-1); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_array + +void jbin_array_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_deinit + +/*********************************************************************************/ +/* Add one or several values to a Json array. */ +/*********************************************************************************/ +my_bool jbin_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jbin_array_add_values_init + +char *jbin_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, true)) { + PJSON top; + PJAR arp; + PJVAL jvp = MakeTypedValue(g, args, 0, TYPE_JAR, &top); + PGLOBAL gb = GetMemPtr(g, args, 0); + + if (jvp->GetValType() != TYPE_JAR) { + if ((arp = (PJAR)JsonNew(gb, TYPE_JAR))) { + arp->AddArrayValue(gb, jvp); + top = arp; + } // endif arp + + } else + arp = jvp->GetArray(); + + for (uint i = 1; i < args->arg_count; i++) + arp->AddArrayValue(gb, MakeValue(gb, args, i)); + + arp->InitArray(gb); + + if ((bsp = JbinAlloc(g, args, initid->max_length, top))) { + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array"); + bsp->Jsp = arp; + } // endif bsp + + } else + if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) + strmake(bsp->Msg, g->Message, BMX-1); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_array_add_values + +void jbin_array_add_values_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_add_values_deinit + +/*********************************************************************************/ +/* Add one value to a Json array. */ +/*********************************************************************************/ +my_bool jbin_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + //} else if (!IsJson(args, 0)) { + // strcpy(message, "First argument must be a json item"); + // return true; + } else + CalcLen(args, false, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jbin_array_add_init + +char *jbin_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + int n = 2; + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 2, false, false, true)) { + int *x = NULL; + uint n = 2; +// PJSON jsp; + PJVAL jvp; + PJAR arp; + + jvp = MakeTypedValue(g, args, 0, TYPE_JSON, &top); + // jsp = jvp->GetJson(); + x = GetIntArgPtr(g, args, n); + + if (CheckPath(g, args, top, jvp, n)) + PUSH_WARNING(g->Message); + else if (jvp) { + PGLOBAL gb = GetMemPtr(g, args, 0); + + if (jvp->GetValType() != TYPE_JAR) { + if ((arp = (PJAR)JsonNew(gb, TYPE_JAR))) { + arp->AddArrayValue(gb, (PJVAL)JvalNew(gb, TYPE_JVAL, jvp)); + jvp->SetValue(arp); + + if (!top) + top = arp; + + } // endif arp + + } else + arp = jvp->GetArray(); + + arp->AddArrayValue(gb, MakeValue(gb, args, 1), x); + arp->InitArray(gb); + } else { + PUSH_WARNING("First argument target is not an array"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length, n); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_array_add + +void jbin_array_add_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_add_deinit + +/*********************************************************************************/ +/* Delete a value from a Json array. */ +/*********************************************************************************/ +my_bool jbin_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); + } // end of jbin_array_delete_init + +char *jbin_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 1, false, false, true)) { + int *x; + uint n = 1; + PJAR arp; + PJVAL jvp = MakeTypedValue(g, args, 0, TYPE_JSON, &top); + + if (CheckPath(g, args, top, jvp, 1)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JAR) { + if ((x = GetIntArgPtr(g, args, n))) { + arp = jvp->GetArray(); + arp->DeleteValue(*x); + arp->InitArray(GetMemPtr(g, args, 0)); + } else + PUSH_WARNING("Missing or null array index"); + + } else { + PUSH_WARNING("First argument target is not an array"); +// if (g->Mrr) *error = 1; + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_array_delete + +void jbin_array_delete_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_delete_deinit + +/*********************************************************************************/ +/* Make a Json Object containing all the parameters. */ +/*********************************************************************************/ +my_bool jbin_object_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jbin_object_init + +char *jbin_object(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, true)) { + PJOB objp; + + if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) { + for (uint i = 0; i < args->arg_count; i++) + objp->SetKeyValue(g, MakeValue(g, args, i), MakeKey(g, args, i)); + + + if ((bsp = JbinAlloc(g, args, initid->max_length, objp))) + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object"); + + } else + bsp = NULL; + + } else + if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) + strmake(bsp->Msg, g->Message, BMX-1); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_object + +void jbin_object_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_deinit + +/*********************************************************************************/ +/* Make a Json Object containing all not null parameters. */ +/*********************************************************************************/ +my_bool jbin_object_nonull_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jbin_object_nonull_init + +char *jbin_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, false, true)) { + PJVAL jvp; + PJOB objp; + + if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) { + for (uint i = 0; i < args->arg_count; i++) + if (!(jvp = MakeValue(g, args, i))->IsNull()) + objp->SetKeyValue(g, jvp, MakeKey(g, args, i)); + + if ((bsp = JbinAlloc(g, args, initid->max_length, objp))) + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object"); + + } else + bsp = NULL; + + } else + if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) + strmake(bsp->Msg, g->Message, BMX-1); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_object_nonull + +void jbin_object_nonull_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_nonull_deinit + +/*********************************************************************************/ +/* Make a Json Object containing all the key/value parameters. */ +/*********************************************************************************/ +my_bool jbin_object_key_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count % 2) { + strcpy(message, "This function must have an even number of arguments"); + return true; + } // endif arg_count + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jbin_object_key_init + +char *jbin_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, false, true)) { + PJOB objp; + + if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) { + for (uint i = 0; i < args->arg_count; i += 2) + objp->SetKeyValue(g, MakeValue(g, args, i + 1), MakePSZ(g, args, i)); + + if ((bsp = JbinAlloc(g, args, initid->max_length, objp))) + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object"); + + } else + bsp = NULL; + + } else + if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) + strmake(bsp->Msg, g->Message, BMX-1); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_object_key + +void jbin_object_key_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_key_deinit + +/*********************************************************************************/ +/* Add or replace a value in a Json Object. */ +/*********************************************************************************/ +my_bool jbin_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, true, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); + } // end of jbin_object_add_init + +char *jbin_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + bsp = (PBSON)g->Xchk; + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 2, false, true, true)) { + PCSZ key; + PJOB jobp; + PJVAL jvp = MakeValue(g, args, 0, &top); + PJSON jsp = jvp->GetJson(); + + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JOB) { + PGLOBAL gb = GetMemPtr(g, args, 0); + + jobp = jvp->GetObject(); + jvp = MakeValue(gb, args, 1); + key = MakeKey(gb, args, 1); + jobp->SetKeyValue(gb, jvp, key); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_object_add + +void jbin_object_add_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_add_deinit + +/*********************************************************************************/ +/* Delete a value from a Json object. */ +/*********************************************************************************/ +my_bool jbin_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have 2 or 3 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument must be a key string"); + return true; + } else + CalcLen(args, true, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); + } // end of jbin_object_delete_init + +char *jbin_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + bsp = (PBSON)g->Xchk; + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 1, false, true, true)) { + PCSZ key; + PJOB jobp; + PJVAL jvp = MakeValue(g, args, 0, &top); + (void) jvp->GetJson(); // XXX Should be removed? + + 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); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_object_delete + +void jbin_object_delete_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_delete_deinit + +/*********************************************************************************/ +/* Returns an array of the Json object keys. */ +/*********************************************************************************/ +my_bool jbin_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_object_list_init(initid, args, message); +} // end of jbin_object_list_init + +char *jbin_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PJAR jarp = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, 1, true, true)) { + char *p; + PJSON jsp; + PJVAL jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (jsp->GetType() == TYPE_JOB) { + jarp = ((PJOB)jsp)->GetKeyList(g); + } else { + PUSH_WARNING("First argument is not an object"); + if (g->Mrr) *error = 1; + } // endif jsp type + + } // endif CheckMemory + + if ((bsp = JbinAlloc(g, args, initid->max_length, jarp))) + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array"); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_object_list + +void jbin_object_list_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_list_deinit + +/*********************************************************************************/ +/* Get a Json item from a Json document. */ +/*********************************************************************************/ +my_bool jbin_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_get_item_init(initid, args, message); +} // end of jbin_get_item_init + +char *jbin_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PBSON bsp = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + bsp = (PBSON)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true, true)) { + PUSH_WARNING("CheckMemory error"); + goto fin; + } // endif CheckMemory + + jvp = MakeTypedValue(g, args, 0, TYPE_JSON); + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = JsnxNew(g, jsp, TYPE_STRING, initid->max_length); + + if (!jsx || jsx->SetJpath(g, path, false)) { + PUSH_WARNING(g->Message); + goto fin; + } // endif SetJpath + + // Get the json tree + if ((jvp = jsx->GetRowValue(g, jsp, 0, false))) { + jsp = (jvp->GetJsp()) ? jvp->GetJsp() : JvalNew(g, TYPE_JVAL, jvp->GetValue(g)); + + if ((bsp = JbinAlloc(g, args, initid->max_length, jsp))) + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " item"); + else + *error = 1; + + } // endif jvp + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)bsp; + + fin: + if (!bsp) { + *is_null = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_get_item + +void jbin_get_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_get_item_deinit + +/*********************************************************************************/ +/* Merge two arrays or objects. */ +/*********************************************************************************/ +my_bool jbin_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (!IsJson(args, 1)) { + strcpy(message, "Second argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); + } // end of jbin_item_merge_init + +char *jbin_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 2, false, false, true)) { + PJVAL jvp; + PJSON jsp[2] = {NULL, NULL}; + PGLOBAL gb = GetMemPtr(g, args, 0); + + for (int i = 0; i < 2; i++) { + jvp = MakeValue(g, args, i); + if (!i) top = jvp->GetJson(); + + if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) { + snprintf(g->Message, sizeof(g->Message), "Argument %d is not an array or object", i); + PUSH_WARNING(g->Message); + } else + jsp[i] = jvp->GetJsp(); + + } // endfor i + + if (jsp[0] && jsp[0]->Merge(gb, jsp[1])) + PUSH_WARNING(gb->Message); + + } // endif CheckMemory + + // In case of error unchanged first argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + if (!bsp) { + *is_null = 1; + *error = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_item_merge + +void jbin_item_merge_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_item_merge_deinit + +/*********************************************************************************/ +/* This function is used by the jbin_set/insert/update functions. */ +/*********************************************************************************/ +char *bin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *p, *path; + int w; + my_bool b = true; + PJSON jsp; + PJSNX jsx; + PJVAL jvp = NULL; + PBSON bsp = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PGLOBAL gb = GetMemPtr(g, args, 0); + + if (g->N) { + bsp = (PBSON)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!strcmp(result, "$set")) + w = 0; + else if (!strcmp(result, "$insert")) + w = 1; + else if (!strcmp(result, "$update")) + w = 2; + else { + PUSH_WARNING("Logical error, please contact CONNECT developer"); + goto fin; + } // endelse + + if (!g->Xchk) { + if (CheckMemory(g, initid, args, 1, true, false, true)) { + PUSH_WARNING("CheckMemory error"); + goto fin; + } else + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString(g))) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto fin; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + jsx = new(g)JSNX(g, jsp, TYPE_STRING, initid->max_length, 0, true); + + for (uint i = 1; i+1 < args->arg_count; i += 2) { + jvp = MakeValue(gb, args, i); + path = MakePSZ(g, args, i+1); + + if (jsx->SetJpath(g, path, false)) { + PUSH_WARNING(g->Message); + continue; + } // endif SetJpath + + if (w) { + jsx->ReadValue(g); + b = jsx->GetValue()->IsNull(); + b = (w == 1) ? b : !b; + } // endif w + + if (b && jsx->WriteValue(gb, jvp)) + PUSH_WARNING(g->Message); + + } // endfor i + + if (!(bsp = MakeBinResult(g, args, jsp, initid->max_length, INT_MAX32))) + *error = 1; + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)bsp; + +fin: + if (!bsp) { + *is_null = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of bin_handle_item + +/*********************************************************************************/ +/* Set Json items of a Json document according to path. */ +/*********************************************************************************/ +my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 0; + int n = IsJson(args, 0); + + if (!(args->arg_count % 2)) { + strcpy(message, "This function must have an odd number of arguments"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + if (n == 2 && args->args[0]) { + char fn[_MAX_PATH]; + long fl; + + memcpy(fn, args->args[0], args->lengths[0]); + fn[args->lengths[0]] = 0; + fl = GetFileLength(fn); + more = fl * 3; + } else if (n != 3) + more = args->lengths[0] * 3; + + return JsonInit(initid, args, message, true, reslen, memlen, more); + } // end of jbin_set_item_init + +char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *p) +{ + strcpy(result, "$set"); + return bin_handle_item(initid, args, result, res_length, is_null, p); +} // end of jbin_set_item + +void jbin_set_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_set_item_deinit + +/*********************************************************************************/ +/* Insert Json items of a Json document according to path. */ +/*********************************************************************************/ +my_bool jbin_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_set_item_init(initid, args, message); +} // end of jbin_insert_item_init + +char *jbin_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *p) +{ + strcpy(result, "$insert"); + return bin_handle_item(initid, args, result, res_length, is_null, p); +} // end of jbin_insert_item + +void jbin_insert_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_insert_item_deinit + +/*********************************************************************************/ +/* Update Json items of a Json document according to path. */ +/*********************************************************************************/ +my_bool jbin_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_set_item_init(initid, args, message); +} // end of jbin_update_item_init + +char *jbin_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *p) +{ + strcpy(result, "$update"); + return bin_handle_item(initid, args, result, res_length, is_null, p); +} // end of jbin_update_item + +void jbin_update_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_update_item_deinit + +/*********************************************************************************/ +/* Returns a json file as a json item. */ +/*********************************************************************************/ +my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, fl, more = 1024; + + if (args->arg_count < 1 || args->arg_count > 4) { + strcpy(message, "This function only accepts 1 to 4 arguments"); + return true; + } else if (args->arg_type[0] != STRING_RESULT || !args->args[0]) { + strcpy(message, "First argument must be a constant string (file name)"); + return true; + } // endifs + + for (unsigned int i = 1; i < args->arg_count; i++) { + if (!(args->arg_type[i] == INT_RESULT || args->arg_type[i] == STRING_RESULT)) { + sprintf(message, "Argument %d is not an integer or a string (pretty or path)", i); + return true; + } // endif arg_type + + // Take care of eventual memory argument + if (args->arg_type[i] == INT_RESULT && args->args[i]) + more += (ulong) * (longlong*)args->args[i]; + + } // endfor i + + initid->maybe_null = 1; + CalcLen(args, false, reslen, memlen); + fl = GetFileLength(args->args[0]); + reslen += fl; + more += fl * M; +//memlen += more; + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of jbin_file_init + +char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *fn; + int pretty = 3, pty = 3; + size_t len = 0; + PJSON jsp; + PJVAL jvp = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) + goto fin; + + PlugSubSet(g->Sarea, g->Sarea_Size); + g->Xchk = NULL; + fn = MakePSZ(g, args, 0); + + for (unsigned int i = 1; i < args->arg_count; i++) + if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) { + pretty = (int) * (longlong*)args->args[i]; + break; + } // endif type + + /*********************************************************************************/ + /* Parse the json file and allocate its tree structure. */ + /*********************************************************************************/ + if (!(jsp = ParseJsonFile(g, fn, &pty, len))) { + PUSH_WARNING(g->Message); + *error = 1; + goto fin; + } // endif jsp + + if (pty == 3) + PUSH_WARNING("File pretty format cannot be determined"); + else if (pretty != 3 && pty != pretty) + PUSH_WARNING("File pretty format doesn't match the specified pretty value"); + else if (pretty == 3) + pretty = pty; + + if ((bsp = JbinAlloc(g, args, len, jsp))) { + safe_strcat(bsp->Msg, sizeof(bsp->Msg), " file"); + bsp->Filename = fn; + bsp->Pretty = pretty; + } else { + *error = 1; + goto fin; + } // endif bsp + + // Check whether a path was specified + if (CheckPath(g, args, jsp, jvp, 1)) { + PUSH_WARNING(g->Message); + bsp = NULL; + goto fin; + } else if (jvp) + bsp->Jsp = jvp->GetJsp(); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + fin: + if (!bsp) { + *res_length = 0; + *is_null = 1; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_file + +void jbin_file_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_file_deinit + +/*********************************************************************************/ +/* Serialize a Json document. . */ +/*********************************************************************************/ +my_bool json_serialize_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count != 1) { + strcpy(message, "This function must have 1 argument"); + return true; + } else if (args->args[0] && IsJson(args, 0) != 3) { + strcpy(message, "Argument must be a Jbin tree"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_serialize_init + +char *json_serialize(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + char *str; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + if (IsJson(args, 0) == 3) { + PBSON bsp = (PBSON)args->args[0]; + + JsonSubSet(g); + + if (!(str = Serialize(g, bsp->Jsp, NULL, 0))) + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else { + // *error = 1; + str = strcpy(result, "Argument is not a Jbin tree"); + } // endif + + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); + return str; +} // end of json_serialize + +void json_serialize_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_serialize_deinit + +/*********************************************************************************/ +/* Convert a prettiest Json file to Pretty=0. */ +/*********************************************************************************/ +my_bool jfile_convert_init(UDF_INIT* initid, UDF_ARGS* args, char* message) { + unsigned long reslen, memlen; + + if (args->arg_count != 3) { + strcpy(message, "This function must have 3 arguments"); + return true; + } else if (args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third Argument must be an integer (LRECL)"); + return true; + } else for (int i = 0; i < 2; i++) + if (args->arg_type[i] != STRING_RESULT) { + sprintf(message, "Arguments %d must be a string (file name)", i+1); + return true; + } // endif args + + CalcLen(args, false, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jfile_convert_init + +char *jfile_convert(UDF_INIT* initid, UDF_ARGS* args, char* result, + unsigned long *res_length, char *is_null, char *error) { + char *str, *fn, *ofn; + int lrecl = (int)*(longlong*)args->args[2]; + PGLOBAL g = (PGLOBAL)initid->ptr; + + PlugSubSet(g->Sarea, g->Sarea_Size); + fn = MakePSZ(g, args, 0); + ofn = MakePSZ(g, args, 1); + + if (!g->Xchk) { + JUP* jup = new(g) JUP(g); + + str = jup->UnprettyJsonFile(g, fn, ofn, lrecl); + g->Xchk = str; + } else + str = (char*)g->Xchk; + + if (!str) { + PUSH_WARNING(g->Message[0] != '\0' ? g->Message : "Unexpected error"); + *is_null = 1; + *error = 1; + *res_length = 0; + } else { + strcpy(result, str); + *res_length = strlen(str); + } // endif str + + return str; +} // end of jfile_convert + +void jfile_convert_deinit(UDF_INIT* initid) { + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jfile_convert_deinit + +/*********************************************************************************/ +/* Convert a prettiest Json file to Pretty=0. */ +/*********************************************************************************/ +my_bool jfile_bjson_init(UDF_INIT* initid, UDF_ARGS* args, char* message) { + unsigned long reslen, memlen; + + if (args->arg_count != 2 && args->arg_count != 3) { + strcpy(message, "This function must have 2 or 3 arguments"); + return true; + } else if (args->arg_count == 3 && args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third Argument must be an integer (LRECL)"); + return true; + } else for (int i = 0; i < 2; i++) + if (args->arg_type[i] != STRING_RESULT) { + sprintf(message, "Arguments %d must be a string (file name)", i + 1); + return true; + } // endif args + + CalcLen(args, false, reslen, memlen); + memlen = memlen * M; + memlen += (args->arg_count == 3) ? (ulong)*(longlong*)args->args[2] : 1024; + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jfile_bjson_init + +char *jfile_bjson(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char*, char *error) { + char *fn, *ofn, *buf, *str = NULL; + bool loop; + ssize_t len, newloc; + size_t lrecl, *binszp; + PJSON jsp; + SWAP *swp; + PGLOBAL g = (PGLOBAL)initid->ptr; + + PlugSubSet(g->Sarea, g->Sarea_Size); + fn = MakePSZ(g, args, 0); + ofn = MakePSZ(g, args, 1); + + if (args->arg_count == 3) + lrecl = (size_t)*(longlong*)args->args[2]; + else + lrecl = 1024; + + if (!g->Xchk) { + int msgid = MSGID_OPEN_MODE_STRERROR; + FILE *fout = NULL; + FILE *fin; + + if (!(fin = global_fopen(g, msgid, fn, "rt"))) + str = strcpy(result, g->Message); + else if (!(fout = global_fopen(g, msgid, ofn, "wb"))) + str = strcpy(result, g->Message); + else if ((buf = (char*)PlgDBSubAlloc(g, NULL, lrecl)) && + (binszp = (size_t*)PlgDBSubAlloc(g, NULL, sizeof(size_t)))) { + JsonMemSave(g); + + try { + do { + loop = false; + JsonSubSet(g); + + if (!fgets(buf, lrecl, fin)) { + if (!feof(fin)) { + snprintf(g->Message, sizeof(g->Message), "Error %d reading %zu bytes from %s", errno, lrecl, fn); + str = strcpy(result, g->Message); + } else + str = strcpy(result, ofn); + + } else if ((len = strlen(buf))) { + if ((jsp = ParseJson(g, buf, len))) { + newloc = (size_t)PlugSubAlloc(g, NULL, 0); + *binszp = newloc - (size_t)jsp; + + swp = new(g) SWAP(g, jsp); + swp->SwapJson(jsp, true); + + if (fwrite(binszp, sizeof(binszp), 1, fout) != 1) { + snprintf(g->Message, sizeof(g->Message), "Error %d writing %zu bytes to %s", + errno, sizeof(binszp), ofn); + str = strcpy(result, g->Message); + } else if (fwrite(jsp, *binszp, 1, fout) != 1) { + snprintf(g->Message, sizeof(g->Message), "Error %d writing %zu bytes to %s", + errno, *binszp, ofn); + str = strcpy(result, g->Message); + } else + loop = true; + + } else { + str = strcpy(result, g->Message); + } // endif jsp + + } else + loop = true; + + } while (loop); + + } catch (int) { + str = strcpy(result, g->Message); + } catch (const char* msg) { + str = strcpy(result, msg); + } // end catch + + } else + str = strcpy(result, g->Message); + + if (fin) fclose(fin); + if (fout) fclose(fout); + g->Xchk = str; + } else + str = (char*)g->Xchk; + + if (!str) { + if (g->Message[0] != '\0') + str = strcpy(result, g->Message); + else + str = strcpy(result, "Unexpected error"); + + } // endif str + + *res_length = strlen(str); + return str; +} // end of jfile_bjson + +void jfile_bjson_deinit(UDF_INIT* initid) { + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jfile_bjson_deinit + +/* --------------------------------- Class JUP --------------------------------- */ + +#define ARGS MY_MIN(24,(int)len-i),s+MY_MAX(i-3,0) + +/*********************************************************************************/ +/* JUP public constructor. */ +/*********************************************************************************/ +JUP::JUP(PGLOBAL g) { + fs = NULL; + s = buff = NULL; + len = 0; + k = recl = 0; + i = 0; +} // end of JUP constructor + +/*********************************************************************************/ +/* Copy a json file to another with pretty = 0. */ +/*********************************************************************************/ +char* JUP::UnprettyJsonFile(PGLOBAL g, char *fn, char *outfn, int lrecl) { + char *ret = NULL; + HANDLE hFile; + MEMMAP mm; + + /*******************************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************************/ + hFile = CreateFileMap(g, fn, &mm, MODE_READ, false); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR), "map", (int)rc, fn); + + return NULL; + } // endif hFile + + /*******************************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*******************************************************************************/ + if (!mm.lenL && !mm.lenH) { // Empty or deleted file + CloseFileHandle(hFile); + return NULL; + } else { + len = (size_t)mm.lenL; + + if (mm.lenH) + len += ((size_t)mm.lenH * 0x000000001LL); + + } // endif size + + if (!mm.memory) { + CloseFileHandle(hFile); + snprintf(g->Message, sizeof(g->Message), MSG(MAP_VIEW_ERROR), fn, GetLastError()); + return NULL; + } else + s = (char*)mm.memory; + + CloseFileHandle(hFile); // Not used anymore + + /*********************************************************************************/ + /* Parse the json file and allocate its tree structure. */ + /*********************************************************************************/ + if (!(fs = fopen(outfn, "wb"))) { + snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR)": %s", + "w", (int)errno, outfn, strerror(errno)); + CloseMemMap(mm.memory, len); + return NULL; + } // endif fs + + g->Message[0] = 0; + + if (!unPretty(g, lrecl)) + ret = outfn; + + CloseMemMap(mm.memory, len); + fclose(fs); + return ret; +} // end of UnprettyJsonFile + +/***********************************************************************/ +/* Translate a json file to pretty = 0. */ +/***********************************************************************/ +bool JUP::unPretty(PGLOBAL g, int lrecl) { + bool go, next, rc = false; + + if (trace(1)) + htrc("UnPretty: s=%.10s len=%zd lrecl=%d\n", s, len, lrecl); + + if (!s || !len) { + snprintf(g->Message, sizeof(g->Message), "Void JSON file"); + return true; + } else if (*s != '[') { + // strcpy(g->Message, "JSON file is not an array"); + s = strchr(s, '['); + // return true; + } // endif s + + i = 1; + go = next = true; + + try { + // Allocate the record + buff = (char*)PlugSubAlloc(g, NULL, (size_t)lrecl + 3); + recl = lrecl; + + do { + for (k = 0; go && i < len; i++) + switch (s[i]) { + case '{': + buff[k++] = s[i++]; + CopyObject(g); + break; + case '[': + throw "JSON file is not an array of objects"; + break; + case ' ': + case '\t': + case '\n': + case '\r': + break; + case ',': + go = false; + break; + case ']': + go = next = false; + break; + default: + snprintf(g->Message, sizeof(g->Message), "Unexpected '%c' near %.*s", s[i], ARGS); + throw 4; + break; + }; // endswitch s[i] + + // Write the record +#ifdef __win_ + buff[k++] = '\r'; +#endif + buff[k++] = '\n'; + buff[k] = 0; + + if ((fputs(buff, fs)) == EOF) { + snprintf(g->Message, sizeof(g->Message), MSG(FPUTS_ERROR), strerror(errno)); + throw 5; + } // endif EOF + + go = true; + } while (next); + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %s\n", n, g->Message); + rc = true; + } catch (const char* msg) { + snprintf(g->Message, sizeof(g->Message), "%s", msg); + rc = true; + } // end catch + + return rc; +} // end of unPretty + +/***********************************************************************/ +/* Copy a JSON Object. */ +/***********************************************************************/ +void JUP::CopyObject(PGLOBAL g) { + int level = 0; + + for (; i < len; i++) + switch (s[i]) { + case '"': + AddBuff(s[i++]); + + if (level < 2) { + CopyString(g); + level = 1; + } else { + snprintf(g->Message, sizeof(g->Message), "misplaced string near %.*s", ARGS); + throw 3; + } // endif level + + break; + case ':': + AddBuff(s[i++]); + + if (level == 1) { + CopyValue(g); + level = 2; + } else { + snprintf(g->Message, sizeof(g->Message), "Unexpected ':' near %.*s", ARGS); + throw 3; + } // endif level + + break; + case ',': + AddBuff(s[i]); + + if (level < 2) { + snprintf(g->Message, sizeof(g->Message), "Unexpected ',' near %.*s", ARGS); + throw 3; + } else + level = 0; + + break; + case '}': + AddBuff(s[i]); + + if (level == 1) { + snprintf(g->Message, sizeof(g->Message), "Unexpected '}' near %.*s", ARGS); + throw 3; + } // endif level + + return; + case '\n': + case '\r': + case ' ': + case '\t': + break; + default: + snprintf(g->Message, sizeof(g->Message), "Unexpected character '%c' near %.*s", s[i], ARGS); + throw 3; + }; // endswitch s[i] + + throw "Unexpected EOF in Object"; +} // end of CopyObject + +/***********************************************************************/ +/* Copy a JSON Array. */ +/***********************************************************************/ +void JUP::CopyArray(PGLOBAL g) { + int level = 0; + + for (; i < len; i++) + switch (s[i]) { + case ',': + if (level < 2) { + snprintf(g->Message, sizeof(g->Message), "Unexpected ',' near %.*s", ARGS); + throw 2; + } else + level = 1; + + AddBuff(s[i]); + break; + case ']': + if (level == 1) { + snprintf(g->Message, sizeof(g->Message), "Unexpected ',]' near %.*s", ARGS); + throw 2; + } // endif level + + AddBuff(s[i]); + return; + case '\n': + case '\r': + case ' ': + case '\t': + break; + default: + if (level == 2) { + snprintf(g->Message, sizeof(g->Message), "Unexpected value near %.*s", ARGS); + throw 2; + } // endif level + + CopyValue(g); + level = 2; + break; + }; // endswitch s[i] + + throw "Unexpected EOF in array"; +} // end of CopyArray + +/***********************************************************************/ +/* Copy a JSON Value. */ +/***********************************************************************/ +void JUP::CopyValue(PGLOBAL g) { + for (; i < len; i++) + switch (s[i]) { + case '\n': + case '\r': + case ' ': + case '\t': + break; + default: + goto suite; + } // endswitch + +suite: + switch (s[i]) { + case '[': + AddBuff(s[i++]); + CopyArray(g); + break; + case '{': + AddBuff(s[i++]); + CopyObject(g); + break; + case '"': + AddBuff(s[i++]); + CopyString(g); + break; + case 't': + if (!strncmp(s + i, "true", 4)) { + AddBuff(s[i++]); + AddBuff(s[i++]); + AddBuff(s[i++]); + AddBuff(s[i]); + } else + goto err; + + break; + case 'f': + if (!strncmp(s + i, "false", 5)) { + AddBuff(s[i++]); + AddBuff(s[i++]); + AddBuff(s[i++]); + AddBuff(s[i++]); + AddBuff(s[i]); + } else + goto err; + + break; + case 'n': + if (!strncmp(s + i, "null", 4)) { + AddBuff(s[i++]); + AddBuff(s[i++]); + AddBuff(s[i++]); + AddBuff(s[i]); + } else + goto err; + + break; + default: + if (s[i] == '-' || isdigit(s[i])) + CopyNumeric(g); + else + goto err; + + }; // endswitch s[i] + + return; + +err: + snprintf(g->Message, sizeof(g->Message), "Unexpected character '%c' near %.*s", s[i], ARGS); + throw 1; +} // end of CopyValue + +/***********************************************************************/ +/* Unescape and parse a JSON string. */ +/***********************************************************************/ +void JUP::CopyString(PGLOBAL g) { + for (; i < len; i++) { + AddBuff(s[i]); + + switch (s[i]) { + case '"': + return; + case '\\': + AddBuff(s[++i]); + break; + default: + break; + }; // endswitch s[i] + + } // endfor i + + throw "Unexpected EOF in String"; +} // end of CopyString + +/***********************************************************************/ +/* Copy a JSON numeric value. */ +/***********************************************************************/ +void JUP::CopyNumeric(PGLOBAL g) { + bool has_dot = false; + bool has_e = false; + bool found_digit = false; + + for (; i < len; i++) { + switch (s[i]) { + case '.': + if (!found_digit || has_dot || has_e) + goto err; + + has_dot = true; + break; + case 'e': + case 'E': + if (!found_digit || has_e) + goto err; + + has_e = true; + found_digit = false; + break; + case '+': + if (!has_e) + goto err; + + // fall through + case '-': + if (found_digit) + goto err; + + break; + default: + if (isdigit(s[i])) { + found_digit = true; + } else + goto fin; + + }; // endswitch s[i] + + AddBuff(s[i]); + } // endfor i + +fin: + if (!found_digit) + throw "No digit found"; + else + i--; + + return; + +err: + throw "Unexpected EOF in number"; +} // end of CopyNumeric + +/*********************************************************************************/ +/* Utility function returning an environment variable value. */ +/*********************************************************************************/ +my_bool envar_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1) { + strcpy(message, "Unique argument must be an environment variable name"); + return true; + } else { + initid->maybe_null = true; + return false; + } // endif count + +} // end of envar_init + +char *envar(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *str, name[256]; + int n = MY_MIN(args->lengths[0], sizeof(name) - 1); + + memcpy(name, args->args[0], n); + name[n] = 0; + + if (!(str = getenv(name))) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of envar + +#if defined(DEVELOPMENT) +extern char *GetUserVariable(PGLOBAL g, const uchar *varname); + +/*********************************************************************************/ +/* Utility function returning a user variable value. */ +/*********************************************************************************/ +my_bool uvar_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count != 1) { + strcpy(message, "Unique argument must be a user variable name"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + initid->maybe_null = true; + return JsonInit(initid, args, message, true, reslen, memlen, 2048); +} // end of uvar_init + +char *uvar(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *str, varname[256]; + PGLOBAL g = (PGLOBAL)initid->ptr; + int n = MY_MIN(args->lengths[0], sizeof(varname) - 1); + + PlugSubSet(g->Sarea, g->Sarea_Size); + memcpy(varname, args->args[0], n); + varname[n] = 0; + + if (!(str = GetUserVariable(g, (const uchar*)&varname))) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of uvar + +void uvar_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of uvar_deinit +#endif // DEVELOPMENT + +/*********************************************************************************/ +/* Returns the distinct number of B occurences in A. */ +/*********************************************************************************/ +my_bool countin_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 2) { + strcpy(message, "This function must have 2 arguments"); + return true; + } else if (args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be string"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string"); + return true; + } // endif args + + return false; +} // end of countin_init + +long long countin(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *) +{ + PSZ str1, str2; + char *s; + long long n = 0; + size_t lg; + + lg = (size_t)args->lengths[0]; + s = str1 = (PSZ)malloc(lg + 1); + memcpy(str1, args->args[0], lg); + str1[lg] = 0; + + lg = (size_t)args->lengths[1]; + str2 = (PSZ)malloc(lg + 1); + memcpy(str2, args->args[1], lg); + str2[lg] = 0; + + while ((s = strstr(s, str2))) { + n++; + s += lg; + } // endwhile + + free(str1); + free(str2); + return n; +} // end of countin |