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