diff options
Diffstat (limited to 'storage/connect/jsonudf.cpp')
-rw-r--r-- | storage/connect/jsonudf.cpp | 1476 |
1 files changed, 1475 insertions, 1 deletions
diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index c9f0ea9239a..0012b3d6bdd 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -63,7 +63,7 @@ static PJSNX JsnxNew(PGLOBAL g, PJSON jsp, int type, int len) return jsx; } /* end of JsnxNew */ - /* ----------------------------------- JSNX ------------------------------------ */ +/* ----------------------------------- JSNX ------------------------------------ */ /*********************************************************************************/ /* JSNX public constructor. */ @@ -1186,6 +1186,7 @@ static my_bool JsonSubSet(PGLOBAL g) pph->To_Free = (g->Saved_Size) ? g->Saved_Size : (size_t)sizeof(POOLHEADER); pph->FreeBlk = g->Sarea_Size - pph->To_Free; + g->Saved_Size = 0; return FALSE; } /* end of JsonSubSet */ @@ -6558,3 +6559,1476 @@ long long countin(UDF_INIT *initid, UDF_ARGS *args, char *result, free(str2); return n; } // end of countin + +/* --------------------------- New Testing BJSON Stuff --------------------------*/ + +/*********************************************************************************/ +/* SubAlloc a new BJNX class with protection against memory exhaustion. */ +/*********************************************************************************/ +static PBJNX BjnxNew(PGLOBAL g, PBVAL vlp, int type, int len) { + PBJNX bjnx; + + try { + bjnx = new(g) BJNX(g, vlp, type, len); + } catch (...) { + if (trace(1023)) + htrc("%s\n", g->Message); + + PUSH_WARNING(g->Message); + bjnx = NULL; + } // end try/catch + + return bjnx; +} /* end of BjnxNew */ + +/* ----------------------------------- BSNX ------------------------------------ */ + +/*********************************************************************************/ +/* BSNX public constructor. */ +/*********************************************************************************/ +BJNX::BJNX(PGLOBAL g, PBVAL row, int type, int len, int prec, my_bool wr) + : BDOC(g->Sarea) +{ + Row = row; + Bvalp = 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 BJNX constructor + +/*********************************************************************************/ +/* SetJpath: set and parse the json path. */ +/*********************************************************************************/ +my_bool BJNX::SetJpath(PGLOBAL g, char* path, my_bool jb) { + // Check Value was allocated + if (!Value) + return true; + + Value->SetNullable(true); + Jpath = path; + + // Parse the json path + Parsed = false; + Nod = 0; + Jb = jb; + return ParseJpath(g); +} // end of SetJpath + +/*********************************************************************************/ +/* Analyse array processing options. */ +/*********************************************************************************/ +my_bool BJNX::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(1)) + 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 BJNX::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(1)) + htrc("ParseJpath %s\n", SVP(Jpath)); + + if (!(pbuf = PlgDBDup(g, Jpath))) + return true; + + if (*pbuf == '$') pbuf++; + if (*pbuf == '.') pbuf++; + if (*pbuf == '[') p1 = pbuf++; + + // Estimate the required number of nodes + for (i = 0, p = pbuf; (p = NextChr(p, '.')); i++, p++) + Nod++; // One path node found + + if (!(Nodes = (PJNODE)PlgDBSubAlloc(g, NULL, (++Nod) * sizeof(JNODE)))) + return true; + + memset(Nodes, 0, (Nod) * sizeof(JNODE)); + + // Analyze the Jpath for this column + for (i = 0, p = pbuf; p && i < Nod; i++, p = (p2 ? p2 : NULL)) { + a = (p1 != NULL); + p1 = strchr(p, '['); + p2 = strchr(p, '.'); + + if (!p2) + p2 = p1; + else if (p1) { + if (p1 < p2) + p2 = p1; + else if (p1 == p2 + 1) + *p2++ = 0; // Old syntax .[ + else + p1 = NULL; + + } // endif p1 + + if (p2) + *p2++ = 0; + + // Jpath must be explicit + if (a || *p == 0 || *p == '[' || IsNum(p)) { + // Analyse intermediate array processing + if (SetArrayOptions(g, p, i, Nodes[i - 1].Key)) + return true; + + } else if (*p == '*') { + if (Wr) { + 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(1)) + for (i = 0; i < Nod; i++) + htrc("Node(%d) Key=%s Op=%d Rank=%d\n", + i, SVP(Nodes[i].Key), Nodes[i].Op, Nodes[i].Rank); + + Parsed = true; + return false; +} // end of ParseJpath + +/*********************************************************************************/ +/* MakeJson: Serialize the json item and set value to it. */ +/*********************************************************************************/ +PVAL BJNX::MakeJson(PGLOBAL g, PBVAL bvp) { + if (Value->IsTypeNum()) { + strcpy(g->Message, "Cannot make Json for a numeric value"); + Value->Reset(); + } else if (bvp->Type != TYPE_JAR && bvp->Type != TYPE_JOB) { + strcpy(g->Message, "Target is not an array or object"); + Value->Reset(); + } else + Value->SetValue_psz(Serialize(g, bvp, NULL, 0)); + + return Value; +} // end of MakeJson + +/*********************************************************************************/ +/* SetValue: Set a value from a JVALUE contains. */ +/*********************************************************************************/ +void BJNX::SetJsonValue(PGLOBAL g, PVAL vp, PBVAL vlp) { + if (vlp) { + vp->SetNull(false); + + if (Jb) { + vp->SetValue_psz(Serialize(g, vlp, NULL, 0)); + } else switch (vlp->Type) { + case TYPE_DTM: + case TYPE_STRG: + vp->SetValue_psz(GetString(g, vlp)); + break; + case TYPE_INTG: + case TYPE_BINT: + vp->SetValue(GetInteger(vlp)); + break; + case TYPE_DBL: + if (vp->IsTypeNum()) + vp->SetValue(GetDouble(vlp)); + else // Get the proper number of decimals + vp->SetValue_psz(GetString(g, vlp)); + + break; + case TYPE_BOOL: + if (vp->IsTypeNum()) + vp->SetValue(GetInteger(vlp) ? 1 : 0); + else + vp->SetValue_psz(GetString(g, vlp)); + + break; + case TYPE_JAR: + vp->SetValue_psz(GetArrayText(g, MVP(vlp->To_Val), NULL)); + break; + case TYPE_JOB: + vp->SetValue_psz(GetObjectText(g, MPP(vlp->To_Val), NULL)); + break; + case TYPE_NULL: + vp->SetNull(true); + default: + vp->Reset(); + } // endswitch Type + + } else { + vp->SetNull(true); + vp->Reset(); + } // endif val + +} // end of SetJsonValue + +/*********************************************************************************/ +/* GetJson: */ +/*********************************************************************************/ +PBVAL BJNX::GetJson(PGLOBAL g) { + return GetRowValue(g, Row, 0); +} // end of GetJson + +/*********************************************************************************/ +/* ReadValue: */ +/*********************************************************************************/ +void BJNX::ReadValue(PGLOBAL g) { + Value->SetValue_pval(GetColumnValue(g, Row, 0)); +} // end of ReadValue + +/*********************************************************************************/ +/* GetColumnValue: */ +/*********************************************************************************/ +PVAL BJNX::GetColumnValue(PGLOBAL g, PBVAL row, int i) { + PBVAL vlp = GetRowValue(g, row, i); + + SetJsonValue(g, Value, vlp); + return Value; +} // end of GetColumnValue + +/*********************************************************************************/ +/* GetRowValue: */ +/*********************************************************************************/ +PBVAL BJNX::GetRowValue(PGLOBAL g, PBVAL row, int i, my_bool b) { + my_bool expd = false; + PBVAL bap; + PBVAL vlp = NULL; + + for (; i < Nod && row; i++) { + if (Nodes[i].Op == OP_NUM) { + Value->SetValue(row->Type == TYPE_JAR ? GetArraySize(MVP(row->To_Val)) : 1); + vlp = SubAllocVal(g, Value); + return vlp; + } else if (Nodes[i].Op == OP_XX) { + Jb = b; +// return DupVal(g, row); + return row; // or last line ??? + } else switch (row->Type) { + case TYPE_JOB: + if (!Nodes[i].Key) { + // Expected Array was not there + if (Nodes[i].Op == OP_LE) { + if (i < Nod - 1) + continue; + else + vlp = row; // DupVal(g, row) ??? + + } else { + strcpy(g->Message, "Unexpected object"); + vlp = NULL; + } //endif Op + + } else + vlp = GetKeyValue(MPP(row->To_Val), Nodes[i].Key); + + break; + case TYPE_JAR: + bap = MVP(row->To_Val); + + if (!Nodes[i].Key) { + if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE) + vlp = GetArrayValue(bap, Nodes[i].Rank); + else if (Nodes[i].Op == OP_EXP) + return (PBVAL)ExpandArray(g, bap, i); + else + return SubAllocVal(g, CalculateArray(g, bap, i)); + + } else { + // Unexpected array, unwrap it as [0] + vlp = GetArrayValue(bap, 0); + i--; + } // endif's + + break; + case TYPE_JVAL: + vlp = row; + break; + default: + sprintf(g->Message, "Invalid row JSON type %d", row->Type); + vlp = NULL; + } // endswitch Type + + row = vlp; + } // endfor i + + return vlp; +} // end of GetRowValue + +/*********************************************************************************/ +/* ExpandArray: */ +/*********************************************************************************/ +PVAL BJNX::ExpandArray(PGLOBAL g, PBVAL arp, int n) +{ + strcpy(g->Message, "Expand cannot be done by this function"); + return NULL; +} // end of ExpandArray + +/*********************************************************************************/ +/* CalculateArray: NIY */ +/*********************************************************************************/ +PVAL BJNX::CalculateArray(PGLOBAL g, PBVAL bap, int n) +{ +#if 0 + int i, ars = GetArraySize(bap), nv = 0; + bool err; + OPVAL op = Nodes[n].Op; + PVAL val[2], vp = Nodes[n].Valp; + PBVAL bvrp, bvp; + BVAL bval; + + vp->Reset(); + xtrc(1,"CalculateArray size=%d op=%d\n", ars, op); + + for (i = 0; i < ars; i++) { + bvrp = GetArrayValue(bap, i); + xtrc(1, "i=%d nv=%d\n", i, nv); + + if (!IsValueNull(bvrp) || (op == OP_CNC && GetJsonNull())) { + if (IsValueNull(bvrp)) { + SetString(bvrp, GetJsonNull(), 0); + bvp = bvrp; + } else if (n < Nod - 1 && bvrp->GetJson()) { + bval.SetValue(g, GetColumnValue(g, jvrp->GetJson(), n + 1)); + bvp = &bval; + } else + jvp = jvrp; + + if (trace(1)) + htrc("jvp=%s null=%d\n", + jvp->GetString(g), jvp->IsNull() ? 1 : 0); + + if (!nv++) { + SetJsonValue(g, vp, jvp); + continue; + } else + SetJsonValue(g, MulVal, jvp); + + if (!MulVal->IsNull()) { + switch (op) { + case OP_CNC: + if (Nodes[n].CncVal) { + val[0] = Nodes[n].CncVal; + err = vp->Compute(g, val, 1, op); + } // endif CncVal + + val[0] = MulVal; + err = vp->Compute(g, val, 1, op); + break; + // case OP_NUM: + case OP_SEP: + val[0] = 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(); + + if (trace(1)) { + char buf(32); + + htrc("vp='%s' err=%d\n", + vp->GetCharString(&buf), err ? 1 : 0); + } // endif trace + + } // endif Zero + + } // endif jvrp + + } // endfor i + + if (op == OP_SEP) { + // Calculate average + MulVal->SetValue(nv); + val[0] = vp; + val[1] = MulVal; + + if (vp->Compute(g, val, 2, OP_DIV)) + vp->Reset(); + + } // endif Op + + return vp; +#else + strcpy(g->Message, "Calculate array NIY"); + return NULL; +#endif +} // end of CalculateArray + +/*********************************************************************************/ +/* CheckPath: Checks whether the path exists in the document. */ +/*********************************************************************************/ +my_bool BJNX::CheckPath(PGLOBAL g) { + PBVAL val = NULL; + PBVAL 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->Type) { + case TYPE_JOB: + if (Nodes[i].Key) + val = GetKeyValue(MPP(row->To_Val), Nodes[i].Key); + + break; + case TYPE_JAR: + if (!Nodes[i].Key) + if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE) + val = GetArrayValue(MVP(row->To_Val), Nodes[i].Rank); + + break; + case TYPE_JVAL: + val = MVP(row->To_Val); + break; + default: + sprintf(g->Message, "Invalid row JSON type %d", row->Type); + } // endswitch Type + +// if (i < Nod - 1) +// if (!(row = (val) ? val->GetJsp() : NULL)) +// val = NULL; + + row = val; + } // endfor i + + return (val != NULL); +} // end of CheckPath + +/***********************************************************************/ +/* GetRow: Set the complete path of the object to be set. */ +/***********************************************************************/ +PBVAL BJNX::GetRow(PGLOBAL g) { + PBVAL val = NULL; + PBVAL arp; + PBVAL nwr, row = Row; + + for (int i = 0; i < Nod - 1 && row; i++) { + if (Nodes[i].Op == OP_XX) + break; + else switch (row->Type) { + case TYPE_JOB: + if (!Nodes[i].Key) + // Expected Array was not there, wrap the value + continue; + + val = GetKeyValue(MPP(row->To_Val), Nodes[i].Key); + break; + case TYPE_JAR: + arp = MVP(row->To_Val); + + if (!Nodes[i].Key) { + if (Nodes[i].Op == OP_EQ) + val = GetArrayValue(arp, Nodes[i].Rank); + else + val = GetArrayValue(arp, Nodes[i].Rx); + + } else { + // Unexpected array, unwrap it as [0] + val = GetArrayValue(arp, 0); + i--; + } // endif Nodes + + break; + case TYPE_JVAL: + val = MVP(row->To_Val); + break; + default: + sprintf(g->Message, "Invalid row JSON type %d", row->Type); + val = NULL; + } // endswitch Type + + if (val) { + row = val; + } 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 = SubAllocVal(g); +// else +// nwr = SubAllocPair(g); + + // Construct new row + nwr = SubAllocVal(g); + + if (row->Type == TYPE_JOB) { + SetKeyValue(g, MPP(row->To_Val), MOF(nwr), Nodes[i - 1].Key); + } else if (row->Type == TYPE_JAR) { + AddArrayValue(g, MVP(row->To_Val), nwr); + } 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 BJNX::WriteValue(PGLOBAL g, PBVAL jvalp) { + PBPR objp = NULL; + PBVAL arp = NULL; + PBVAL jvp = NULL; + PBVAL row = GetRow(g); + + if (!row) + return true; + + switch (row->Type) { + case TYPE_JOB: objp = MPP(row->To_Val); break; + case TYPE_JAR: arp = MVP(row->To_Val); break; + case TYPE_JVAL: jvp = MVP(row->To_Val); 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) + SetArrayValue(g, arp, jvalp, Nodes[Nod - 1].Rank); + else + AddArrayValue(g, arp, jvalp); + + } // endif Key + + } else if (objp) { + if (Nodes[Nod - 1].Key) + SetKeyValue(g, objp, MOF(jvalp), Nodes[Nod - 1].Key); + + } else if (jvp) + SetValueVal(jvp, jvalp); + + return false; +} // end of WriteValue + +/*********************************************************************************/ +/* Locate a value in a JSON tree: */ +/*********************************************************************************/ +PSZ BJNX::Locate(PGLOBAL g, PBVAL jsp, PBVAL jvp, int k) { + PSZ str = NULL; + my_bool b = false, err = true; + + g->Message[0] = 0; + + if (!jsp) { + strcpy(g->Message, "Null json tree"); + return NULL; + } // endif jsp + + try { + // Write to the path string + Jp = new(g) JOUTSTR(g); + Jp->WriteChr('$'); + Bvalp = jvp; + K = k; + + switch (jsp->Type) { + case TYPE_JAR: + err = LocateArray(g, MVP(jsp->To_Val)); + break; + case TYPE_JOB: + err = LocateObject(g, MPP(jsp->To_Val)); + break; + case TYPE_JVAL: + err = LocateValue(g, MVP(jsp->To_Val)); + 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); + str = Jp->Strp; + } // endif's + + } catch (int n) { + if (trace(1)) + htrc("Exception %d: %s\n", n, g->Message); + + PUSH_WARNING(g->Message); + } catch (const char* msg) { + strcpy(g->Message, msg); + } // end catch + + return str; +} // end of Locate + +/*********************************************************************************/ +/* Locate in a JSON Array. */ +/*********************************************************************************/ +my_bool BJNX::LocateArray(PGLOBAL g, PBVAL jarp) { + char s[16]; + int n = GetArraySize(jarp); + size_t m = Jp->N; + + for (int i = 0; i < n && !Found; i++) { + Jp->N = m; + sprintf(s, "[%d]", i + B); + + if (Jp->WriteStr(s)) + return true; + + if (LocateValue(g, GetArrayValue(jarp, i))) + return true; + + } // endfor i + + return false; +} // end of LocateArray + +/*********************************************************************************/ +/* Locate in a JSON Object. */ +/*********************************************************************************/ +my_bool BJNX::LocateObject(PGLOBAL g, PBPR jobp) { + size_t m; + + if (Jp->WriteChr('.')) + return true; + + m = Jp->N; + + for (PBPR pair = jobp; pair && !Found; pair = MPP(pair->Next)) { + Jp->N = m; + + if (Jp->WriteStr(MZP(pair->Key))) + return true; + + if (LocateValue(g, MVP(pair->Vlp))) + return true; + + } // endfor i + + return false; +} // end of LocateObject + +/*********************************************************************************/ +/* Locate a JSON Value. */ +/*********************************************************************************/ +my_bool BJNX::LocateValue(PGLOBAL g, PBVAL jvp) +{ + if (CompareTree(g, Bvalp, jvp)) + Found = (--K == 0); + else if (jvp->Type == TYPE_JAR) + return LocateArray(g, GetArray(jvp)); + else if (jvp->Type == TYPE_JOB) + return LocateObject(g, GetObject(jvp)); + + return false; +} // end of LocateValue + +/*********************************************************************************/ +/* Locate all occurrences of a value in a JSON tree: */ +/*********************************************************************************/ +PSZ BJNX::LocateAll(PGLOBAL g, PBVAL jsp, PBVAL bvp, int mx) +{ + PSZ str = NULL; + my_bool b = false, err = true; + PJPN jnp; + + if (!jsp) { + strcpy(g->Message, "Null json tree"); + return NULL; + } // endif jsp + + try { + jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx); + memset(jnp, 0, sizeof(JPN) * mx); + g->Message[0] = 0; + + // Write to the path string + Jp = new(g)JOUTSTR(g); + Bvalp = bvp; + Imax = mx - 1; + Jpnp = jnp; + Jp->WriteChr('['); + + switch (jsp->Type) { + case TYPE_JAR: + err = LocateArrayAll(g, MVP(jsp->To_Val)); + break; + case TYPE_JOB: + err = LocateObjectAll(g, MPP(jsp->To_Val)); + break; + case TYPE_JVAL: + err = LocateValueAll(g, MVP(jsp->To_Val)); + break; + default: + err = LocateValueAll(g, jsp); + } // endswitch Type + + if (!err) { + if (Jp->N > 1) + Jp->N--; + + Jp->WriteChr(']'); + Jp->WriteChr('\0'); + PlugSubAlloc(g, NULL, Jp->N); + str = Jp->Strp; + } else if (!g->Message[0]) + strcpy(g->Message, "Invalid json tree"); + + } catch (int n) { + xtrc(1, "Exception %d: %s\n", n, g->Message); + PUSH_WARNING(g->Message); + } catch (const char* msg) { + strcpy(g->Message, msg); + } // end catch + + return str; +} // end of LocateAll + +/*********************************************************************************/ +/* Locate in a JSON Array. */ +/*********************************************************************************/ +my_bool BJNX::LocateArrayAll(PGLOBAL g, PBVAL jarp) +{ + int i = 0; + + if (I < Imax) { + Jpnp[++I].Type = TYPE_JAR; + + for (PBVAL vp = jarp; vp; vp = MVP(vp->Next)) { + Jpnp[I].N = i; + + if (LocateValueAll(g, GetArrayValue(jarp, i))) + return true; + + i++; + } // endfor i + + I--; + } // endif I + + return false; +} // end of LocateArrayAll + +/*********************************************************************************/ +/* Locate in a JSON Object. */ +/*********************************************************************************/ +my_bool BJNX::LocateObjectAll(PGLOBAL g, PBPR jobp) +{ + if (I < Imax) { + Jpnp[++I].Type = TYPE_JOB; + + for (PBPR pair = jobp; pair; pair = MPP(pair->Next)) { + Jpnp[I].Key = MZP(pair->Key); + + if (LocateValueAll(g, MVP(pair->Vlp))) + return true; + + } // endfor i + + I--; + } // endif I + + return false; +} // end of LocateObjectAll + +/*********************************************************************************/ +/* Locate a JSON Value. */ +/*********************************************************************************/ +my_bool BJNX::LocateValueAll(PGLOBAL g, PBVAL jvp) { + if (CompareTree(g, Bvalp, jvp)) + return AddPath(); + else if (jvp->Type == TYPE_JAR) + return LocateArrayAll(g, GetArray(jvp)); + else if (jvp->Type == TYPE_JOB) + return LocateObjectAll(g, GetObject(jvp)); + + return false; +} // end of LocateValueAll + +/*********************************************************************************/ +/* Compare two JSON trees. */ +/*********************************************************************************/ +my_bool BJNX::CompareTree(PGLOBAL g, PBVAL jp1, PBVAL jp2) +{ + if (!jp1 || !jp2 || jp1->Type != jp2->Type || GetSize(jp1) != GetSize(jp2)) + return false; + + my_bool found = true; + + if (jp1->Type == TYPE_JAR) { + for (int i = 0; found && i < GetArraySize(jp1); i++) + found = (CompareValues(g, GetArrayValue(jp1, i), GetArrayValue(jp2, i))); + + } else if (jp1->Type == TYPE_JOB) { + PBPR p1 = MPP(jp1->To_Val), p2 = MPP(jp2->To_Val); + + // Keys can be differently ordered + for (; found && p1 && p2; p1 = MPP(p1->Next)) + found = CompareValues(g, MVP(p1->Vlp), GetKeyValue(p2, MZP(p1->Key))); + + } else if (jp1->Type == TYPE_JVAL) { + found = CompareTree(g, MVP(jp1->To_Val), (MVP(jp2->To_Val))); + } else + found = CompareValues(g, jp1, jp2); + + return found; +} // end of CompareTree + +/*********************************************************************************/ +/* Compare two VAL values and return true if they are equal. */ +/*********************************************************************************/ +my_bool BJNX::CompareValues(PGLOBAL g, PBVAL v1, PBVAL v2) +{ + my_bool b = false; + + if (v1 && v2) + switch (v1->Type) { + case TYPE_JAR: + if (v2->Type == TYPE_JAR) + b = CompareTree(g, MVP(v1->To_Val), MVP(v2->To_Val)); + + break; + case TYPE_STRG: + if (v2->Type == TYPE_STRG) { + if (v1->Nd || v2->Nd) // Case insensitive + b = (!stricmp(MZP(v1->To_Val), MZP(v2->To_Val))); + else + b = (!strcmp(MZP(v1->To_Val), MZP(v2->To_Val))); + + } // endif Type + + break; + case TYPE_DTM: + if (v2->Type == TYPE_DTM) + b = (!strcmp(MZP(v1->To_Val), MZP(v2->To_Val))); + + break; + case TYPE_INTG: + if (v2->Type == TYPE_INTG) + b = (v1->N == v2->N); + else if (v2->Type == TYPE_BINT) + b = ((longlong)v1->N == LLN(v2->To_Val)); + + break; + case TYPE_BINT: + if (v2->Type == TYPE_INTG) + b = (LLN(v1->To_Val) == (longlong)v2->N); + else if (v2->Type == TYPE_BINT) + b = (LLN(v1->To_Val) == LLN(v2->To_Val)); + + break; + case TYPE_FLOAT: + if (v2->Type == TYPE_FLOAT) + b = (v1->F == v2->F); + else if (v2->Type == TYPE_DBL) + b = ((double)v1->F == DBL(v2->To_Val)); + + break; + case TYPE_DBL: + if (v2->Type == TYPE_DBL) + b = (DBL(v1->To_Val) == DBL(v2->To_Val)); + else if (v2->Type == TYPE_FLOAT) + b = (DBL(v1->To_Val) == (double)v2->F); + + break; + case TYPE_BOOL: + if (v2->Type == TYPE_BOOL) + b = (v1->B == v2->B); + + break; + case TYPE_NULL: + b = (v2->Type == TYPE_NULL); + break; + default: + break; + } // endswitch Type + + else + b = (!v1 && !v2); + + return b; +} // end of CompareValues + +/*********************************************************************************/ +/* Add the found path to the list. */ +/*********************************************************************************/ +my_bool BJNX::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 + +/*********************************************************************************/ +/* Make a BVAL value from the passed argument. */ +/*********************************************************************************/ +static PBVAL MakeBinValue(PGLOBAL g, UDF_ARGS* args, uint i) { + char* sap = (args->arg_count > i) ? args->args[i] : NULL; + int n, len; + int ci; + longlong bigint; + void* Base = g->Sarea; // Required by MOF + BDOC doc(Base); + PBVAL bp; + PBVAL bvp = doc.SubAllocVal(g); + + 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 == 2) { + if (!(sap = GetJsonFile(g, sap))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif sap + + len = strlen(sap); + } // endif 2 + + if (!(bp = doc.ParseJson(g, sap, strlen(sap)))) + PUSH_WARNING(g->Message); + + bvp = bp; + } else { + // Check whether this string is a valid json string + JsonMemSave(g); + + if (!(bvp = doc.ParseJson(g, sap, strlen(sap)))) { + // Recover suballocated memory + JsonSubSet(g); + ci = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1; + bvp = doc.SubAllocVal(g, MOF(sap), TYPE_STRG, ci); + } else + g->Saved_Size = 0; + + } // endif n + + } // endif len + + break; + case INT_RESULT: + bigint = *(longlong*)sap; + + if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) || + (bigint == 1LL && !strcmp(args->attributes[i], "TRUE"))) + doc.SetBool(bvp, (bool)bigint); + else + doc.SetBigint(g, bvp, bigint); + + break; + case REAL_RESULT: + doc.SetFloat(bvp, *(double*)sap); + break; + case DECIMAL_RESULT: + doc.SetFloat(bvp, atof(MakePSZ(g, args, i))); + break; + case TIME_RESULT: + case ROW_RESULT: + default: + bvp = NULL; + break; + } // endswitch arg_type + + return bvp; +} // end of MakeBinValue + +/*********************************************************************************/ +/* Test BJSON parse and serialize. */ +/*********************************************************************************/ +my_bool json_test_bson_init(UDF_INIT* initid, UDF_ARGS* args, char* message) { + unsigned long reslen, memlen, more = 1000; + + if (args->arg_count == 0) { + strcpy(message, "At least 1 argument required (json)"); + 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); + + return JsonInit(initid, args, message, true, reslen, memlen, more); +} // end of json_test_bson_init + +char* json_test_bson(UDF_INIT* initid, UDF_ARGS* args, char* result, + unsigned long* res_length, char* is_null, char* error) { + char* str = NULL, * sap = NULL, * fn = NULL; + int pretty = 1; + PBVAL bvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + BDOC doc(g); + + 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, !g->Xchk)) { + PUSH_WARNING("CheckMemory error"); + *error = 1; + goto err; + } else if (!(bvp = MakeBinValue(g, args, 0))) { + PUSH_WARNING(g->Message); + goto err; + } // endif bvp + + if (g->Mrr) { // First argument is a constant + g->Xchk = bvp; + JsonMemSave(g); + } // endif Mrr + + } else + bvp = (PBVAL)g->Xchk; + + for (uint i = 1; i < args->arg_count; i++) + if (args->arg_type[i] == STRING_RESULT) + fn = args->args[i]; + else if (args->arg_type[i] == INT_RESULT) + pretty = (int)*(longlong*)args->args[i]; + + // Serialize the parse tree + str = doc.Serialize(g, bvp, fn, pretty); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + } catch (int n) { + xtrc(1, "json_test_bson: error %d: %s\n", n, g->Message); + PUSH_WARNING(g->Message); + *error = 1; + str = NULL; + } catch (const char* msg) { + strcpy(g->Message, msg); + PUSH_WARNING(g->Message); + *error = 1; + str = NULL; + } // end catch + +err: + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of json_test_bson + +void json_test_bson_deinit(UDF_INIT* initid) { + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_test_bson_deinit + +/*********************************************************************************/ +/* Locate a value in a Json tree. */ +/*********************************************************************************/ +my_bool jsonlocate_bson_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; + } // endifs args + + 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_bson_init + +char* jsonlocate_bson(UDF_INIT* initid, UDF_ARGS* args, char* result, + unsigned long* res_length, char* is_null, char* error) { + char* path = NULL; + int k; + PBVAL bvp, bvp2; + PBJNX bnxp; + 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 + bvp = MakeBinValue(g, args, 0); + + if (!bvp) { + PUSH_WARNING("First argument is not a valid JSON item"); + goto err; + } // endif bvp + + if (g->Mrr) { // First argument is a constant + g->Xchk = bvp; + JsonMemSave(g); + } // endif Mrr + + } else + bvp = (PBVAL)g->Xchk; + + // The item to locate + bvp2 = MakeBinValue(g, args, 1); + + k = (args->arg_count > 2) ? (int)*(long long*)args->args[2] : 1; + + bnxp = new(g) BJNX(g, bvp, TYPE_STRING); + path = bnxp->Locate(g, bvp, bvp2, k); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)path; + + } catch (int n) { + xtrc(1, "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_bson + +void jsonlocate_bson_deinit(UDF_INIT* initid) { + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonlocate_bson_deinit + +/*********************************************************************************/ +/* Locate all occurences of a value in a Json tree. */ +/*********************************************************************************/ +my_bool json_locate_all_bson_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; + } // endifs + + 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_bson_init + +char* json_locate_all_bson(UDF_INIT* initid, UDF_ARGS* args, char* result, + unsigned long* res_length, char* is_null, char* error) +{ + char *path = NULL; + int mx = 10; + PBVAL bvp, bvp2; + PBJNX bnxp; + 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 + bvp = MakeBinValue(g, args, 0); + + if (!bvp) { + PUSH_WARNING("First argument is not a valid JSON item"); + goto err; + } // endif bvp + + if (g->Mrr) { // First argument is a constant + g->Xchk = bvp; + JsonMemSave(g); + } // endif Mrr + + } else + bvp = (PBVAL)g->Xchk; + + // The item to locate + bvp2 = MakeBinValue(g, args, 1); + + if (args->arg_count > 2) + mx = (int)*(long long*)args->args[2]; + + bnxp = new(g) BJNX(g, bvp, TYPE_STRING); + path = bnxp->LocateAll(g, bvp, bvp2, mx); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)path; + + } catch (int n) { + xtrc(1, "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_bson + +void json_locate_all_bson_deinit(UDF_INIT* initid) { + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_locate_all_bson_deinit + |