2020-11-25 17:42:01 +01:00
|
|
|
/****************** bsonudf C++ Program Source Code File (.CPP) ******************/
|
|
|
|
/* PROGRAM NAME: bsonudf Version 1.0 */
|
|
|
|
/* (C) Copyright to the author Olivier BERTRAND 2020 */
|
2020-11-27 10:25:47 +01:00
|
|
|
/* This program are the BSON User Defined Functions. */
|
2020-11-25 17:42:01 +01:00
|
|
|
/*********************************************************************************/
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Include relevant sections of the MariaDB header file. */
|
|
|
|
/*********************************************************************************/
|
|
|
|
#include <my_global.h>
|
|
|
|
#include <mysqld.h>
|
|
|
|
#include <mysql.h>
|
|
|
|
#include <sql_error.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "bsonudf.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
|
2020-11-27 10:25:47 +01:00
|
|
|
#define M 6
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
/* --------------------------------- JSON UDF ---------------------------------- */
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Program for saving the status of the memory pools. */
|
|
|
|
/*********************************************************************************/
|
|
|
|
inline void JsonMemSave(PGLOBAL g) {
|
|
|
|
g->Saved_Size = ((PPOOLHEADER)g->Sarea)->To_Free;
|
|
|
|
} /* end of JsonMemSave */
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Program for freeing the memory pools. */
|
|
|
|
/*********************************************************************************/
|
|
|
|
inline void JsonFreeMem(PGLOBAL g) {
|
|
|
|
g->Activityp = NULL;
|
2020-12-01 19:30:56 +01:00
|
|
|
g = PlugExit(g);
|
2020-11-25 17:42:01 +01:00
|
|
|
} /* end of JsonFreeMem */
|
|
|
|
|
|
|
|
/* --------------------------- New Testing BJSON Stuff --------------------------*/
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* SubAlloc a new BJNX class with protection against memory exhaustion. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
static PBJNX BjnxNew(PGLOBAL g, PBVAL vlp, int type, int len)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-12-01 19:30:56 +01:00
|
|
|
BJNX::BJNX(PGLOBAL g, PBVAL row, int type, int len, int prec, my_bool wr) : BDOC(g)
|
2020-11-27 10:25:47 +01:00
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::SetJpath(PGLOBAL g, char* path, my_bool jb)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
// 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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::SetArrayOptions(PGLOBAL g, char* p, int i, PSZ nm)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::ParseJpath(PGLOBAL g)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PVAL BJNX::MakeJson(PGLOBAL g, PBVAL bvp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
void BJNX::SetJsonValue(PGLOBAL g, PVAL vp, PBVAL vlp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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:
|
2020-12-01 19:30:56 +01:00
|
|
|
vp->SetValue_psz(GetString(vlp));
|
2020-11-25 17:42:01 +01:00
|
|
|
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
|
2020-12-01 19:30:56 +01:00
|
|
|
vp->SetValue_psz(GetString(vlp));
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
case TYPE_BOOL:
|
|
|
|
if (vp->IsTypeNum())
|
|
|
|
vp->SetValue(GetInteger(vlp) ? 1 : 0);
|
|
|
|
else
|
2020-12-01 19:30:56 +01:00
|
|
|
vp->SetValue_psz(GetString(vlp));
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
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: */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PBVAL BJNX::GetJson(PGLOBAL g)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
return GetRowValue(g, Row, 0);
|
|
|
|
} // end of GetJson
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* ReadValue: */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
void BJNX::ReadValue(PGLOBAL g)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
Value->SetValue_pval(GetColumnValue(g, Row, 0));
|
|
|
|
} // end of ReadValue
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* GetColumnValue: */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PVAL BJNX::GetColumnValue(PGLOBAL g, PBVAL row, int i)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
PBVAL vlp = GetRowValue(g, row, i);
|
|
|
|
|
|
|
|
SetJsonValue(g, Value, vlp);
|
|
|
|
return Value;
|
|
|
|
} // end of GetColumnValue
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* GetRowValue: */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PBVAL BJNX::GetRowValue(PGLOBAL g, PBVAL row, int i, my_bool b)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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);
|
2020-12-01 19:30:56 +01:00
|
|
|
vlp = SubAllocVal(Value);
|
2020-11-25 17:42:01 +01:00
|
|
|
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
|
2020-12-01 19:30:56 +01:00
|
|
|
return SubAllocVal(CalculateArray(g, bap, i));
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
} 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: */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PVAL BJNX::ExpandArray(PGLOBAL g, PBVAL arp, int n)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
strcpy(g->Message, "Expand cannot be done by this function");
|
|
|
|
return NULL;
|
|
|
|
} // end of ExpandArray
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* CalculateArray: NIY */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PVAL BJNX::CalculateArray(PGLOBAL g, PBVAL bap, int n)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
#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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::CheckPath(PGLOBAL g)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/***********************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PBVAL BJNX::GetRow(PGLOBAL g)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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
|
2020-12-01 19:30:56 +01:00
|
|
|
nwr = NewVal();
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
if (row->Type == TYPE_JOB) {
|
2020-12-01 19:30:56 +01:00
|
|
|
SetKeyValue(MPP(row->To_Val), MOF(nwr), Nodes[i - 1].Key);
|
2020-11-25 17:42:01 +01:00
|
|
|
} else if (row->Type == TYPE_JAR) {
|
2020-12-01 19:30:56 +01:00
|
|
|
AddArrayValue(MVP(row->To_Val), nwr);
|
2020-11-25 17:42:01 +01:00
|
|
|
} 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: */
|
|
|
|
/***********************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::WriteValue(PGLOBAL g, PBVAL jvalp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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)
|
2020-12-01 19:30:56 +01:00
|
|
|
SetArrayValue(arp, jvalp, Nodes[Nod - 1].Rank);
|
2020-11-25 17:42:01 +01:00
|
|
|
else
|
2020-12-01 19:30:56 +01:00
|
|
|
AddArrayValue(arp, jvalp);
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
} // endif Key
|
|
|
|
|
|
|
|
} else if (objp) {
|
|
|
|
if (Nodes[Nod - 1].Key)
|
2020-12-01 19:30:56 +01:00
|
|
|
SetKeyValue(objp, MOF(jvalp), Nodes[Nod - 1].Key);
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
} else if (jvp)
|
|
|
|
SetValueVal(jvp, jvalp);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
} // end of WriteValue
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Locate a value in a JSON tree: */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PSZ BJNX::Locate(PGLOBAL g, PBVAL jsp, PBVAL jvp, int k)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::LocateArray(PGLOBAL g, PBVAL jarp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::LocateObject(PGLOBAL g, PBPR jobp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::LocateValue(PGLOBAL g, PBVAL jvp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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: */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
PSZ BJNX::LocateAll(PGLOBAL g, PBVAL jsp, PBVAL bvp, int mx)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::LocateArrayAll(PGLOBAL g, PBVAL jarp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::LocateObjectAll(PGLOBAL g, PBPR jobp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::LocateValueAll(PGLOBAL g, PBVAL jvp)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::CompareTree(PGLOBAL g, PBVAL jp1, PBVAL jp2)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::CompareValues(PGLOBAL g, PBVAL v1, PBVAL v2)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool BJNX::AddPath(void)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
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
|
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
/* -----------------------------Utility functions ------------------------------ */
|
|
|
|
|
2020-11-25 17:42:01 +01:00
|
|
|
/*********************************************************************************/
|
|
|
|
/* Make a BVAL value from the passed argument. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
static PBVAL MakeBinValue(PGLOBAL g, UDF_ARGS* args, uint i)
|
|
|
|
{
|
2020-11-25 17:42:01 +01:00
|
|
|
char* sap = (args->arg_count > i) ? args->args[i] : NULL;
|
|
|
|
int n, len;
|
|
|
|
int ci;
|
|
|
|
longlong bigint;
|
2020-12-01 19:30:56 +01:00
|
|
|
BDOC doc(g);
|
|
|
|
PBVAL bp, bvp = doc.NewVal();
|
2020-11-27 10:25:47 +01:00
|
|
|
|
|
|
|
if (sap) {
|
|
|
|
if (args->arg_type[i] == 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)))) {
|
2020-11-25 17:42:01 +01:00
|
|
|
PUSH_WARNING(g->Message);
|
|
|
|
return NULL;
|
2020-11-27 10:25:47 +01:00
|
|
|
} else
|
|
|
|
bvp = bp;
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
} else {
|
|
|
|
// Check whether this string is a valid json string
|
|
|
|
JsonMemSave(g);
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
if (!(bp = doc.ParseJson(g, sap, strlen(sap)))) {
|
|
|
|
// Recover suballocated memory
|
|
|
|
JsonSubSet(g);
|
|
|
|
ci = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1;
|
|
|
|
doc.SetString(bvp, sap, ci);
|
|
|
|
} else
|
|
|
|
bvp = bp;
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
g->Saved_Size = 0;
|
2020-11-27 10:25:47 +01:00
|
|
|
} // endif n
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
} // endif len
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
} else switch (args->arg_type[i]) {
|
|
|
|
case INT_RESULT:
|
|
|
|
bigint = *(longlong*)sap;
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) ||
|
|
|
|
(bigint == 1LL && !strcmp(args->attributes[i], "TRUE")))
|
|
|
|
doc.SetBool(bvp, (bool)bigint);
|
|
|
|
else
|
2020-12-01 19:30:56 +01:00
|
|
|
doc.SetBigint(bvp, bigint);
|
2020-11-27 10:25:47 +01:00
|
|
|
|
|
|
|
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->Type = TYPE_UNKNOWN;
|
|
|
|
break;
|
|
|
|
} // endswitch arg_type
|
|
|
|
|
|
|
|
} // endif sap
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
return bvp;
|
|
|
|
} // end of MakeBinValue
|
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
/* ------------------------- Now the new Bin UDF's ----------------------------- */
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Make a Json value containing the parameter. */
|
|
|
|
/*********************************************************************************/
|
|
|
|
my_bool bsonvalue_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 bsonvalue_init
|
|
|
|
|
|
|
|
char* bsonvalue(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)) {
|
2020-12-01 19:30:56 +01:00
|
|
|
BDOC doc(g);
|
2020-11-27 10:25:47 +01:00
|
|
|
PBVAL bvp = MakeBinValue(g, args, 0);
|
|
|
|
|
|
|
|
if (!(str = doc.Serialize(g, bvp, 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 bsonValue
|
|
|
|
|
|
|
|
void bsonvalue_deinit(UDF_INIT* initid) {
|
|
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
|
|
} // end of bsonvalue_deinit
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Make a Bson array containing all the parameters. */
|
|
|
|
/*********************************************************************************/
|
|
|
|
my_bool bson_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 bson_make_array_init
|
|
|
|
|
|
|
|
char* bson_make_array(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)) {
|
2020-12-01 19:30:56 +01:00
|
|
|
BDOC doc(g);
|
2020-11-27 10:25:47 +01:00
|
|
|
PBVAL bvp = NULL, arp = NULL;
|
|
|
|
|
|
|
|
for (uint i = 0; i < args->arg_count; i++)
|
2020-12-01 19:30:56 +01:00
|
|
|
bvp = doc.AddArrayValue(bvp, MakeBinValue(g, args, i));
|
2020-11-27 10:25:47 +01:00
|
|
|
|
2020-12-01 19:30:56 +01:00
|
|
|
arp = doc.SubAllocVal(bvp, TYPE_JAR);
|
2020-11-27 10:25:47 +01:00
|
|
|
|
|
|
|
if (!(str = doc.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 bson_make_array
|
|
|
|
|
|
|
|
void bson_make_array_deinit(UDF_INIT* initid) {
|
|
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
|
|
} // end of bson_make_array_deinit
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Add one or several values to a Bson array. */
|
|
|
|
/*********************************************************************************/
|
|
|
|
my_bool bson_array_add_values_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
|
|
|
|
unsigned long reslen, memlen;
|
|
|
|
|
|
|
|
if (args->arg_count < 2) {
|
|
|
|
strcpy(message, "This function must have at least 2 arguments");
|
|
|
|
return true;
|
|
|
|
//} else if (!IsJson(args, 0, true)) {
|
|
|
|
// strcpy(message, "First argument must be a valid json string or item");
|
|
|
|
// return true;
|
|
|
|
} else
|
|
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
|
|
|
|
if (!JsonInit(initid, args, message, true, reslen, memlen)) {
|
|
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
|
|
|
|
// This is a constant function
|
|
|
|
g->N = (initid->const_item) ? 1 : 0;
|
|
|
|
|
|
|
|
// This is to avoid double execution when using prepared statements
|
|
|
|
if (IsJson(args, 0) > 1)
|
|
|
|
initid->const_item = 0;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
} else
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} // end of bson_array_add_values_init
|
|
|
|
|
|
|
|
char* bson_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)) {
|
|
|
|
uint n = 1;
|
|
|
|
bool b = false;
|
2020-12-01 19:30:56 +01:00
|
|
|
BDOC doc(g);
|
2020-11-27 10:25:47 +01:00
|
|
|
PBVAL bvp = NULL, arp = MakeBinValue(g, args, 0);
|
|
|
|
|
|
|
|
if (arp->Type == TYPE_JAR) {
|
|
|
|
bvp = doc.GetArray(arp);
|
|
|
|
b = !bvp;
|
|
|
|
} else
|
|
|
|
n = 0;
|
|
|
|
|
|
|
|
for (uint i = n; i < args->arg_count; i++)
|
2020-12-01 19:30:56 +01:00
|
|
|
bvp = doc.AddArrayValue(bvp, MakeBinValue(g, args, i));
|
2020-11-27 10:25:47 +01:00
|
|
|
|
|
|
|
if (!n)
|
2020-12-01 19:30:56 +01:00
|
|
|
arp = doc.SubAllocVal(bvp, TYPE_JAR);
|
2020-11-27 10:25:47 +01:00
|
|
|
else if (b)
|
|
|
|
doc.SetValueArr(arp, bvp);
|
|
|
|
|
|
|
|
// str = MakeResult(g, args, top, args->arg_count);
|
|
|
|
str = doc.Serialize(g, arp, NULL, 0);
|
|
|
|
} // 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 bson_array_add_values
|
|
|
|
|
|
|
|
void bson_array_add_values_deinit(UDF_INIT* initid) {
|
|
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
|
|
} // end of bson_array_add_values_deinit
|
|
|
|
|
2020-11-25 17:42:01 +01:00
|
|
|
/*********************************************************************************/
|
|
|
|
/* Test BJSON parse and serialize. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool bson_test_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
|
2020-11-25 17:42:01 +01:00
|
|
|
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);
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bson_test_init
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
char* bson_test(UDF_INIT* initid, UDF_ARGS* args, char* result,
|
2020-11-25 17:42:01 +01:00
|
|
|
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;
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bson_test
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
void bson_test_deinit(UDF_INIT* initid) {
|
2020-11-25 17:42:01 +01:00
|
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bson_test_deinit
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Locate a value in a Json tree. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool bsonlocate_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
|
2020-11-25 17:42:01 +01:00
|
|
|
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);
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bsonlocate_init
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
char* bsonlocate(UDF_INIT* initid, UDF_ARGS* args, char* result,
|
2020-11-25 17:42:01 +01:00
|
|
|
unsigned long* res_length, char* is_null, char* error) {
|
2020-11-27 10:25:47 +01:00
|
|
|
char *path = NULL;
|
2020-11-25 17:42:01 +01:00
|
|
|
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
|
2020-11-27 10:25:47 +01:00
|
|
|
if (!(bvp2 = MakeBinValue(g, args, 1))) {
|
|
|
|
PUSH_WARNING("Invalid second argument");
|
|
|
|
goto err;
|
|
|
|
} // endif bvp
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
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;
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bsonlocate
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
void bsonlocate_deinit(UDF_INIT* initid) {
|
2020-11-25 17:42:01 +01:00
|
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bsonlocate_deinit
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Locate all occurences of a value in a Json tree. */
|
|
|
|
/*********************************************************************************/
|
2020-11-27 10:25:47 +01:00
|
|
|
my_bool bson_locate_all_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
|
2020-11-25 17:42:01 +01:00
|
|
|
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);
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bson_locate_all_init
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
char* bson_locate_all(UDF_INIT* initid, UDF_ARGS* args, char* result,
|
2020-11-25 17:42:01 +01:00
|
|
|
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
|
2020-11-27 10:25:47 +01:00
|
|
|
if (!(bvp2 = MakeBinValue(g, args, 1))) {
|
|
|
|
PUSH_WARNING("Invalid second argument");
|
|
|
|
goto err;
|
|
|
|
} // endif bvp
|
2020-11-25 17:42:01 +01:00
|
|
|
|
|
|
|
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;
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bson_locate_all
|
2020-11-25 17:42:01 +01:00
|
|
|
|
2020-11-27 10:25:47 +01:00
|
|
|
void bson_locate_all_deinit(UDF_INIT* initid) {
|
2020-11-25 17:42:01 +01:00
|
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
2020-11-27 10:25:47 +01:00
|
|
|
} // end of bson_locate_all_deinit
|
2020-11-25 17:42:01 +01:00
|
|
|
|