summaryrefslogtreecommitdiffstats
path: root/storage/connect/jsonudf.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
commit3f619478f796eddbba6e39502fe941b285dd97b1 (patch)
treee2c7b5777f728320e5b5542b6213fd3591ba51e2 /storage/connect/jsonudf.cpp
parentInitial commit. (diff)
downloadmariadb-3f619478f796eddbba6e39502fe941b285dd97b1.tar.xz
mariadb-3f619478f796eddbba6e39502fe941b285dd97b1.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.cpp6646
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