/****************** jsonudf C++ Program Source Code File (.CPP) ******************/ /* PROGRAM NAME: jsonudf Version 1.6 */ /* (C) Copyright to the author Olivier BERTRAND 2015-2017 */ /* This program are the JSON User Defined Functions . */ /*********************************************************************************/ /*********************************************************************************/ /* Include relevant sections of the MariaDB header file. */ /*********************************************************************************/ #include #include #include #include #include #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, 0, M) #else #define PUSH_WARNING(M) htrc(M) #endif #define M 7 bool IsNum(PSZ s); char *NextChr(PSZ s, char sep); char *GetJsonNull(void); uint GetJsonGrpSize(void); static int IsJson(UDF_ARGS *args, uint i); static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i); 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); void json_array_deinit(UDF_INIT* initid); static uint JsonGrpSize = 10; /* ----------------------------------- 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); #if 0 if (jb) { // Path must return a Json item size_t n = strlen(path); if (!n || path[n - 1] != '*') { Jpath = (char*)PlugSubAlloc(g, NULL, n + 3); strcat(strcpy(Jpath, path), (n) ? ":*" : "*"); } else Jpath = path; } else #endif // 0 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 sprintf(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) { sprintf(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 strcpy(g->Message, "Expand not supported by this function"); return true; default: sprintf(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) htrc("Concat string=%s\n", p + 1); jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING); } // endif n } else { strcpy(g->Message, "Wrong array specification"); return true; } // endif's // For calculated arrays, a local Value must be used switch (jnp->Op) { case OP_NUM: jnp->Valp = AllocateValue(g, TYPE_INT); break; case OP_ADD: case OP_MULT: case OP_SEP: if (!IsTypeChar(Buf_Type)) jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision()); else jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2); break; case OP_MIN: case OP_MAX: jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision()); break; case OP_CNC: if (IsTypeChar(Buf_Type)) jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision()); else jnp->Valp = AllocateValue(g, TYPE_STRING, 512); break; default: break; } // endswitch Op if (jnp->Valp) MulVal = AllocateValue(g, jnp->Valp); return false; } // end of SetArrayOptions /*********************************************************************************/ /* Parse the eventual passed Jpath information. */ /* This information can be specified in the Fieldfmt column option when */ /* creating the table. It permits to indicate the position of the node */ /* corresponding to that column. */ /*********************************************************************************/ my_bool JSNX::ParseJpath(PGLOBAL g) { char *p, *p1 = NULL, *p2 = NULL, *pbuf = NULL; int i; my_bool a, mul = false; if (Parsed) return false; // Already done else if (!Jpath) // Jpath = Name; return true; if (trace) 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) { sprintf(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) 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 /*********************************************************************************/ /* MakeJson: Serialize the json item and set value to it. */ /*********************************************************************************/ PVAL JSNX::MakeJson(PGLOBAL g, PJSON jsp) { if (Value->IsTypeNum()) { strcpy(g->Message, "Cannot make Json for a numeric value"); Value->Reset(); } else if (jsp->GetType() != TYPE_JAR && jsp->GetType() != TYPE_JOB) { strcpy(g->Message, "Target is not an array or object"); Value->Reset(); } else Value->SetValue_psz(Serialize(g, jsp, NULL, 0)); return Value; } // end of MakeJson /*********************************************************************************/ /* SetValue: Set a value from a JVALUE contains. */ /*********************************************************************************/ void JSNX::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n) { if (val) { vp->SetNull(false); if (Jb) { vp->SetValue_psz(Serialize(g, val->GetJsp(), NULL, 0)); } else switch (val->GetValType()) { case TYPE_STRG: case TYPE_INTG: case TYPE_BINT: case TYPE_DBL: vp->SetValue_pval(val->GetValue()); break; case TYPE_BOOL: if (vp->IsTypeNum()) vp->SetValue(val->GetInteger() ? 1 : 0); else vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false")); break; case TYPE_JAR: SetJsonValue(g, vp, val->GetArray()->GetValue(0), n); break; case TYPE_JOB: // if (!vp->IsTypeNum() || !Strict) { vp->SetValue_psz(val->GetObject()->GetText(g, NULL)); break; // } // endif Type case TYPE_NULL: vp->SetNull(true); default: vp->Reset(); } // endswitch Type } else { vp->SetNull(true); vp->Reset(); } // endif val } // end of SetJsonValue /*********************************************************************************/ /* 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) { int n = Nod - 1; PJVAL val = NULL; val = GetRowValue(g, row, i); SetJsonValue(g, Value, val, n); return Value; } // end of GetColumnValue /*********************************************************************************/ /* GetRowValue: */ /*********************************************************************************/ PJVAL JSNX::GetRowValue(PGLOBAL g, PJSON row, int i, my_bool b) { my_bool expd = false; PJAR arp; PJVAL val = NULL; for (; i < Nod && row; i++) { if (Nodes[i].Op == OP_NUM) { Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1); val = new(g) JVALUE(g, Value); return val; } else if (Nodes[i].Op == OP_XX) { Jb = b; return new(g)JVALUE(row); } 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 { strcpy(g->Message, "Unexpected object"); val = NULL; } //endif Op } else val = ((PJOB)row)->GetValue(Nodes[i].Key); break; case TYPE_JAR: arp = (PJAR)row; if (!Nodes[i].Key) { if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE) val = arp->GetValue(Nodes[i].Rank); else if (Nodes[i].Op == OP_EXP) return (PJVAL)ExpandArray(g, arp, i); else return new(g) JVALUE(g, CalculateArray(g, arp, i)); } else { // Unexpected array, unwrap it as [0] val = arp->GetValue(0); i--; } // endif's break; case TYPE_JVAL: val = (PJVAL)row; break; default: sprintf(g->Message, "Invalid row JSON type %d", row->GetType()); val = NULL; } // endswitch Type if (i < Nod-1) if (!(row = (val) ? val->GetJsp() : NULL)) val = NULL; // row = (val) ? val->GetJson() : NULL; } // endfor i // SetJsonValue(g, Value, val, n); return val; } // end of GetRowValue /*********************************************************************************/ /* ExpandArray: */ /*********************************************************************************/ PVAL JSNX::ExpandArray(PGLOBAL g, PJAR arp, int n) { strcpy(g->Message, "Expand cannot be done by this function"); return NULL; } // end of ExpandArray /*********************************************************************************/ /* CalculateArray: */ /*********************************************************************************/ PVAL JSNX::CalculateArray(PGLOBAL g, PJAR arp, int n) { //int i, ars, nv = 0, nextsame = Tjp->NextSame; int i, nv = 0, nextsame = 0; my_bool err; OPVAL op = Nodes[n].Op; PVAL val[2], vp = Nodes[n].Valp; PJVAL jvrp, jvp; JVALUE jval; vp->Reset(); //ars = arp->size(); if (trace) htrc("CalculateArray size=%d\n", arp->size()); // htrc("CalculateArray size=%d\n", ars); for (i = 0; i < arp->size(); i++) { //for (i = 0; i < ars; i++) { because compiler bug jvrp = arp->GetValue(i); if (trace) htrc("Value %s null=%d nv=%d\n", jvrp->GetString(g), jvrp->IsNull() ? 1 : 0, nv); if (!jvrp->IsNull() || (op == OP_CNC && GetJsonNull())) { if (jvrp->IsNull()) { jvrp->Value = AllocateValue(g, GetJsonNull(), TYPE_STRING); jvp = jvrp; } else if (n < Nod - 1 && jvrp->GetJson()) { jval.SetValue(GetColumnValue(g, jvrp->GetJson(), n + 1)); jvp = &jval; } else jvp = jvrp; if (!nv++) { SetJsonValue(g, vp, jvp, n); continue; } else SetJsonValue(g, MulVal, jvp, n); if (!MulVal->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] = Nodes[n].Valp; val[1] = MulVal; err = vp->Compute(g, val, 2, OP_ADD); break; default: val[0] = Nodes[n].Valp; val[1] = MulVal; err = vp->Compute(g, val, 2, op); } // endswitch Op if (err) vp->Reset(); } // endif Zero } // 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 //Tjp->NextSame = nextsame; 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)->GetValue(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)->GetValue(Nodes[i].Rank); break; case TYPE_JVAL: val = (PJVAL)row; break; default: sprintf(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)->GetValue(Nodes[i].Key); break; case TYPE_JAR: arp = (PJAR)row; if (!Nodes[i].Key) { if (Nodes[i].Op == OP_EQ) val = arp->GetValue(Nodes[i].Rank); else val = arp->GetValue(Nodes[i].Rx); } else { // Unexpected array, unwrap it as [0] val = arp->GetValue(0); i--; } // endif Nodes break; case TYPE_JVAL: val = (PJVAL)row; break; default: sprintf(g->Message, "Invalid row JSON type %d", row->GetType()); val = NULL; } // endswitch Type if (val) { row = val->GetJson(); } else { // Construct missing objects for (i++; row && i < Nod; i++) { if (Nodes[i].Op == OP_XX) break; else if (!Nodes[i].Key) // Construct intermediate array nwr = new(g)JARRAY; else nwr = new(g)JOBJECT; if (row->GetType() == TYPE_JOB) { ((PJOB)row)->SetValue(g, new(g)JVALUE(nwr), Nodes[i-1].Key); } else if (row->GetType() == TYPE_JAR) { ((PJAR)row)->AddValue(g, new(g)JVALUE(nwr)); ((PJAR)row)->InitArray(g); } else { strcpy(g->Message, "Wrong type when writing new row"); nwr = NULL; } // endif's row = nwr; } // endfor i break; } // endelse } // endfor i return row; } // end of GetRow /***********************************************************************/ /* 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: strcpy(g->Message, "Invalid target type"); return true; } // endswitch Type if (arp) { if (!Nodes[Nod-1].Key) { if (Nodes[Nod-1].Op == OP_EQ) arp->SetValue(g, jvalp, Nodes[Nod-1].Rank); else arp->AddValue(g, jvalp); arp->InitArray(g); } // endif Key } else if (objp) { if (Nodes[Nod-1].Key) objp->SetValue(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) { my_bool b = false, err = true; g->Message[0] = 0; if (!jsp) { strcpy(g->Message, "Null json tree"); return NULL; } // endif jsp // Write to the path string Jp = new(g) JOUTSTR(g); Jp->WriteChr('$'); Jvalp = jvp; K = k; switch (jsp->GetType()) { case TYPE_JAR: err = LocateArray((PJAR)jsp); break; case TYPE_JOB: err = LocateObject((PJOB)jsp); break; case TYPE_JVAL: err = LocateValue((PJVAL)jsp); break; default: err = true; } // endswitch Type if (err) { if (!g->Message[0]) strcpy(g->Message, "Invalid json tree"); } else if (Found) { Jp->WriteChr('\0'); PlugSubAlloc(g, NULL, Jp->N); return Jp->Strp; } // endif's return NULL; } // end of Locate /*********************************************************************************/ /* Locate in a JSON Array. */ /*********************************************************************************/ my_bool JSNX::LocateArray(PJAR jarp) { char s[16]; size_t m = Jp->N; for (int i = 0; i < jarp->size() && !Found; i++) { Jp->N = m; sprintf(s, "[%d]", i + B); if (Jp->WriteStr(s)) return true; if (LocateValue(jarp->GetValue(i))) return true; } // endfor i return false; } // end of LocateArray /*********************************************************************************/ /* Locate in a JSON Object. */ /*********************************************************************************/ my_bool JSNX::LocateObject(PJOB jobp) { size_t m; 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(pair->Val)) return true; } // endfor i return false; } // end of LocateObject /*********************************************************************************/ /* Locate a JSON Value. */ /*********************************************************************************/ my_bool JSNX::LocateValue(PJVAL jvp) { if (CompareTree(Jvalp, jvp)) Found = (--K == 0); else if (jvp->GetArray()) return LocateArray(jvp->GetArray()); else if (jvp->GetObject()) return LocateObject(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) { my_bool b = false, err = true; PJPN jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx); memset(jnp, 0, sizeof(JPN) * mx); g->Message[0] = 0; if (!jsp) { strcpy(g->Message, "Null json tree"); return NULL; } // endif jsp // 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((PJAR)jsp); break; case TYPE_JOB: err = LocateObjectAll((PJOB)jsp); break; case TYPE_JVAL: err = LocateValueAll((PJVAL)jsp); break; default: err = true; } // endswitch Type if (err) { if (!g->Message[0]) strcpy(g->Message, "Invalid json tree"); return NULL; } else { if (Jp->N > 1) Jp->N--; Jp->WriteChr(']'); Jp->WriteChr('\0'); PlugSubAlloc(g, NULL, Jp->N); return Jp->Strp; } // endif's } // end of LocateAll /*********************************************************************************/ /* Locate in a JSON Array. */ /*********************************************************************************/ my_bool JSNX::LocateArrayAll(PJAR jarp) { if (I < Imax) { Jpnp[++I].Type = TYPE_JAR; for (int i = 0; i < jarp->size(); i++) { Jpnp[I].N = i; if (LocateValueAll(jarp->GetValue(i))) return true; } // endfor i I--; } // endif I return false; } // end of LocateArrayAll /*********************************************************************************/ /* Locate in a JSON Object. */ /*********************************************************************************/ my_bool JSNX::LocateObjectAll(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(pair->Val)) return true; } // endfor i I--; } // endif I return false; } // end of LocateObjectAll /*********************************************************************************/ /* Locate a JSON Value. */ /*********************************************************************************/ my_bool JSNX::LocateValueAll(PJVAL jvp) { if (CompareTree(Jvalp, jvp)) return AddPath(); else if (jvp->GetArray()) return LocateArrayAll(jvp->GetArray()); else if (jvp->GetObject()) return LocateObjectAll(jvp->GetObject()); return false; } // end of LocateValueAll /*********************************************************************************/ /* Compare two JSON trees. */ /*********************************************************************************/ my_bool JSNX::CompareTree(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) { PVAL v1 = jp1->GetValue(), v2 = jp2->GetValue(); if (v1 && v2) { if (v1->GetType() == v2->GetType()) found = !v1->CompareValue(v2); else found = false; } else found = CompareTree(jp1->GetJsp(), jp2->GetJsp()); } else if (jp1->GetType() == TYPE_JAR) { for (int i = 0; found && i < jp1->size(); i++) found = (CompareTree(jp1->GetValue(i), jp2->GetValue(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(p1->Val, p2->Val); } else found = false; return found; } // end of CompareTree /*********************************************************************************/ /* 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) { sprintf(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 ---------------------------------- */ // BSON size should be equal on Linux and Windows #define BMX 255 typedef struct BSON *PBSON; /*********************************************************************************/ /* Structure used to return binary json. */ /*********************************************************************************/ struct BSON { char Msg[BMX + 1]; char *Filename; PGLOBAL G; int Pretty; ulong Reslen; my_bool Changed; PJSON Top; PJSON Jsp; PBSON Bsp; }; // end of struct BSON /*********************************************************************************/ /* Allocate and initialize a BSON structure. */ /*********************************************************************************/ static 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 = (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. */ /*********************************************************************************/ static uint GetJsonGroupSize(void) { return (JsonGrpSize) ? JsonGrpSize : GetJsonGrpSize(); } // end of GetJsonGroupSize /*********************************************************************************/ /* Program for SubSet re-initialization of the memory pool. */ /*********************************************************************************/ static my_bool JsonSubSet(PGLOBAL g) { PPOOLHEADER pph = (PPOOLHEADER)g->Sarea; pph->To_Free = (OFFSET)((g->Createas) ? g->Createas : sizeof(POOLHEADER)); pph->FreeBlk = g->Sarea_Size - pph->To_Free; return FALSE; } /* end of JsonSubSet */ /*********************************************************************************/ /* Program for saving the status of the memory pools. */ /*********************************************************************************/ inline void JsonMemSave(PGLOBAL g) { g->Createas = (int)((PPOOLHEADER)g->Sarea)->To_Free; } /* end of JsonMemSave */ /*********************************************************************************/ /* Program for freeing the memory pools. */ /*********************************************************************************/ inline void JsonFreeMem(PGLOBAL g) { g->Activityp = NULL; PlugExit(g); } /* end of JsonFreeMem */ /*********************************************************************************/ /* Allocate and initialise the memory area. */ /*********************************************************************************/ static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args, char *message, my_bool mbn, unsigned long reslen, unsigned long memlen, unsigned long more = 0) { PGLOBAL g = PlugInit(NULL, 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); 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))) { sprintf(g->Message, "No sub-item at '%s'", path); return true; } // endif jvp } else { strcpy(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); strncpy(bsnp->Msg, bsnp->Filename, BMX); } else strncpy(bsnp->Msg, "null filename", BMX); } else if (IsJson(args, 0) == 3) { PBSON bsp = (PBSON)args->args[0]; if (bsp->Filename) { bsnp->Filename = bsp->Filename; strncpy(bsnp->Msg, bsp->Filename, BMX); 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]) { x = (int*)PlugSubAlloc(g, NULL, sizeof(int)); *x = (int)*(longlong*)args->args[i]; } // 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. */ /*********************************************************************************/ static int IsJson(UDF_ARGS *args, uint i) { int n = 0; if (i >= args->arg_count || args->arg_type[i] != STRING_RESULT) { } else if (!strnicmp(args->attributes[i], "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(args->attributes[i], "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(args->attributes[i], "Jfile_", 6)) n = 2; // arg is a json file name 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. */ /*********************************************************************************/ static my_bool CalcLen(UDF_ARGS *args, my_bool obj, unsigned long& reslen, unsigned long& memlen, my_bool mod = false) { 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); break; case INT_RESULT: memlen += sizeof(TYPVAL); break; case REAL_RESULT: case DECIMAL_RESULT: memlen += sizeof(TYPVAL); 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. */ /*********************************************************************************/ static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n, my_bool m, my_bool obj = false, my_bool mod = false) { 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) { #if !defined(DEVELOPMENT) if (trace) #endif htrc("Freeing Sarea at %p size=%d\n", g->Sarea, g->Sarea_Size); free(g->Sarea); if (!(g->Sarea = PlugAllocMem(g, ml))) { char errmsg[MAX_STR]; snprintf(errmsg, sizeof(errmsg)-1, MSG(WORK_AREA), g->Message); strcpy(g->Message, errmsg); g->Sarea_Size = 0; return true; } // endif Alloc g->Sarea_Size = ml; g->Createas = 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. */ /*********************************************************************************/ static 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)PlugSubAlloc(g, NULL, n + 1); memcpy(s, args->args[i], n); s[n] = 0; 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, 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 "Key"; if (!b) { p = (PSZ)PlugSubAlloc(g, NULL, n + 1); memcpy(p, s, n); p[n] = 0; s = p; } // endif b } // endif s return s; } // endif count return "Key"; } // end of MakeKey /*********************************************************************************/ /* Parse a json file. */ /*********************************************************************************/ static PJSON ParseJsonFile(PGLOBAL g, char *fn, int *pretty, int& 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)) sprintf(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) */ /*******************************************************************************/ len = mm.lenL; memory = (char *)mm.memory; if (!len) { // Empty or deleted file CloseFileHandle(hFile); return NULL; } // endif len if (!memory) { CloseFileHandle(hFile); sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, GetLastError()); return NULL; } // endif Memory CloseFileHandle(hFile); // Not used anymore /*********************************************************************************/ /* 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. */ /*********************************************************************************/ static 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) { sprintf(g->Message, "Error %d opening %s", errno, fn); return NULL; } // endif h if ((len = _filelength(h)) < 0) { sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn); close(h); return NULL; } // endif len str = (char*)PlugSubAlloc(g, NULL, len + 1); if ((n = read(h, str, len)) < 0) { sprintf(g->Message, "Error %d reading %d bytes from %s", errno, len, fn); return NULL; } // endif n str[n] = 0; close(h); return str; } // end of GetJsonFile /*********************************************************************************/ /* Make a JSON value from the passed argument. */ /*********************************************************************************/ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, uint i, PJSON *top = NULL) { char *sap = (args->arg_count > i) ? args->args[i] : NULL; int n, len; short c; 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 { c = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1; jvp->SetString(g, sap, c); } // 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->SetTiny(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 /*********************************************************************************/ /* 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->AddValue(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) && args->arg_type[0] != STRING_RESULT) { strcpy(message, "First argument must be a 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)) { char *p; PJSON top; PJAR arp; PJVAL jvp = MakeValue(g, args, 0, &top); if ((p = jvp->GetString(g))) { if (!(top = ParseJson(g, p, strlen(p)))) { PUSH_WARNING(g->Message); return NULL; } // endif jsp jvp->SetValue(top); } // endif p if (jvp->GetValType() != TYPE_JAR) { arp = new(g)JARRAY; arp->AddValue(g, jvp); } else arp = jvp->GetArray(); for (uint i = 1; i < args->arg_count; i++) arp->AddValue(g, MakeValue(g, args, i)); arp->InitArray(g); // str = Serialize(g, arp, NULL, 0); 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)) { strcpy(message, "First 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_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 = MakeValue(g, args, 0, &top); jsp = jvp->GetJson(); x = GetIntArgPtr(g, args, n); if (CheckPath(g, args, jsp, jvp, 2)) PUSH_WARNING(g->Message); else if (jvp && jvp->GetValType() == TYPE_JAR) { PGLOBAL gb = GetMemPtr(g, args, 0); arp = jvp->GetArray(); arp->AddValue(gb, MakeValue(gb, args, 1), x); arp->InitArray(gb); str = MakeResult(g, args, top, n); } 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 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 if (!IsJson(args, 0)) { strcpy(message, "First 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_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 = MakeValue(g, args, 0, &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->GetValue(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 = (long long*)PlugSubAlloc(g, NULL, sizeof(long long)); *np = n; g->Activityp = (PACTIVITY)np; } // 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->GetValue(i)->GetFloat(); } else { PUSH_WARNING("First argument target is not an array"); } // endif jvp } else { *error = 1; n = -1.0; } // end of CheckMemory if (g->N) { // Keep result of constant function double *np = (double*)PlugSubAlloc(g, NULL, sizeof(double)); *np = n; g->Activityp = (PACTIVITY)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->GetValue(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; } // end of CheckMemory if (g->N) { // Keep result of constant function double *np = (double*)PlugSubAlloc(g, NULL, sizeof(double)); *np = n; g->Activityp = (PACTIVITY)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 = new(g)JOBJECT; for (uint i = 0; i < args->arg_count; i++) objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i)); str = Serialize(g, objp, NULL, 0); } // endif CheckMemory if (!str) str = strcpy(result, g->Message); // 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 = new(g)JOBJECT; for (uint i = 0; i < args->arg_count; i++) if (!(jvp = MakeValue(g, args, i))->IsNull()) objp->SetValue(g, jvp, MakeKey(g, args, i)); str = Serialize(g, objp, NULL, 0); } // endif CheckMemory if (!str) str = strcpy(result, g->Message); // 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 = new(g)JOBJECT; for (uint i = 0; i < args->arg_count; i += 2) objp->SetValue(g, MakeValue(g, args, i+1), MakePSZ(g, args, i)); str = Serialize(g, objp, NULL, 0); } // 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->SetValue(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 /*********************************************************************************/ /* 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, g->Sarea, g->Sarea_Size); g->Activityp = (PACTIVITY)new(g) JARRAY; 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 (g->N-- > 0) arp->AddValue(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"); arp->InitArray(g); if (!(str = Serialize(g, arp, NULL, 0))) 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, g->Sarea, g->Sarea_Size); g->Activityp = (PACTIVITY)new(g) JARRAY; 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, g->Sarea, g->Sarea_Size); g->Activityp = (PACTIVITY)new(g) JOBJECT; 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->SetValue(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 (!(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, g->Sarea, g->Sarea_Size); g->Activityp = (PACTIVITY)new(g) JOBJECT; 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) { sprintf(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 *p, *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; } else 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 (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 = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length); if (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 = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length); if (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) htrc("Exception %d: %s\n", n, g->Message); PUSH_WARNING(g->Message); str = NULL; } catch (const char *msg) { strcpy(g->Message, 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 = new(g) JSNX(g, jsp, TYPE_BIGINT); if (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*)PlugSubAlloc(g, NULL, sizeof(long long)); *np = n; g->Activityp = (PACTIVITY)np; } // 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 = new(g) JSNX(g, jsp, TYPE_DOUBLE); if (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 = (double*)PlugSubAlloc(g, NULL, sizeof(double)); *dp = d; g->Activityp = (PACTIVITY)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 *p, *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 = 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); 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) htrc("Exception %d: %s\n", n, g->Message); PUSH_WARNING(g->Message); *error = 1; path = NULL; } catch (const char *msg) { strcpy(g->Message, 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) htrc("Exception %d: %s\n", n, g->Message); PUSH_WARNING(g->Message); *error = 1; path = NULL; } catch (const char *msg) { strcpy(g->Message, 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 *result, unsigned long *res_length, char *is_null, char *error) { char *p __attribute__((unused)), res[256]; long long n; unsigned long reslen; *is_null = 0; p = jsonlocate(initid, args, res, &reslen, is_null, error); n = (*is_null) ? 0LL : 1LL; return n; } // 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 *result, unsigned long *res_length, 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"); 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 = new(g)JSNX(g, jsp, TYPE_BIGINT); if (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*)PlugSubAlloc(g, NULL, sizeof(long long)); *np = n; g->Activityp = (PACTIVITY)np; } // endif const_item return n; err: if (g->Mrr) *error = 1; *is_null = 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) htrc("Exception %d: %s\n", n, g->Message); PUSH_WARNING(g->Message); str = NULL; } catch (const char *msg) { strcpy(g->Message, 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, g->Sarea, g->Sarea_Size); fn = MakePSZ(g, args, 0); if (args->arg_count > 1) { int len, pretty, pty = 3; PJSON jsp; PJVAL jvp = NULL; pretty = (args->arg_type[1] == INT_RESULT) ? (int)*(longlong*)args->args[1] : (args->arg_count > 2 && args->arg_type[2] == INT_RESULT) ? (int)*(longlong*)args->args[2] : 3; /*******************************************************************************/ /* 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); 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 = new(g) JARRAY; bsp = JbinAlloc(g, args, initid->max_length, arp); strcat(bsp->Msg, " array"); for (uint i = 0; i < args->arg_count; i++) arp->AddValue(g, MakeValue(g, args, i)); arp->InitArray(g); } else if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) strncpy(bsp->Msg, g->Message, 139); // 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 if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { strcpy(message, "First argument must be a json string or item"); 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)) { char *p; PJSON top; PJAR arp; PJVAL jvp = MakeValue(g, args, 0, &top); PGLOBAL gb = GetMemPtr(g, args, 0); if ((p = jvp->GetString(g))) { if (!(top = ParseJson(g, p, strlen(p)))) { PUSH_WARNING(g->Message); return NULL; } // endif jsp jvp->SetValue(top); } // endif p if (jvp->GetValType() != TYPE_JAR) { arp = new(gb)JARRAY; arp->AddValue(gb, jvp); } else arp = jvp->GetArray(); for (uint i = 1; i < args->arg_count; i++) arp->AddValue(gb, MakeValue(gb, args, i)); arp->InitArray(gb); if ((bsp = JbinAlloc(g, args, initid->max_length, top))) { strcat(bsp->Msg, " array"); bsp->Jsp = arp; } // endif bsp } else if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) strncpy(bsp->Msg, g->Message, BMX); // 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 = MakeValue(g, args, 0, &top); // jsp = jvp->GetJson(); x = GetIntArgPtr(g, args, n); if (CheckPath(g, args, top, jvp, n)) PUSH_WARNING(g->Message); else if (jvp && jvp->GetValType() == TYPE_JAR) { PGLOBAL gb = GetMemPtr(g, args, 0); arp = jvp->GetArray(); arp->AddValue(gb, MakeValue(gb, args, 1), x); arp->InitArray(gb); } else { PUSH_WARNING("First argument 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 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_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 = MakeValue(g, args, 0, &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 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 = new(g)JOBJECT; for (uint i = 0; i < args->arg_count; i++) objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i)); if ((bsp = JbinAlloc(g, args, initid->max_length, objp))) strcat(bsp->Msg, " object"); } else if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) strncpy(bsp->Msg, g->Message, BMX); // 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 = new(g)JOBJECT; for (uint i = 0; i < args->arg_count; i++) if (!(jvp = MakeValue(g, args, i))->IsNull()) objp->SetValue(g, jvp, MakeKey(g, args, i)); if ((bsp = JbinAlloc(g, args, initid->max_length, objp))) strcat(bsp->Msg, " object"); } else if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) strncpy(bsp->Msg, g->Message, BMX); // 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 = new(g)JOBJECT; for (uint i = 0; i < args->arg_count; i += 2) objp->SetValue(g, MakeValue(g, args, i+1), MakePSZ(g, args, i)); if ((bsp = JbinAlloc(g, args, initid->max_length, objp))) strcat(bsp->Msg, " object"); } else if ((bsp = JbinAlloc(g, args, initid->max_length, NULL))) strncpy(bsp->Msg, g->Message, BMX); // 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->SetValue(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); PJSON jsp = jvp->GetJson(); if (CheckPath(g, args, top, jvp, 2)) PUSH_WARNING(g->Message); else if (jvp && jvp->GetValType() == TYPE_JOB) { key = MakeKey(g, args, 1); jobp = jvp->GetObject(); jobp->DeleteKey(key); } 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))) strcat(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 *p, *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; } 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; path = MakePSZ(g, args, 1); jsx = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length); if (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() : new(g) JVALUE(g, jvp->GetValue()); if ((bsp = JbinAlloc(g, args, initid->max_length, jsp))) strcat(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) { sprintf(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; } else if (args->arg_count > 1 && args->arg_type[1] != STRING_RESULT) { strcpy(message, "Second argument is not a string (path)"); return true; } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) { strcpy(message, "Third argument is not an integer (pretty)"); return true; } 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[3]; } // endifs 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, len = 0, pty = 3; PJSON jsp; PJVAL jvp = NULL; PGLOBAL g = (PGLOBAL)initid->ptr; PBSON bsp = (PBSON)g->Xchk; if (bsp && !bsp->Changed) goto fin; PlugSubSet(g, g->Sarea, g->Sarea_Size); g->Xchk = NULL; fn = MakePSZ(g, args, 0); pretty = (args->arg_count > 2 && args->args[2]) ? (int)*(longlong*)args->args[2] : 3; /*********************************************************************************/ /* 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))) { strcat(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 /*********************************************************************************/ /* 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 /*********************************************************************************/ /* 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 *result, unsigned long *res_length, 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