mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
dd30ba4c1b
In addition to files and Mongo collections, JSON as well as XML and CSV data can be retrieved from the net as answers from REST queries. Because it uses and external package (cpprestsdk) this is currently available only to MariaDB servers compiled from source. -- Add the REST support when Microsoft Casablanca package (cpprestsdk) is installed. -- Add compile flags needed on Windows /MD or /MDd (debug) -- Also include some changes specific to MariaDB 10.3. modified: storage/connect/CMakeLists.txt -- Add conditional REST support -- Added string options HTTP and URI. -- Added added internal table type TAB_REST. modified: storage/connect/ha_connect.cc modified: storage/connect/mycat.cc modified: storage/connect/mycat.h modified: storage/connect/plgdbsem.h -- Add conditional code based on the preprocessor definition MARIADB -- This to be able to use the same code in CONNECT and EOM modules modified: storage/connect/osutil.h modified: storage/connect/tabrest.cpp -- Add files for the REST OEM module added: storage/connect/mini-global.h added: storage/connect/rest.def -- Fix MDEV-19648 Variable connect_conv_size doesn't change -- Change Variable wrong block parameter from 8169 to 1. -- Also change connect_conv_size default value to 1024. modified: storage/connect/ha_connect.cc -- Fix compilation error when ZIP is not supported modified: storage/connect/ha_connect.cc modified: storage/connect/tabfmt.cpp -- Replace PlugSetPath by some concat (crashed on Fedora) + typo modified: storage/connect/reldef.cpp -- Avoid possible buffer overflow -- In particular by the function ShowValue. modified: storage/connect/tabdos.cpp modified: storage/connect/tabfmt.cpp modified: storage/connect/value.cpp modified: storage/connect/value.h -- Add some cast to avoid some compiler warnings modified: storage/connect/filamdbf.cpp -- Fix some C++ error modified: storage/connect/javaconn.cpp modified: storage/connect/jmgoconn.cpp modified: storage/connect/plugutil.cpp -- Add some tracing + typo modified: storage/connect/mycat.cc modified: storage/connect/tabjson.cpp -- Add the xtrc tracing function modified: storage/connect/global.h modified: storage/connect/plugutil.cpp -- Modify tracing to use xtrc and some typo modified: storage/connect/array.cpp modified: storage/connect/block.h -- Miscellaneous Typo and warning suppressing changes modified: storage/connect/connect.cpp modified: storage/connect/connect.h modified: storage/connect/filamvct.cpp modified: storage/connect/inihandl.cpp modified: storage/connect/jsonudf.cpp modified: storage/connect/libdoc.cpp modified: storage/connect/tabjson.cpp modified: storage/connect/tabtbl.cpp modified: storage/connect/tabxml.cpp modified: storage/connect/user_connect.cc modified: storage/connect/user_connect.h -- Update failing test results and disbling modified: storage/connect/mysql-test/connect/disabled.def modified: storage/connect/mysql-test/connect/r/dir.result modified: storage/connect/mysql-test/connect/r/grant.result modified: storage/connect/mysql-test/connect/r/jdbc.result modified: storage/connect/mysql-test/connect/r/jdbc_postgresql.result modified: storage/connect/mysql-test/connect/r/xml.result modified: storage/connect/mysql-test/connect/r/xml2.result modified: storage/connect/mysql-test/connect/r/xml2_mult.result modified: storage/connect/mysql-test/connect/r/xml_mult.result -- Add an option modified: storage/connect/mysql-test/connect/t/grant.test
5900 lines
155 KiB
C++
5900 lines
155 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 <mysql.h>
|
|
#include <sql_error.h>
|
|
#include <stdio.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, 0, M)
|
|
#else
|
|
#define PUSH_WARNING(M) htrc(M)
|
|
#endif
|
|
#define M 7
|
|
|
|
bool IsNum(PSZ s);
|
|
char *NextChr(PSZ s, char sep);
|
|
char *GetJsonNull(void);
|
|
uint GetJsonGrpSize(void);
|
|
static int IsJson(UDF_ARGS *args, uint i, bool b = false);
|
|
static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i);
|
|
static char *handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error);
|
|
static char *bin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error);
|
|
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);
|
|
|
|
static 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
|
|
sprintf(g->Message, "Invalid array specification %s", p);
|
|
return true;
|
|
} // endif p
|
|
|
|
} else
|
|
b = true;
|
|
|
|
// To check whether a numeric Rank was specified
|
|
dg = IsNum(p);
|
|
|
|
if (!n) {
|
|
// Default specifications
|
|
if (jnp->Op != OP_EXP) {
|
|
if (Wr) {
|
|
// Force append
|
|
jnp->Rank = INT_MAX32;
|
|
jnp->Op = OP_LE;
|
|
} else if (Jb) {
|
|
// Return a Json item
|
|
jnp->Op = OP_XX;
|
|
} else if (b) {
|
|
// Return 1st value (B is the index base)
|
|
jnp->Rank = B;
|
|
jnp->Op = OP_LE;
|
|
} else if (!Value->IsTypeNum()) {
|
|
jnp->CncVal = AllocateValue(g, PlugDup(g, ", "), TYPE_STRING);
|
|
jnp->Op = OP_CNC;
|
|
} else
|
|
jnp->Op = OP_ADD;
|
|
|
|
} // endif OP
|
|
|
|
} else if (dg) {
|
|
// Return nth value
|
|
jnp->Rank = atoi(p) - B;
|
|
jnp->Op = OP_EQ;
|
|
} else if (Wr) {
|
|
sprintf(g->Message, "Invalid specification %s in a write path", p);
|
|
return true;
|
|
} else if (n == 1) {
|
|
// Set the Op value;
|
|
switch (*p) {
|
|
case '+': jnp->Op = OP_ADD; break;
|
|
case 'x': jnp->Op = OP_MULT; break;
|
|
case '>': jnp->Op = OP_MAX; break;
|
|
case '<': jnp->Op = OP_MIN; break;
|
|
case '!': jnp->Op = OP_SEP; break; // Average
|
|
case '#': jnp->Op = OP_NUM; break;
|
|
case '*': // Expand this array
|
|
strcpy(g->Message, "Expand not supported by this function");
|
|
return true;
|
|
default:
|
|
sprintf(g->Message, "Invalid function specification %c", *p);
|
|
return true;
|
|
} // endswitch *p
|
|
|
|
} else if (*p == '"' && p[n - 1] == '"') {
|
|
// This is a concat specification
|
|
jnp->Op = OP_CNC;
|
|
|
|
if (n > 2) {
|
|
// Set concat intermediate string
|
|
p[n - 1] = 0;
|
|
|
|
if (trace(1))
|
|
htrc("Concat string=%s\n", p + 1);
|
|
|
|
jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
|
|
} // endif n
|
|
|
|
} else {
|
|
strcpy(g->Message, "Wrong array specification");
|
|
return true;
|
|
} // endif's
|
|
|
|
// For calculated arrays, a local Value must be used
|
|
switch (jnp->Op) {
|
|
case OP_NUM:
|
|
jnp->Valp = AllocateValue(g, TYPE_INT);
|
|
break;
|
|
case OP_ADD:
|
|
case OP_MULT:
|
|
case OP_SEP:
|
|
if (!IsTypeChar(Buf_Type))
|
|
jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
|
|
else
|
|
jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);
|
|
|
|
break;
|
|
case OP_MIN:
|
|
case OP_MAX:
|
|
jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
|
|
break;
|
|
case OP_CNC:
|
|
if (IsTypeChar(Buf_Type))
|
|
jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
|
|
else
|
|
jnp->Valp = AllocateValue(g, TYPE_STRING, 512);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
} // endswitch Op
|
|
|
|
if (jnp->Valp)
|
|
MulVal = AllocateValue(g, jnp->Valp);
|
|
|
|
return false;
|
|
} // end of SetArrayOptions
|
|
|
|
/*********************************************************************************/
|
|
/* Parse the eventual passed Jpath information. */
|
|
/* This information can be specified in the Fieldfmt column option when */
|
|
/* creating the table. It permits to indicate the position of the node */
|
|
/* corresponding to that column. */
|
|
/*********************************************************************************/
|
|
my_bool JSNX::ParseJpath(PGLOBAL g)
|
|
{
|
|
char *p, *p1 = NULL, *p2 = NULL, *pbuf = NULL;
|
|
int i;
|
|
my_bool a, mul = false;
|
|
|
|
if (Parsed)
|
|
return false; // Already done
|
|
else if (!Jpath)
|
|
// Jpath = Name;
|
|
return true;
|
|
|
|
if (trace(1))
|
|
htrc("ParseJpath %s\n", SVP(Jpath));
|
|
|
|
if (!(pbuf = PlgDBDup(g, Jpath)))
|
|
return true;
|
|
|
|
if (*pbuf == '$') pbuf++;
|
|
if (*pbuf == '.') pbuf++;
|
|
if (*pbuf == '[') p1 = pbuf++;
|
|
|
|
// Estimate the required number of nodes
|
|
for (i = 0, p = pbuf; (p = NextChr(p, '.')); i++, p++)
|
|
Nod++; // One path node found
|
|
|
|
if (!(Nodes = (PJNODE)PlgDBSubAlloc(g, NULL, (++Nod) * sizeof(JNODE))))
|
|
return true;
|
|
|
|
memset(Nodes, 0, (Nod)* sizeof(JNODE));
|
|
|
|
// Analyze the Jpath for this column
|
|
for (i = 0, p = pbuf; p && i < Nod; i++, p = (p2 ? p2 : NULL)) {
|
|
a = (p1 != NULL);
|
|
p1 = strchr(p, '[');
|
|
p2 = strchr(p, '.');
|
|
|
|
if (!p2)
|
|
p2 = p1;
|
|
else if (p1) {
|
|
if (p1 < p2)
|
|
p2 = p1;
|
|
else if (p1 == p2 + 1)
|
|
*p2++ = 0; // Old syntax .[
|
|
else
|
|
p1 = NULL;
|
|
|
|
} // endif p1
|
|
|
|
if (p2)
|
|
*p2++ = 0;
|
|
|
|
// Jpath must be explicit
|
|
if (a || *p == 0 || *p == '[' || IsNum(p)) {
|
|
// Analyse intermediate array processing
|
|
if (SetArrayOptions(g, p, i, Nodes[i-1].Key))
|
|
return true;
|
|
|
|
} else if (*p == '*') {
|
|
if (Wr) {
|
|
sprintf(g->Message, "Invalid specification %c in a write path", *p);
|
|
return true;
|
|
} else // Return JSON
|
|
Nodes[i].Op = OP_XX;
|
|
|
|
} else {
|
|
Nodes[i].Key = p;
|
|
Nodes[i].Op = OP_EXIST;
|
|
} // endif's
|
|
|
|
} // endfor i, p
|
|
|
|
Nod = i;
|
|
MulVal = AllocateValue(g, Value);
|
|
|
|
if (trace(1))
|
|
for (i = 0; i < Nod; i++)
|
|
htrc("Node(%d) Key=%s Op=%d Rank=%d\n",
|
|
i, SVP(Nodes[i].Key), Nodes[i].Op, Nodes[i].Rank);
|
|
|
|
Parsed = true;
|
|
return false;
|
|
} // end of ParseJpath
|
|
|
|
/*********************************************************************************/
|
|
/* MakeJson: Serialize the json item and set value to it. */
|
|
/*********************************************************************************/
|
|
PVAL JSNX::MakeJson(PGLOBAL g, PJSON jsp)
|
|
{
|
|
if (Value->IsTypeNum()) {
|
|
strcpy(g->Message, "Cannot make Json for a numeric value");
|
|
Value->Reset();
|
|
} else if (jsp->GetType() != TYPE_JAR && jsp->GetType() != TYPE_JOB) {
|
|
strcpy(g->Message, "Target is not an array or object");
|
|
Value->Reset();
|
|
} else
|
|
Value->SetValue_psz(Serialize(g, jsp, NULL, 0));
|
|
|
|
return Value;
|
|
} // end of MakeJson
|
|
|
|
/*********************************************************************************/
|
|
/* SetValue: Set a value from a JVALUE contains. */
|
|
/*********************************************************************************/
|
|
void JSNX::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
|
|
{
|
|
if (val) {
|
|
vp->SetNull(false);
|
|
|
|
if (Jb) {
|
|
vp->SetValue_psz(Serialize(g, val->GetJsp(), NULL, 0));
|
|
} else switch (val->GetValType()) {
|
|
case TYPE_STRG:
|
|
case TYPE_INTG:
|
|
case TYPE_BINT:
|
|
case TYPE_DBL:
|
|
vp->SetValue_pval(val->GetValue());
|
|
break;
|
|
case TYPE_BOOL:
|
|
if (vp->IsTypeNum())
|
|
vp->SetValue(val->GetInteger() ? 1 : 0);
|
|
else
|
|
vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));
|
|
|
|
break;
|
|
case TYPE_JAR:
|
|
SetJsonValue(g, vp, val->GetArray()->GetValue(0), n);
|
|
break;
|
|
case TYPE_JOB:
|
|
// if (!vp->IsTypeNum() || !Strict) {
|
|
vp->SetValue_psz(val->GetObject()->GetText(g, NULL));
|
|
break;
|
|
// } // endif Type
|
|
|
|
case TYPE_NULL:
|
|
vp->SetNull(true);
|
|
default:
|
|
vp->Reset();
|
|
} // endswitch Type
|
|
|
|
} else {
|
|
vp->SetNull(true);
|
|
vp->Reset();
|
|
} // endif val
|
|
|
|
} // end of SetJsonValue
|
|
|
|
/*********************************************************************************/
|
|
/* GetJson: */
|
|
/*********************************************************************************/
|
|
PJVAL JSNX::GetJson(PGLOBAL g)
|
|
{
|
|
return GetRowValue(g, Row, 0);
|
|
} // end of GetJson
|
|
|
|
/*********************************************************************************/
|
|
/* ReadValue: */
|
|
/*********************************************************************************/
|
|
void JSNX::ReadValue(PGLOBAL g)
|
|
{
|
|
Value->SetValue_pval(GetColumnValue(g, Row, 0));
|
|
} // end of ReadValue
|
|
|
|
/*********************************************************************************/
|
|
/* GetColumnValue: */
|
|
/*********************************************************************************/
|
|
PVAL JSNX::GetColumnValue(PGLOBAL g, PJSON row, int i)
|
|
{
|
|
int n = Nod - 1;
|
|
PJVAL val = NULL;
|
|
|
|
val = GetRowValue(g, row, i);
|
|
SetJsonValue(g, Value, val, n);
|
|
return Value;
|
|
} // end of GetColumnValue
|
|
|
|
/*********************************************************************************/
|
|
/* GetRowValue: */
|
|
/*********************************************************************************/
|
|
PJVAL JSNX::GetRowValue(PGLOBAL g, PJSON row, int i, my_bool b)
|
|
{
|
|
my_bool expd = false;
|
|
PJAR arp;
|
|
PJVAL val = NULL;
|
|
|
|
for (; i < Nod && row; i++) {
|
|
if (Nodes[i].Op == OP_NUM) {
|
|
Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1);
|
|
val = new(g) JVALUE(g, Value);
|
|
return val;
|
|
} else if (Nodes[i].Op == OP_XX) {
|
|
Jb = b;
|
|
return new(g)JVALUE(row);
|
|
} else switch (row->GetType()) {
|
|
case TYPE_JOB:
|
|
if (!Nodes[i].Key) {
|
|
// Expected Array was not there
|
|
if (Nodes[i].Op == OP_LE) {
|
|
if (i < Nod-1)
|
|
continue;
|
|
else
|
|
val = new(g)JVALUE(row);
|
|
|
|
} else {
|
|
strcpy(g->Message, "Unexpected object");
|
|
val = NULL;
|
|
} //endif Op
|
|
|
|
} else
|
|
val = ((PJOB)row)->GetValue(Nodes[i].Key);
|
|
|
|
break;
|
|
case TYPE_JAR:
|
|
arp = (PJAR)row;
|
|
|
|
if (!Nodes[i].Key) {
|
|
if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
|
|
val = arp->GetValue(Nodes[i].Rank);
|
|
else if (Nodes[i].Op == OP_EXP)
|
|
return (PJVAL)ExpandArray(g, arp, i);
|
|
else
|
|
return new(g) JVALUE(g, CalculateArray(g, arp, i));
|
|
|
|
} else {
|
|
// Unexpected array, unwrap it as [0]
|
|
val = arp->GetValue(0);
|
|
i--;
|
|
} // endif's
|
|
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
|
|
val = NULL;
|
|
} // endswitch Type
|
|
|
|
if (i < Nod-1)
|
|
if (!(row = (val) ? val->GetJsp() : NULL))
|
|
val = NULL;
|
|
// row = (val) ? val->GetJson() : NULL;
|
|
|
|
} // endfor i
|
|
|
|
// SetJsonValue(g, Value, val, n);
|
|
return val;
|
|
} // end of GetRowValue
|
|
|
|
/*********************************************************************************/
|
|
/* ExpandArray: */
|
|
/*********************************************************************************/
|
|
PVAL JSNX::ExpandArray(PGLOBAL g, PJAR arp, int n)
|
|
{
|
|
strcpy(g->Message, "Expand cannot be done by this function");
|
|
return NULL;
|
|
} // end of ExpandArray
|
|
|
|
/*********************************************************************************/
|
|
/* CalculateArray: */
|
|
/*********************************************************************************/
|
|
PVAL JSNX::CalculateArray(PGLOBAL g, PJAR arp, int n)
|
|
{
|
|
int i, ars = arp->size(), nv = 0;
|
|
bool err;
|
|
OPVAL op = Nodes[n].Op;
|
|
PVAL val[2], vp = Nodes[n].Valp;
|
|
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->GetValue(i);
|
|
|
|
if (trace(1))
|
|
htrc("i=%d nv=%d\n", i, nv);
|
|
|
|
if (!jvrp->IsNull() || (op == OP_CNC && GetJsonNull())) {
|
|
if (jvrp->IsNull()) {
|
|
jvrp->Value = AllocateValue(g, GetJsonNull(), TYPE_STRING);
|
|
jvp = jvrp;
|
|
} else if (n < Nod - 1 && jvrp->GetJson()) {
|
|
jval.SetValue(GetColumnValue(g, jvrp->GetJson(), n + 1));
|
|
jvp = &jval;
|
|
} else
|
|
jvp = jvrp;
|
|
|
|
if (trace(1))
|
|
htrc("jvp=%s null=%d\n",
|
|
jvp->GetString(g), jvp->IsNull() ? 1 : 0);
|
|
|
|
if (!nv++) {
|
|
SetJsonValue(g, vp, jvp, n);
|
|
continue;
|
|
} else
|
|
SetJsonValue(g, MulVal, jvp, n);
|
|
|
|
if (!MulVal->IsNull()) {
|
|
switch (op) {
|
|
case OP_CNC:
|
|
if (Nodes[n].CncVal) {
|
|
val[0] = Nodes[n].CncVal;
|
|
err = vp->Compute(g, val, 1, op);
|
|
} // endif CncVal
|
|
|
|
val[0] = MulVal;
|
|
err = vp->Compute(g, val, 1, op);
|
|
break;
|
|
// case OP_NUM:
|
|
case OP_SEP:
|
|
val[0] = Nodes[n].Valp;
|
|
val[1] = MulVal;
|
|
err = vp->Compute(g, val, 2, OP_ADD);
|
|
break;
|
|
default:
|
|
val[0] = Nodes[n].Valp;
|
|
val[1] = MulVal;
|
|
err = vp->Compute(g, val, 2, op);
|
|
} // endswitch Op
|
|
|
|
if (err)
|
|
vp->Reset();
|
|
|
|
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)->GetValue(Nodes[i].Key);
|
|
|
|
break;
|
|
case TYPE_JAR:
|
|
if (!Nodes[i].Key)
|
|
if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
|
|
val = ((PJAR)row)->GetValue(Nodes[i].Rank);
|
|
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
|
|
} // endswitch Type
|
|
|
|
if (i < Nod-1)
|
|
if (!(row = (val) ? val->GetJsp() : NULL))
|
|
val = NULL;
|
|
|
|
} // endfor i
|
|
|
|
return (val != NULL);
|
|
} // end of CheckPath
|
|
|
|
/***********************************************************************/
|
|
/* GetRow: Set the complete path of the object to be set. */
|
|
/***********************************************************************/
|
|
PJSON JSNX::GetRow(PGLOBAL g)
|
|
{
|
|
PJVAL val = NULL;
|
|
PJAR arp;
|
|
PJSON nwr, row = Row;
|
|
|
|
for (int i = 0; i < Nod - 1 && row; i++) {
|
|
if (Nodes[i].Op == OP_XX)
|
|
break;
|
|
else switch (row->GetType()) {
|
|
case TYPE_JOB:
|
|
if (!Nodes[i].Key)
|
|
// Expected Array was not there, wrap the value
|
|
continue;
|
|
|
|
val = ((PJOB)row)->GetValue(Nodes[i].Key);
|
|
break;
|
|
case TYPE_JAR:
|
|
arp = (PJAR)row;
|
|
|
|
if (!Nodes[i].Key) {
|
|
if (Nodes[i].Op == OP_EQ)
|
|
val = arp->GetValue(Nodes[i].Rank);
|
|
else
|
|
val = arp->GetValue(Nodes[i].Rx);
|
|
|
|
} else {
|
|
// Unexpected array, unwrap it as [0]
|
|
val = arp->GetValue(0);
|
|
i--;
|
|
} // endif Nodes
|
|
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
|
|
val = NULL;
|
|
} // endswitch Type
|
|
|
|
if (val) {
|
|
row = val->GetJson();
|
|
} else {
|
|
// Construct missing objects
|
|
for (i++; row && i < Nod; i++) {
|
|
if (Nodes[i].Op == OP_XX)
|
|
break;
|
|
else if (!Nodes[i].Key)
|
|
// Construct intermediate array
|
|
nwr = new(g)JARRAY;
|
|
else
|
|
nwr = new(g)JOBJECT;
|
|
|
|
if (row->GetType() == TYPE_JOB) {
|
|
((PJOB)row)->SetValue(g, new(g)JVALUE(nwr), Nodes[i-1].Key);
|
|
} else if (row->GetType() == TYPE_JAR) {
|
|
((PJAR)row)->AddValue(g, new(g)JVALUE(nwr));
|
|
((PJAR)row)->InitArray(g);
|
|
} else {
|
|
strcpy(g->Message, "Wrong type when writing new row");
|
|
nwr = NULL;
|
|
} // endif's
|
|
|
|
row = nwr;
|
|
} // endfor i
|
|
|
|
break;
|
|
} // endelse
|
|
|
|
} // endfor i
|
|
|
|
return row;
|
|
} // end of GetRow
|
|
|
|
/***********************************************************************/
|
|
/* WriteValue: */
|
|
/***********************************************************************/
|
|
my_bool JSNX::WriteValue(PGLOBAL g, PJVAL jvalp)
|
|
{
|
|
PJOB objp = NULL;
|
|
PJAR arp = NULL;
|
|
PJVAL jvp = NULL;
|
|
PJSON row = GetRow(g);
|
|
|
|
if (!row)
|
|
return true;
|
|
|
|
switch (row->GetType()) {
|
|
case TYPE_JOB: objp = (PJOB)row; break;
|
|
case TYPE_JAR: arp = (PJAR)row; break;
|
|
case TYPE_JVAL: jvp = (PJVAL)row; break;
|
|
default:
|
|
strcpy(g->Message, "Invalid target type");
|
|
return true;
|
|
} // endswitch Type
|
|
|
|
if (arp) {
|
|
if (!Nodes[Nod-1].Key) {
|
|
if (Nodes[Nod-1].Op == OP_EQ)
|
|
arp->SetValue(g, jvalp, Nodes[Nod-1].Rank);
|
|
else
|
|
arp->AddValue(g, jvalp);
|
|
|
|
arp->InitArray(g);
|
|
} // endif Key
|
|
|
|
} else if (objp) {
|
|
if (Nodes[Nod-1].Key)
|
|
objp->SetValue(g, jvalp, Nodes[Nod-1].Key);
|
|
|
|
} else if (jvp)
|
|
jvp->SetValue(jvalp);
|
|
|
|
return false;
|
|
} // end of WriteValue
|
|
|
|
/*********************************************************************************/
|
|
/* Locate a value in a JSON tree: */
|
|
/*********************************************************************************/
|
|
PSZ JSNX::Locate(PGLOBAL g, PJSON jsp, PJVAL jvp, int k)
|
|
{
|
|
PSZ str = NULL;
|
|
my_bool b = false, err = true;
|
|
|
|
g->Message[0] = 0;
|
|
|
|
if (!jsp) {
|
|
strcpy(g->Message, "Null json tree");
|
|
return NULL;
|
|
} // endif jsp
|
|
|
|
try {
|
|
// Write to the path string
|
|
Jp = new(g) JOUTSTR(g);
|
|
Jp->WriteChr('$');
|
|
Jvalp = jvp;
|
|
K = k;
|
|
|
|
switch (jsp->GetType()) {
|
|
case TYPE_JAR:
|
|
err = LocateArray((PJAR)jsp);
|
|
break;
|
|
case TYPE_JOB:
|
|
err = LocateObject((PJOB)jsp);
|
|
break;
|
|
case TYPE_JVAL:
|
|
err = LocateValue((PJVAL)jsp);
|
|
break;
|
|
default:
|
|
err = true;
|
|
} // endswitch Type
|
|
|
|
if (err) {
|
|
if (!g->Message[0])
|
|
strcpy(g->Message, "Invalid json tree");
|
|
|
|
} else if (Found) {
|
|
Jp->WriteChr('\0');
|
|
PlugSubAlloc(g, NULL, Jp->N);
|
|
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(PJAR jarp)
|
|
{
|
|
char s[16];
|
|
size_t m = Jp->N;
|
|
|
|
for (int i = 0; i < jarp->size() && !Found; i++) {
|
|
Jp->N = m;
|
|
sprintf(s, "[%d]", i + B);
|
|
|
|
if (Jp->WriteStr(s))
|
|
return true;
|
|
|
|
if (LocateValue(jarp->GetValue(i)))
|
|
return true;
|
|
|
|
} // endfor i
|
|
|
|
return false;
|
|
} // end of LocateArray
|
|
|
|
/*********************************************************************************/
|
|
/* Locate in a JSON Object. */
|
|
/*********************************************************************************/
|
|
my_bool JSNX::LocateObject(PJOB jobp)
|
|
{
|
|
size_t m;
|
|
|
|
if (Jp->WriteChr('.'))
|
|
return true;
|
|
|
|
m = Jp->N;
|
|
|
|
for (PJPR pair = jobp->First; pair && !Found; pair = pair->Next) {
|
|
Jp->N = m;
|
|
|
|
if (Jp->WriteStr(pair->Key))
|
|
return true;
|
|
|
|
if (LocateValue(pair->Val))
|
|
return true;
|
|
|
|
} // endfor i
|
|
|
|
return false;
|
|
} // end of LocateObject
|
|
|
|
/*********************************************************************************/
|
|
/* Locate a JSON Value. */
|
|
/*********************************************************************************/
|
|
my_bool JSNX::LocateValue(PJVAL jvp)
|
|
{
|
|
if (CompareTree(Jvalp, jvp))
|
|
Found = (--K == 0);
|
|
else if (jvp->GetArray())
|
|
return LocateArray(jvp->GetArray());
|
|
else if (jvp->GetObject())
|
|
return LocateObject(jvp->GetObject());
|
|
|
|
return false;
|
|
} // end of LocateValue
|
|
|
|
/*********************************************************************************/
|
|
/* Locate all occurrences of a value in a JSON tree: */
|
|
/*********************************************************************************/
|
|
PSZ JSNX::LocateAll(PGLOBAL g, PJSON jsp, PJVAL jvp, int mx)
|
|
{
|
|
PSZ str = NULL;
|
|
my_bool b = false, err = true;
|
|
PJPN jnp;
|
|
|
|
if (!jsp) {
|
|
strcpy(g->Message, "Null json tree");
|
|
return NULL;
|
|
} // endif jsp
|
|
|
|
try {
|
|
jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx);
|
|
memset(jnp, 0, sizeof(JPN) * mx);
|
|
g->Message[0] = 0;
|
|
|
|
// Write to the path string
|
|
Jp = new(g)JOUTSTR(g);
|
|
Jvalp = jvp;
|
|
Imax = mx - 1;
|
|
Jpnp = jnp;
|
|
Jp->WriteChr('[');
|
|
|
|
switch (jsp->GetType()) {
|
|
case TYPE_JAR:
|
|
err = LocateArrayAll((PJAR)jsp);
|
|
break;
|
|
case TYPE_JOB:
|
|
err = LocateObjectAll((PJOB)jsp);
|
|
break;
|
|
case TYPE_JVAL:
|
|
err = LocateValueAll((PJVAL)jsp);
|
|
break;
|
|
default:
|
|
err = true;
|
|
} // endswitch Type
|
|
|
|
if (!err) {
|
|
if (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(PJAR jarp)
|
|
{
|
|
if (I < Imax) {
|
|
Jpnp[++I].Type = TYPE_JAR;
|
|
|
|
for (int i = 0; i < jarp->size(); i++) {
|
|
Jpnp[I].N = i;
|
|
|
|
if (LocateValueAll(jarp->GetValue(i)))
|
|
return true;
|
|
|
|
} // endfor i
|
|
|
|
I--;
|
|
} // endif I
|
|
|
|
return false;
|
|
} // end of LocateArrayAll
|
|
|
|
/*********************************************************************************/
|
|
/* Locate in a JSON Object. */
|
|
/*********************************************************************************/
|
|
my_bool JSNX::LocateObjectAll(PJOB jobp)
|
|
{
|
|
if (I < Imax) {
|
|
Jpnp[++I].Type = TYPE_JOB;
|
|
|
|
for (PJPR pair = jobp->First; pair; pair = pair->Next) {
|
|
Jpnp[I].Key = pair->Key;
|
|
|
|
if (LocateValueAll(pair->Val))
|
|
return true;
|
|
|
|
} // endfor i
|
|
|
|
I--;
|
|
} // endif I
|
|
|
|
return false;
|
|
} // end of LocateObjectAll
|
|
|
|
/*********************************************************************************/
|
|
/* Locate a JSON Value. */
|
|
/*********************************************************************************/
|
|
my_bool JSNX::LocateValueAll(PJVAL jvp)
|
|
{
|
|
if (CompareTree(Jvalp, jvp))
|
|
return AddPath();
|
|
else if (jvp->GetArray())
|
|
return LocateArrayAll(jvp->GetArray());
|
|
else if (jvp->GetObject())
|
|
return LocateObjectAll(jvp->GetObject());
|
|
|
|
return false;
|
|
} // end of LocateValueAll
|
|
|
|
/*********************************************************************************/
|
|
/* Compare two JSON trees. */
|
|
/*********************************************************************************/
|
|
my_bool JSNX::CompareTree(PJSON jp1, PJSON jp2)
|
|
{
|
|
if (!jp1 || !jp2 || jp1->GetType() != jp2->GetType()
|
|
|| jp1->size() != jp2->size())
|
|
return false;
|
|
|
|
my_bool found = true;
|
|
|
|
if (jp1->GetType() == TYPE_JVAL) {
|
|
PVAL v1 = jp1->GetValue(), v2 = jp2->GetValue();
|
|
|
|
if (v1 && v2) {
|
|
if (v1->GetType() == v2->GetType())
|
|
found = !v1->CompareValue(v2);
|
|
else
|
|
found = false;
|
|
|
|
} else
|
|
found = CompareTree(jp1->GetJsp(), jp2->GetJsp());
|
|
|
|
} else if (jp1->GetType() == TYPE_JAR) {
|
|
for (int i = 0; found && i < jp1->size(); i++)
|
|
found = (CompareTree(jp1->GetValue(i), jp2->GetValue(i)));
|
|
|
|
} else if (jp1->GetType() == TYPE_JOB) {
|
|
PJPR p1 = jp1->GetFirst(), p2 = jp2->GetFirst();
|
|
|
|
for (; found && p1 && p2; p1 = p1->Next, p2 = p2->Next)
|
|
found = CompareTree(p1->Val, p2->Val);
|
|
|
|
} else
|
|
found = false;
|
|
|
|
return found;
|
|
} // end of CompareTree
|
|
|
|
/*********************************************************************************/
|
|
/* Add the found path to the list. */
|
|
/*********************************************************************************/
|
|
my_bool JSNX::AddPath(void)
|
|
{
|
|
char s[16];
|
|
|
|
if (Jp->WriteStr("\"$"))
|
|
return true;
|
|
|
|
for (int i = 0; i <= I; i++) {
|
|
if (Jpnp[i].Type == TYPE_JAR) {
|
|
sprintf(s, "[%d]", Jpnp[i].N + B);
|
|
|
|
if (Jp->WriteStr(s))
|
|
return true;
|
|
|
|
} else {
|
|
if (Jp->WriteChr('.'))
|
|
return true;
|
|
|
|
if (Jp->WriteStr(Jpnp[i].Key))
|
|
return true;
|
|
|
|
} // endif's
|
|
|
|
} // endfor i
|
|
|
|
if (Jp->WriteStr("\","))
|
|
return true;
|
|
|
|
return false;
|
|
} // end of AddPath
|
|
|
|
/* --------------------------------- JSON UDF ---------------------------------- */
|
|
|
|
// BSON size should be equal on Linux and Windows
|
|
#define BMX 255
|
|
typedef struct BSON *PBSON;
|
|
|
|
/*********************************************************************************/
|
|
/* Structure used to return binary json. */
|
|
/*********************************************************************************/
|
|
struct BSON {
|
|
char Msg[BMX + 1];
|
|
char *Filename;
|
|
PGLOBAL G;
|
|
int Pretty;
|
|
ulong Reslen;
|
|
my_bool Changed;
|
|
PJSON Top;
|
|
PJSON Jsp;
|
|
PBSON Bsp;
|
|
}; // end of struct BSON
|
|
|
|
/*********************************************************************************/
|
|
/* Allocate and initialize a BSON structure. */
|
|
/*********************************************************************************/
|
|
static PBSON JbinAlloc(PGLOBAL g, UDF_ARGS *args, ulong len, PJSON jsp)
|
|
{
|
|
PBSON bsp = (PBSON)PlgDBSubAlloc(g, NULL, sizeof(BSON));
|
|
|
|
if (bsp) {
|
|
strcpy(bsp->Msg, "Binary Json");
|
|
bsp->Msg[BMX] = 0;
|
|
bsp->Filename = NULL;
|
|
bsp->G = g;
|
|
bsp->Pretty = 2;
|
|
bsp->Reslen = len;
|
|
bsp->Changed = false;
|
|
bsp->Top = bsp->Jsp = jsp;
|
|
bsp->Bsp = (IsJson(args, 0) == 3) ? (PBSON)args->args[0] : NULL;
|
|
} else
|
|
PUSH_WARNING(g->Message);
|
|
|
|
return bsp;
|
|
} /* end of JbinAlloc */
|
|
|
|
/*********************************************************************************/
|
|
/* Set the BSON chain as changed. */
|
|
/*********************************************************************************/
|
|
static void SetChanged(PBSON bsp)
|
|
{
|
|
if (bsp->Bsp)
|
|
SetChanged(bsp->Bsp);
|
|
|
|
bsp->Changed = true;
|
|
} /* end of SetChanged */
|
|
|
|
/*********************************************************************************/
|
|
/* Replaces GetJsonGrpSize not usable when CONNECT is not installed. */
|
|
/*********************************************************************************/
|
|
static uint GetJsonGroupSize(void)
|
|
{
|
|
return (JsonGrpSize) ? JsonGrpSize : GetJsonGrpSize();
|
|
} // end of GetJsonGroupSize
|
|
|
|
/*********************************************************************************/
|
|
/* Program for SubSet re-initialization of the memory pool. */
|
|
/*********************************************************************************/
|
|
static my_bool JsonSubSet(PGLOBAL g)
|
|
{
|
|
PPOOLHEADER pph = (PPOOLHEADER)g->Sarea;
|
|
|
|
pph->To_Free = (OFFSET)((g->Createas) ? g->Createas : sizeof(POOLHEADER));
|
|
pph->FreeBlk = g->Sarea_Size - pph->To_Free;
|
|
return FALSE;
|
|
} /* end of JsonSubSet */
|
|
|
|
/*********************************************************************************/
|
|
/* Program for saving the status of the memory pools. */
|
|
/*********************************************************************************/
|
|
inline void JsonMemSave(PGLOBAL g)
|
|
{
|
|
g->Createas = (int)((PPOOLHEADER)g->Sarea)->To_Free;
|
|
} /* end of JsonMemSave */
|
|
|
|
/*********************************************************************************/
|
|
/* Program for freeing the memory pools. */
|
|
/*********************************************************************************/
|
|
inline void JsonFreeMem(PGLOBAL g)
|
|
{
|
|
g->Activityp = NULL;
|
|
PlugExit(g);
|
|
} /* end of JsonFreeMem */
|
|
|
|
/*********************************************************************************/
|
|
/* 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_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 JsonNew */
|
|
|
|
/*********************************************************************************/
|
|
/* Allocate and initialise the memory area. */
|
|
/*********************************************************************************/
|
|
static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args,
|
|
char *message, my_bool mbn,
|
|
unsigned long reslen, unsigned long memlen,
|
|
unsigned long more = 0)
|
|
{
|
|
PGLOBAL g = PlugInit(NULL, memlen + more + 500); // +500 to avoid CheckMem
|
|
|
|
if (!g) {
|
|
strcpy(message, "Allocation error");
|
|
return true;
|
|
} else if (g->Sarea_Size == 0) {
|
|
strcpy(message, g->Message);
|
|
PlugExit(g);
|
|
return true;
|
|
} // endif g
|
|
|
|
g->Mrr = (args->arg_count && args->args[0]) ? 1 : 0;
|
|
g->More = more;
|
|
initid->maybe_null = mbn;
|
|
initid->max_length = reslen;
|
|
initid->ptr = (char*)g;
|
|
return false;
|
|
} // end of JsonInit
|
|
|
|
/*********************************************************************************/
|
|
/* Check if a path was specified and set jvp according to it. */
|
|
/*********************************************************************************/
|
|
static my_bool CheckPath(PGLOBAL g, UDF_ARGS *args, PJSON jsp, PJVAL& jvp, int n)
|
|
{
|
|
for (uint i = n; i < args->arg_count; i++)
|
|
if (args->arg_type[i] == STRING_RESULT && args->args[i]) {
|
|
// A path to a subset of the json tree is given
|
|
char *path = MakePSZ(g, args, i);
|
|
|
|
if (path) {
|
|
PJSNX jsx = new(g)JSNX(g, jsp, TYPE_STRING);
|
|
|
|
if (jsx->SetJpath(g, path))
|
|
return true;
|
|
|
|
if (!(jvp = jsx->GetJson(g))) {
|
|
sprintf(g->Message, "No sub-item at '%s'", path);
|
|
return true;
|
|
} // endif jvp
|
|
|
|
} else {
|
|
strcpy(g->Message, "Path argument is null");
|
|
return true;
|
|
} // endif path
|
|
|
|
break;
|
|
} // endif type
|
|
|
|
return false;
|
|
} // end of CheckPath
|
|
|
|
/*********************************************************************************/
|
|
/* Make the result according to the first argument type. */
|
|
/*********************************************************************************/
|
|
static char *MakeResult(PGLOBAL g, UDF_ARGS *args, PJSON top, uint n = 2)
|
|
{
|
|
char *str;
|
|
|
|
if (IsJson(args, 0) == 2) {
|
|
// Make the change in the json file
|
|
int pretty = 2;
|
|
|
|
for (uint i = n; i < args->arg_count; i++)
|
|
if (args->arg_type[i] == INT_RESULT) {
|
|
pretty = (int)*(longlong*)args->args[i];
|
|
break;
|
|
} // endif type
|
|
|
|
if (!Serialize(g, top, MakePSZ(g, args, 0), pretty))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
str = NULL;
|
|
} else if (IsJson(args, 0) == 3) {
|
|
PBSON bsp = (PBSON)args->args[0];
|
|
|
|
if (bsp->Filename) {
|
|
// Make the change in the json file
|
|
if (!Serialize(g, top, bsp->Filename, bsp->Pretty))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
str = bsp->Filename;
|
|
} else if (!(str = Serialize(g, top, NULL, 0)))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
SetChanged(bsp);
|
|
} else if (!(str = Serialize(g, top, NULL, 0)))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
return str;
|
|
} // end of MakeResult
|
|
|
|
/*********************************************************************************/
|
|
/* Make the binary result according to the first argument type. */
|
|
/*********************************************************************************/
|
|
static PBSON MakeBinResult(PGLOBAL g, UDF_ARGS *args, PJSON top, ulong len, int n = 2)
|
|
{
|
|
PBSON bsnp = JbinAlloc(g, args, len, top);
|
|
|
|
if (!bsnp)
|
|
return NULL;
|
|
|
|
if (IsJson(args, 0) == 2) {
|
|
int pretty = 0;
|
|
|
|
for (uint i = n; i < args->arg_count; i++)
|
|
if (args->arg_type[i] == INT_RESULT) {
|
|
pretty = (int)*(longlong*)args->args[i];
|
|
break;
|
|
} // endif type
|
|
|
|
bsnp->Pretty = pretty;
|
|
|
|
if (bsnp->Filename = (char*)args->args[0]) {
|
|
bsnp->Filename = MakePSZ(g, args, 0);
|
|
strncpy(bsnp->Msg, bsnp->Filename, BMX);
|
|
} else
|
|
strncpy(bsnp->Msg, "null filename", BMX);
|
|
|
|
} else if (IsJson(args, 0) == 3) {
|
|
PBSON bsp = (PBSON)args->args[0];
|
|
|
|
if (bsp->Filename) {
|
|
bsnp->Filename = bsp->Filename;
|
|
strncpy(bsnp->Msg, bsp->Filename, BMX);
|
|
bsnp->Pretty = bsp->Pretty;
|
|
} else
|
|
strcpy(bsnp->Msg, "Json Binary item");
|
|
|
|
} else
|
|
strcpy(bsnp->Msg, "Json Binary item");
|
|
|
|
return bsnp;
|
|
} // end of MakeBinResult
|
|
|
|
/*********************************************************************************/
|
|
/* Returns a pointer to the first integer argument found from the nth argument. */
|
|
/*********************************************************************************/
|
|
static int *GetIntArgPtr(PGLOBAL g, UDF_ARGS *args, uint& n)
|
|
{
|
|
int *x = NULL;
|
|
|
|
for (uint i = n; i < args->arg_count; i++)
|
|
if (args->arg_type[i] == INT_RESULT) {
|
|
if (args->args[i]) {
|
|
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. */
|
|
/*********************************************************************************/
|
|
static int IsJson(UDF_ARGS *args, uint i, bool b)
|
|
{
|
|
int n = 0;
|
|
|
|
if (i >= args->arg_count || args->arg_type[i] != STRING_RESULT) {
|
|
} else if (!strnicmp(args->attributes[i], "Json_", 5)) {
|
|
if (!args->args[i] || strchr("[{ \t\r\n", *args->args[i]))
|
|
n = 1; // arg should be is a json item
|
|
else
|
|
n = 2; // A file name may have been returned
|
|
|
|
} else if (!strnicmp(args->attributes[i], "Jbin_", 5)) {
|
|
if (args->lengths[i] == sizeof(BSON))
|
|
n = 3; // arg is a binary json item
|
|
else
|
|
n = 2; // A file name may have been returned
|
|
|
|
} else if (!strnicmp(args->attributes[i], "Jfile_", 6)) {
|
|
n = 2; // arg is a json file name
|
|
} else if (b) {
|
|
char *sap;
|
|
PGLOBAL g = PlugInit(NULL, 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. */
|
|
/*********************************************************************************/
|
|
static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
|
|
unsigned long& reslen, unsigned long& memlen,
|
|
my_bool mod = false)
|
|
{
|
|
char fn[_MAX_PATH];
|
|
unsigned long i, k, m, n;
|
|
long fl = 0, j = -1;
|
|
|
|
reslen = args->arg_count + 2;
|
|
|
|
// Calculate the result max length
|
|
for (i = 0; i < args->arg_count; i++) {
|
|
n = IsJson(args, i);
|
|
|
|
if (obj) {
|
|
if (!(k = args->attribute_lengths[i]))
|
|
k = strlen(args->attributes[i]);
|
|
|
|
reslen += (k + 3); // For quotes and :
|
|
} // endif obj
|
|
|
|
switch (args->arg_type[i]) {
|
|
case STRING_RESULT:
|
|
if (n == 2 && args->args[i]) {
|
|
if (!mod) {
|
|
m = MY_MIN(args->lengths[i], sizeof(fn) - 1);
|
|
memcpy(fn, args->args[i], m);
|
|
fn[m] = 0;
|
|
j = i;
|
|
fl = GetFileLength(fn);
|
|
reslen += fl;
|
|
} else
|
|
reslen += args->lengths[i];
|
|
|
|
} else if (n == 3 && args->args[i])
|
|
reslen += ((PBSON)args->args[i])->Reslen;
|
|
else if (n == 1)
|
|
reslen += args->lengths[i];
|
|
else
|
|
reslen += (args->lengths[i] + 1) * 2; // Pessimistic !
|
|
|
|
break;
|
|
case INT_RESULT:
|
|
reslen += 20;
|
|
break;
|
|
case REAL_RESULT:
|
|
reslen += 31;
|
|
break;
|
|
case DECIMAL_RESULT:
|
|
reslen += (args->lengths[i] + 7); // 6 decimals
|
|
break;
|
|
case TIME_RESULT:
|
|
case ROW_RESULT:
|
|
default:
|
|
// What should we do here ?
|
|
break;
|
|
} // endswitch arg_type
|
|
|
|
} // endfor i
|
|
|
|
// Calculate the amount of memory needed
|
|
memlen = MEMFIX + sizeof(JOUTSTR) + reslen;
|
|
|
|
for (i = 0; i < args->arg_count; i++) {
|
|
n = IsJson(args, i);
|
|
memlen += (args->lengths[i] + sizeof(JVALUE));
|
|
|
|
if (obj) {
|
|
if (!(k = args->attribute_lengths[i]))
|
|
k = strlen(args->attributes[i]);
|
|
|
|
memlen += (k + sizeof(JOBJECT) + sizeof(JPAIR));
|
|
} else
|
|
memlen += sizeof(JARRAY);
|
|
|
|
switch (args->arg_type[i]) {
|
|
case STRING_RESULT:
|
|
if (n == 2 && args->args[i]) {
|
|
if ((signed)i != j) {
|
|
m = MY_MIN(args->lengths[i], sizeof(fn) - 1);
|
|
memcpy(fn, args->args[i], m);
|
|
fn[m] = 0;
|
|
j = -1;
|
|
fl = GetFileLength(fn);
|
|
} // endif i
|
|
|
|
memlen += fl * M;
|
|
} else if (n == 1) {
|
|
if (i == 0)
|
|
memlen += sizeof(BSON); // For Jbin functions
|
|
|
|
memlen += args->lengths[i] * M; // Estimate parse memory
|
|
} else if (n == 3)
|
|
memlen += sizeof(JVALUE);
|
|
|
|
memlen += sizeof(TYPVAL<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. */
|
|
/*********************************************************************************/
|
|
static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n,
|
|
my_bool m, my_bool obj = false, my_bool mod = false)
|
|
{
|
|
unsigned long rl, ml;
|
|
my_bool b = false;
|
|
|
|
n = MY_MIN(n, args->arg_count);
|
|
|
|
for (uint i = 0; i < n; i++)
|
|
if (IsJson(args, i) == 2 ||
|
|
(b = (m && !i && args->arg_type[0] == STRING_RESULT && !IsJson(args, 0)))) {
|
|
if (CalcLen(args, obj, rl, ml, mod))
|
|
return true;
|
|
else if (b) {
|
|
ulong len;
|
|
char *p = args->args[0];
|
|
|
|
// Is this a file name?
|
|
if (p && !strchr("[{ \t\r\n", *p) && (len = GetFileLength(p)))
|
|
ml += len * (M + 1);
|
|
else
|
|
ml += args->lengths[0] * M;
|
|
|
|
} // endif b
|
|
|
|
ml += g->More;
|
|
|
|
if (ml > g->Sarea_Size) {
|
|
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->Createas = 0;
|
|
g->Xchk = NULL;
|
|
initid->max_length = rl;
|
|
} // endif Size
|
|
|
|
break;
|
|
} // endif IsJson
|
|
|
|
JsonSubSet(g);
|
|
return false;
|
|
} // end of CheckMemory
|
|
|
|
/*********************************************************************************/
|
|
/* Make a zero terminated string from the passed argument. */
|
|
/*********************************************************************************/
|
|
static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i)
|
|
{
|
|
if (args->arg_count > (unsigned)i && args->args[i]) {
|
|
int n = args->lengths[i];
|
|
PSZ s = (PSZ)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, int& len)
|
|
{
|
|
char *memory;
|
|
HANDLE hFile;
|
|
MEMMAP mm;
|
|
PJSON jsp;
|
|
|
|
/*******************************************************************************/
|
|
/* Create the mapping file object. */
|
|
/*******************************************************************************/
|
|
hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DWORD rc = GetLastError();
|
|
|
|
if (!(*g->Message))
|
|
sprintf(g->Message, MSG(OPEN_MODE_ERROR), "map", (int)rc, fn);
|
|
|
|
return NULL;
|
|
} // endif hFile
|
|
|
|
/*******************************************************************************/
|
|
/* Get the file size (assuming file is smaller than 4 GB) */
|
|
/*******************************************************************************/
|
|
len = mm.lenL;
|
|
memory = (char *)mm.memory;
|
|
|
|
if (!len) { // Empty or deleted file
|
|
CloseFileHandle(hFile);
|
|
return NULL;
|
|
} // endif len
|
|
|
|
if (!memory) {
|
|
CloseFileHandle(hFile);
|
|
sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, GetLastError());
|
|
return NULL;
|
|
} // endif Memory
|
|
|
|
CloseFileHandle(hFile); // Not used anymore
|
|
|
|
/*********************************************************************************/
|
|
/* Parse the json file and allocate its tree structure. */
|
|
/*********************************************************************************/
|
|
g->Message[0] = 0;
|
|
jsp = ParseJson(g, memory, len, pretty);
|
|
CloseMemMap(memory, len);
|
|
return jsp;
|
|
} // end of ParseJsonFile
|
|
|
|
/*********************************************************************************/
|
|
/* Return a json file contains. */
|
|
/*********************************************************************************/
|
|
static char *GetJsonFile(PGLOBAL g, char *fn)
|
|
{
|
|
char *str;
|
|
int h, n, len;
|
|
|
|
#if defined(UNIX) || defined(UNIV_LINUX)
|
|
h= open(fn, O_RDONLY);
|
|
#else
|
|
h= open(fn, _O_RDONLY, _O_TEXT);
|
|
#endif
|
|
|
|
if (h == -1) {
|
|
sprintf(g->Message, "Error %d opening %s", errno, fn);
|
|
return NULL;
|
|
} // endif h
|
|
|
|
if ((len = _filelength(h)) < 0) {
|
|
sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn);
|
|
close(h);
|
|
return NULL;
|
|
} // endif len
|
|
|
|
if ((str = (char*)PlgDBSubAlloc(g, NULL, len + 1))) {
|
|
if ((n = read(h, str, len)) < 0) {
|
|
sprintf(g->Message, "Error %d reading %d bytes from %s", errno, len, fn);
|
|
return NULL;
|
|
} // endif n
|
|
|
|
str[n] = 0;
|
|
close(h);
|
|
} // 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;
|
|
short c;
|
|
long long bigint;
|
|
PJSON jsp;
|
|
PJVAL jvp = new(g) JVALUE;
|
|
|
|
if (top)
|
|
*top = NULL;
|
|
|
|
if (sap) switch (args->arg_type[i]) {
|
|
case STRING_RESULT:
|
|
if ((len = args->lengths[i])) {
|
|
if ((n = IsJson(args, i)) < 3)
|
|
sap = MakePSZ(g, args, i);
|
|
|
|
if (n) {
|
|
if (n == 3) {
|
|
if (top)
|
|
*top = ((PBSON)sap)->Top;
|
|
|
|
jsp = ((PBSON)sap)->Jsp;
|
|
} else {
|
|
if (n == 2) {
|
|
if (!(sap = GetJsonFile(g, sap))) {
|
|
PUSH_WARNING(g->Message);
|
|
return jvp;
|
|
} // endif sap
|
|
|
|
len = strlen(sap);
|
|
} // endif n
|
|
|
|
if (!(jsp = ParseJson(g, sap, strlen(sap))))
|
|
PUSH_WARNING(g->Message);
|
|
else if (top)
|
|
*top = jsp;
|
|
|
|
} // endif's n
|
|
|
|
if (jsp && jsp->GetType() == TYPE_JVAL)
|
|
jvp = (PJVAL)jsp;
|
|
else
|
|
jvp->SetValue(jsp);
|
|
|
|
} else {
|
|
c = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1;
|
|
jvp->SetString(g, sap, c);
|
|
} // endif n
|
|
|
|
} // endif len
|
|
|
|
break;
|
|
case INT_RESULT:
|
|
bigint = *(long long*)sap;
|
|
|
|
if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) ||
|
|
(bigint == 1LL && !strcmp(args->attributes[i], "TRUE")))
|
|
jvp->SetTiny(g, (char)bigint);
|
|
else
|
|
jvp->SetBigint(g, bigint);
|
|
|
|
break;
|
|
case REAL_RESULT:
|
|
jvp->SetFloat(g, *(double*)sap);
|
|
break;
|
|
case DECIMAL_RESULT:
|
|
jvp->SetFloat(g, atof(MakePSZ(g, args, i)));
|
|
break;
|
|
case TIME_RESULT:
|
|
case ROW_RESULT:
|
|
default:
|
|
break;
|
|
} // endswitch arg_type
|
|
|
|
return jvp;
|
|
} // end of MakeValue
|
|
|
|
/*********************************************************************************/
|
|
/* 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
|
|
|
|
/*********************************************************************************/
|
|
/* Make a Json value containing the parameter. */
|
|
/*********************************************************************************/
|
|
my_bool jsonvalue_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count > 1) {
|
|
strcpy(message, "Cannot accept more than 1 argument");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
return JsonInit(initid, args, message, false, reslen, memlen);
|
|
} // end of jsonvalue_init
|
|
|
|
char *jsonvalue(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *, char *)
|
|
{
|
|
char *str;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (!g->Xchk) {
|
|
if (!CheckMemory(g, initid, args, 1, false)) {
|
|
PJVAL jvp = MakeValue(g, args, 0);
|
|
|
|
if (!(str = Serialize(g, jvp, NULL, 0)))
|
|
str = strcpy(result, g->Message);
|
|
|
|
} else
|
|
str = strcpy(result, g->Message);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? str : NULL;
|
|
} else
|
|
str = (char*)g->Xchk;
|
|
|
|
*res_length = strlen(str);
|
|
return str;
|
|
} // end of JsonValue
|
|
|
|
void jsonvalue_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jsonvalue_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Make a Json array containing all the parameters. */
|
|
/*********************************************************************************/
|
|
my_bool json_make_array_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
CalcLen(args, false, reslen, memlen);
|
|
return JsonInit(initid, args, message, false, reslen, memlen);
|
|
} // end of json_make_array_init
|
|
|
|
char *json_make_array(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *, char *)
|
|
{
|
|
char *str;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (!g->Xchk) {
|
|
if (!CheckMemory(g, initid, args, args->arg_count, false)) {
|
|
PJAR arp = new(g)JARRAY;
|
|
|
|
for (uint i = 0; i < args->arg_count; i++)
|
|
arp->AddValue(g, MakeValue(g, args, i));
|
|
|
|
arp->InitArray(g);
|
|
|
|
if (!(str = Serialize(g, arp, NULL, 0)))
|
|
str = strcpy(result, g->Message);
|
|
|
|
} else
|
|
str = strcpy(result, g->Message);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? str : NULL;
|
|
} else
|
|
str = (char*)g->Xchk;
|
|
|
|
*res_length = strlen(str);
|
|
return str;
|
|
} // end of json_make_array
|
|
|
|
void json_make_array_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_make_array_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Add one or several values to a Json array. */
|
|
/*********************************************************************************/
|
|
my_bool json_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "This function must have at least 2 arguments");
|
|
return true;
|
|
//} else if (!IsJson(args, 0, 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->AddValue(g, jvp);
|
|
top = arp;
|
|
} else
|
|
arp = jvp->GetArray();
|
|
|
|
for (uint i = 1; i < args->arg_count; i++)
|
|
arp->AddValue(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->AddValue(gb, JvalNew(gb, TYPE_JVAL, jvp));
|
|
jvp->SetValue(arp);
|
|
|
|
if (!top)
|
|
top = arp;
|
|
|
|
} // endif arp
|
|
|
|
} else
|
|
arp = jvp->GetArray();
|
|
|
|
if (arp) {
|
|
arp->AddValue(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->GetValue(i)->GetBigint();
|
|
|
|
} else {
|
|
PUSH_WARNING("First argument target is not an array");
|
|
} // endif jvp
|
|
|
|
} else {
|
|
*error = 1;
|
|
n = -1LL;
|
|
} // end of CheckMemory
|
|
|
|
if (g->N) {
|
|
// Keep result of constant function
|
|
long long *np;
|
|
|
|
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->GetValue(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->GetValue(i)->GetFloat();
|
|
|
|
n /= arp->size();
|
|
} // endif size
|
|
|
|
} else {
|
|
PUSH_WARNING("First argument target is not an array");
|
|
} // endif jvp
|
|
|
|
} else {
|
|
*error = 1;
|
|
n = -1.0;
|
|
} // 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->SetValue(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->SetValue(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->SetValue(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->SetValue(gb, jvp, key);
|
|
str = MakeResult(g, args, top);
|
|
} else {
|
|
PUSH_WARNING("First argument target is not an object");
|
|
// if (g->Mrr) *error = 1; (only if no path)
|
|
} // endif jvp
|
|
|
|
} // endif CheckMemory
|
|
|
|
// In case of error or file, return unchanged argument
|
|
if (!str)
|
|
str = MakePSZ(g, args, 0);
|
|
|
|
if (g->N)
|
|
// Keep result of constant function
|
|
g->Xchk = str;
|
|
|
|
fin:
|
|
if (!str) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of json_object_add
|
|
|
|
void json_object_add_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_object_add_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Delete a value from a Json object. */
|
|
/*********************************************************************************/
|
|
my_bool json_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "This function must have 2 or 3 arguments");
|
|
return true;
|
|
} else if (!IsJson(args, 0)) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (args->arg_type[1] != STRING_RESULT) {
|
|
strcpy(message, "Second argument must be a key string");
|
|
return true;
|
|
} else
|
|
CalcLen(args, true, reslen, memlen, true);
|
|
|
|
if (!JsonInit(initid, args, message, true, reslen, memlen)) {
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
// This is a constant function
|
|
g->N = (initid->const_item) ? 1 : 0;
|
|
|
|
// This is to avoid double execution when using prepared statements
|
|
if (IsJson(args, 0) > 1)
|
|
initid->const_item = 0;
|
|
|
|
return false;
|
|
} else
|
|
return true;
|
|
|
|
} // end of json_object_delete_init
|
|
|
|
char *json_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *str = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (g->Xchk) {
|
|
// This constant function was recalled
|
|
str = (char*)g->Xchk;
|
|
goto fin;
|
|
} // endif Xchk
|
|
|
|
if (!CheckMemory(g, initid, args, 1, false, true, true)) {
|
|
PCSZ key;
|
|
PJOB jobp;
|
|
PJSON jsp, top;
|
|
PJVAL jvp = MakeValue(g, args, 0, &top);
|
|
|
|
jsp = jvp->GetJson();
|
|
|
|
if (CheckPath(g, args, jsp, jvp, 2))
|
|
PUSH_WARNING(g->Message);
|
|
else if (jvp && jvp->GetValType() == TYPE_JOB) {
|
|
key = MakeKey(GetMemPtr(g, args, 0), args, 1);
|
|
jobp = jvp->GetObject();
|
|
jobp->DeleteKey(key);
|
|
str = MakeResult(g, args, top);
|
|
} else {
|
|
PUSH_WARNING("First argument target is not an object");
|
|
// if (g->Mrr) *error = 1; (only if no path)
|
|
} // endif jvp
|
|
|
|
} // endif CheckMemory
|
|
|
|
// In case of error or file, return unchanged argument
|
|
if (!str)
|
|
str = MakePSZ(g, args, 0);
|
|
|
|
if (g->N)
|
|
// Keep result of constant function
|
|
g->Xchk = str;
|
|
|
|
fin:
|
|
if (!str) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of json_object_delete
|
|
|
|
void json_object_delete_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_object_delete_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Returns an array of the Json object keys. */
|
|
/*********************************************************************************/
|
|
my_bool json_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count != 1) {
|
|
strcpy(message, "This function must have 1 argument");
|
|
return true;
|
|
} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "Argument must be a json item");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of json_object_list_init
|
|
|
|
char *json_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *str = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (!g->N) {
|
|
if (!CheckMemory(g, initid, args, 1, true, true)) {
|
|
char *p;
|
|
PJSON jsp;
|
|
PJVAL jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
PUSH_WARNING(g->Message);
|
|
return NULL;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = jvp->GetJson();
|
|
|
|
if (jsp->GetType() == TYPE_JOB) {
|
|
PJAR jarp = ((PJOB)jsp)->GetKeyList(g);
|
|
|
|
if (!(str = Serialize(g, jarp, NULL, 0)))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
} else {
|
|
PUSH_WARNING("First argument is not an object");
|
|
if (g->Mrr) *error = 1;
|
|
} // endif jvp
|
|
|
|
} // endif CheckMemory
|
|
|
|
if (initid->const_item) {
|
|
// Keep result of constant function
|
|
g->Xchk = str;
|
|
g->N = 1; // str can be NULL
|
|
} // endif const_item
|
|
|
|
} else
|
|
str = (char*)g->Xchk;
|
|
|
|
if (!str) {
|
|
*is_null = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of json_object_list
|
|
|
|
void json_object_list_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_object_list_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* 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->AddValue(g, MakeValue(g, args, 0));
|
|
|
|
} // end of json_array_grp_add
|
|
|
|
char *json_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result,
|
|
unsigned long *res_length, char *, char *)
|
|
{
|
|
char *str;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PJAR arp = (PJAR)g->Activityp;
|
|
|
|
if (g->N < 0)
|
|
PUSH_WARNING("Result truncated to json_grp_size values");
|
|
|
|
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->SetValue(g, MakeValue(g, args, 1), MakePSZ(g, args, 0));
|
|
|
|
} // end of json_object_grp_add
|
|
|
|
char *json_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result,
|
|
unsigned long *res_length, char *, char *)
|
|
{
|
|
char *str;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PJOB objp = (PJOB)g->Activityp;
|
|
|
|
if (g->N < 0)
|
|
PUSH_WARNING("Result truncated to json_grp_size values");
|
|
|
|
if (!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) {
|
|
sprintf(g->Message, "Argument %d is not an array or object", i);
|
|
PUSH_WARNING(g->Message);
|
|
} else
|
|
jsp[i] = jvp->GetJsp();
|
|
|
|
} // endfor i
|
|
|
|
if (jsp[0]) {
|
|
if (jsp[0]->Merge(GetMemPtr(g, args, 0), jsp[1]))
|
|
PUSH_WARNING(GetMemPtr(g, args, 0)->Message);
|
|
else
|
|
str = MakeResult(g, args, top);
|
|
|
|
} // endif jsp
|
|
|
|
} // endif CheckMemory
|
|
|
|
// In case of error or file, return unchanged argument
|
|
if (!str)
|
|
str = MakePSZ(g, args, 0);
|
|
|
|
if (g->N)
|
|
// Keep result of constant function
|
|
g->Xchk = str;
|
|
|
|
fin:
|
|
if (!str) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of json_item_merge
|
|
|
|
void json_item_merge_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_item_merge_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Get a Json item from a Json document. */
|
|
/*********************************************************************************/
|
|
my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, more;
|
|
int n = IsJson(args, 0);
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "This function must have at least 2 arguments");
|
|
return true;
|
|
} else if (!n && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (args->arg_type[1] != STRING_RESULT) {
|
|
strcpy(message, "Second argument is not a string (jpath)");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
if (n == 2 && args->args[0]) {
|
|
char fn[_MAX_PATH];
|
|
long fl;
|
|
|
|
memcpy(fn, args->args[0], args->lengths[0]);
|
|
fn[args->lengths[0]] = 0;
|
|
fl = GetFileLength(fn);
|
|
more = fl * 3;
|
|
} else if (n != 3) {
|
|
more = args->lengths[0] * 3;
|
|
} else
|
|
more = 0;
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen, more);
|
|
} // end of json_get_item_init
|
|
|
|
char *json_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *)
|
|
{
|
|
char *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: %s\n", n, g->Message);
|
|
|
|
PUSH_WARNING(g->Message);
|
|
str = NULL;
|
|
} catch (const char *msg) {
|
|
strcpy(g->Message, msg);
|
|
PUSH_WARNING(g->Message);
|
|
str = NULL;
|
|
} // end catch
|
|
|
|
err:
|
|
if (!str) {
|
|
*is_null = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of jsonget_string
|
|
|
|
void jsonget_string_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jsonget_string_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Get an integer value from a Json item. */
|
|
/*********************************************************************************/
|
|
my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, more;
|
|
|
|
if (args->arg_count != 2) {
|
|
strcpy(message, "This function must have 2 arguments");
|
|
return true;
|
|
} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (args->arg_type[1] != STRING_RESULT) {
|
|
strcpy(message, "Second argument is not a (jpath) string");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
// TODO: calculate this
|
|
more = (IsJson(args, 0) != 3) ? 1000 : 0;
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen, more);
|
|
} // end of jsonget_int_init
|
|
|
|
long long jsonget_int(UDF_INIT *initid, UDF_ARGS *args,
|
|
char *is_null, char *error)
|
|
{
|
|
char *p, *path;
|
|
long long n;
|
|
PJSON jsp;
|
|
PJSNX jsx;
|
|
PJVAL jvp;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (g->N) {
|
|
if (!g->Activityp) {
|
|
*is_null = 1;
|
|
return 0LL;
|
|
} else
|
|
return *(long long*)g->Activityp;
|
|
|
|
} else if (initid->const_item)
|
|
g->N = 1;
|
|
|
|
if (!g->Xchk) {
|
|
if (CheckMemory(g, initid, args, 1, true)) {
|
|
PUSH_WARNING("CheckMemory error");
|
|
if (g->Mrr) *error = 1;
|
|
*is_null = 1;
|
|
return 0LL;
|
|
} else
|
|
jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
PUSH_WARNING(g->Message);
|
|
if (g->Mrr) *error = 1;
|
|
*is_null = 1;
|
|
return 0;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = jvp->GetJson();
|
|
|
|
if (g->Mrr) { // First argument is a constant
|
|
g->Xchk = jsp;
|
|
JsonMemSave(g);
|
|
} // endif Mrr
|
|
|
|
} else
|
|
jsp = (PJSON)g->Xchk;
|
|
|
|
path = MakePSZ(g, args, 1);
|
|
jsx = 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: %s\n", n, g->Message);
|
|
|
|
PUSH_WARNING(g->Message);
|
|
*error = 1;
|
|
path = NULL;
|
|
} catch (const char *msg) {
|
|
strcpy(g->Message, msg);
|
|
PUSH_WARNING(g->Message);
|
|
*error = 1;
|
|
path = NULL;
|
|
} // end catch
|
|
|
|
err:
|
|
if (!path) {
|
|
*res_length = 0;
|
|
*is_null = 1;
|
|
} else
|
|
*res_length = strlen(path);
|
|
|
|
return path;
|
|
} // end of jsonlocate
|
|
|
|
void jsonlocate_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jsonlocate_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Locate all occurences of a value in a Json tree. */
|
|
/*********************************************************************************/
|
|
my_bool json_locate_all_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, more = 1000;
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "At least 2 arguments required");
|
|
return true;
|
|
} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
|
|
strcpy(message, "Third argument is not an integer (Depth)");
|
|
return true;
|
|
} else if (args->arg_count > 3)
|
|
if (args->arg_type[3] != INT_RESULT) {
|
|
strcpy(message, "Fourth argument is not an integer (memory)");
|
|
return true;
|
|
} else
|
|
more += (ulong)*(longlong*)args->args[2];
|
|
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
// TODO: calculate this
|
|
if (IsJson(args, 0) == 3)
|
|
more = 0;
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen, more);
|
|
} // end of json_locate_all_init
|
|
|
|
char *json_locate_all(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *p, *path = NULL;
|
|
int mx = 10;
|
|
PJVAL jvp, jvp2;
|
|
PJSON jsp;
|
|
PJSNX jsx;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (g->N) {
|
|
if (g->Activityp) {
|
|
path = (char*)g->Activityp;
|
|
*res_length = strlen(path);
|
|
return path;
|
|
} else {
|
|
*error = 1;
|
|
*res_length = 0;
|
|
*is_null = 1;
|
|
return NULL;
|
|
} // endif Activityp
|
|
|
|
} else if (initid->const_item)
|
|
g->N = 1;
|
|
|
|
try {
|
|
if (!g->Xchk) {
|
|
if (CheckMemory(g, initid, args, 1, true)) {
|
|
PUSH_WARNING("CheckMemory error");
|
|
*error = 1;
|
|
goto err;
|
|
} else
|
|
jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
PUSH_WARNING(g->Message);
|
|
goto err;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = jvp->GetJson();
|
|
|
|
if (g->Mrr) { // First argument is a constant
|
|
g->Xchk = jsp;
|
|
JsonMemSave(g);
|
|
} // endif Mrr
|
|
|
|
} else
|
|
jsp = (PJSON)g->Xchk;
|
|
|
|
// The item to locate
|
|
jvp2 = MakeValue(g, args, 1);
|
|
|
|
if (args->arg_count > 2)
|
|
mx = (int)*(long long*)args->args[2];
|
|
|
|
jsx = new(g) JSNX(g, jsp, TYPE_STRING);
|
|
path = jsx->LocateAll(g, jsp, jvp2, mx);
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Activityp = (PACTIVITY)path;
|
|
|
|
} catch (int n) {
|
|
if (trace(1))
|
|
htrc("Exception %d: %s\n", n, g->Message);
|
|
|
|
PUSH_WARNING(g->Message);
|
|
*error = 1;
|
|
path = NULL;
|
|
} catch (const char *msg) {
|
|
strcpy(g->Message, msg);
|
|
PUSH_WARNING(g->Message);
|
|
*error = 1;
|
|
path = NULL;
|
|
} // end catch
|
|
|
|
err:
|
|
if (!path) {
|
|
*res_length = 0;
|
|
*is_null = 1;
|
|
} else
|
|
*res_length = strlen(path);
|
|
|
|
return path;
|
|
} // end of json_locate_all
|
|
|
|
void json_locate_all_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_locate_all_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Check whether the document contains a value or item. */
|
|
/*********************************************************************************/
|
|
my_bool jsoncontains_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, more = 1024;
|
|
int n = IsJson(args, 0);
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "At least 2 arguments required");
|
|
return true;
|
|
} else if (!n && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
|
|
strcpy(message, "Third argument is not an integer (index)");
|
|
return true;
|
|
} else if (args->arg_count > 3) {
|
|
if (args->arg_type[3] == INT_RESULT && args->args[3])
|
|
more += (unsigned long)*(long long*)args->args[3];
|
|
else
|
|
strcpy(message, "Fourth argument is not an integer (memory)");
|
|
|
|
} // endif's
|
|
|
|
CalcLen(args, false, reslen, memlen);
|
|
//memlen += more;
|
|
|
|
// TODO: calculate this
|
|
more += (IsJson(args, 0) != 3 ? 1000 : 0);
|
|
|
|
return JsonInit(initid, args, message, false, reslen, memlen, more);
|
|
} // end of jsoncontains_init
|
|
|
|
long long jsoncontains(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *p __attribute__((unused)), res[256];
|
|
long long n;
|
|
unsigned long reslen;
|
|
|
|
*is_null = 0;
|
|
p = jsonlocate(initid, args, res, &reslen, is_null, error);
|
|
n = (*is_null) ? 0LL : 1LL;
|
|
return n;
|
|
} // end of jsoncontains
|
|
|
|
void jsoncontains_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jsoncontains_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Check whether the document contains a path. */
|
|
/*********************************************************************************/
|
|
my_bool jsoncontains_path_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, more = 1024;
|
|
int n = IsJson(args, 0);
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "At least 2 arguments required");
|
|
return true;
|
|
} else if (!n && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (args->arg_type[1] != STRING_RESULT) {
|
|
strcpy(message, "Second argument is not a string (path)");
|
|
return true;
|
|
} else if (args->arg_count > 2) {
|
|
if (args->arg_type[2] == INT_RESULT && args->args[2])
|
|
more += (unsigned long)*(long long*)args->args[2];
|
|
else
|
|
strcpy(message, "Third argument is not an integer (memory)");
|
|
|
|
} // endif's
|
|
|
|
CalcLen(args, false, reslen, memlen);
|
|
//memlen += more;
|
|
|
|
// TODO: calculate this
|
|
more += (IsJson(args, 0) != 3 ? 1000 : 0);
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen, more);
|
|
} // end of jsoncontains_path_init
|
|
|
|
long long jsoncontains_path(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *p, *path;
|
|
long long n;
|
|
PJSON jsp;
|
|
PJSNX jsx;
|
|
PJVAL jvp;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (g->N) {
|
|
if (!g->Activityp) {
|
|
*is_null = 1;
|
|
return 0LL;
|
|
} else
|
|
return *(long long*)g->Activityp;
|
|
|
|
} else if (initid->const_item)
|
|
g->N = 1;
|
|
|
|
if (!g->Xchk) {
|
|
if (CheckMemory(g, initid, args, 1, true)) {
|
|
PUSH_WARNING("CheckMemory error");
|
|
goto err;
|
|
} else
|
|
jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
PUSH_WARNING(g->Message);
|
|
goto err;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = jvp->GetJson();
|
|
|
|
if (g->Mrr) { // First argument is a constant
|
|
g->Xchk = jsp;
|
|
JsonMemSave(g);
|
|
} // endif Mrr
|
|
|
|
} else
|
|
jsp = (PJSON)g->Xchk;
|
|
|
|
path = MakePSZ(g, args, 1);
|
|
jsx = 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;
|
|
*is_null = 1;
|
|
return 0LL;
|
|
} // end of jsoncontains_path
|
|
|
|
void jsoncontains_path_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jsoncontains_path_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* This function is used by the json_set/insert/update_item functions. */
|
|
/*********************************************************************************/
|
|
char *handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *p, *path, *str = NULL;
|
|
int w;
|
|
my_bool b = true;
|
|
PJSON jsp;
|
|
PJSNX jsx;
|
|
PJVAL jvp;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PGLOBAL gb = GetMemPtr(g, args, 0);
|
|
|
|
if (g->Alchecked) {
|
|
str = (char*)g->Activityp;
|
|
goto fin;
|
|
} else if (g->N)
|
|
g->Alchecked = 1;
|
|
|
|
if (!strcmp(result, "$set"))
|
|
w = 0;
|
|
else if (!strcmp(result, "$insert"))
|
|
w = 1;
|
|
else if (!strcmp(result, "$update"))
|
|
w = 2;
|
|
else {
|
|
PUSH_WARNING("Logical error, please contact CONNECT developer");
|
|
goto fin;
|
|
} // endelse
|
|
|
|
try {
|
|
if (!g->Xchk) {
|
|
if (CheckMemory(g, initid, args, 1, true, false, true)) {
|
|
PUSH_WARNING("CheckMemory error");
|
|
throw 1;
|
|
} else
|
|
jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
throw 2;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = jvp->GetJson();
|
|
|
|
if (g->Mrr) { // First argument is a constant
|
|
g->Xchk = jsp;
|
|
JsonMemSave(g);
|
|
} // endif Mrr
|
|
|
|
} else
|
|
jsp = (PJSON)g->Xchk;
|
|
|
|
jsx = new(g)JSNX(g, jsp, TYPE_STRING, initid->max_length, 0, true);
|
|
|
|
for (uint i = 1; i + 1 < args->arg_count; i += 2) {
|
|
jvp = MakeValue(gb, args, i);
|
|
path = MakePSZ(g, args, i + 1);
|
|
|
|
if (jsx->SetJpath(g, path, false)) {
|
|
PUSH_WARNING(g->Message);
|
|
continue;
|
|
} // endif SetJpath
|
|
|
|
if (w) {
|
|
jsx->ReadValue(g);
|
|
b = jsx->GetValue()->IsNull();
|
|
b = (w == 1) ? b : !b;
|
|
} // endif w
|
|
|
|
if (b && jsx->WriteValue(gb, jvp))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
} // endfor i
|
|
|
|
// In case of error or file, return unchanged argument
|
|
if (!(str = MakeResult(g, args, jsp, INT_MAX32)))
|
|
str = MakePSZ(g, args, 0);
|
|
|
|
if (g->N)
|
|
// Keep result of constant function
|
|
g->Activityp = (PACTIVITY)str;
|
|
|
|
} catch (int n) {
|
|
if (trace(1))
|
|
htrc("Exception %d: %s\n", n, g->Message);
|
|
|
|
PUSH_WARNING(g->Message);
|
|
str = NULL;
|
|
} catch (const char *msg) {
|
|
strcpy(g->Message, msg);
|
|
PUSH_WARNING(g->Message);
|
|
str = NULL;
|
|
} // end catch
|
|
|
|
fin:
|
|
if (!str) {
|
|
*is_null = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of handle_item
|
|
|
|
/*********************************************************************************/
|
|
/* Set Json items of a Json document according to path. */
|
|
/*********************************************************************************/
|
|
my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, more = 0;
|
|
int n = IsJson(args, 0);
|
|
|
|
if (!(args->arg_count % 2)) {
|
|
strcpy(message, "This function must have an odd number of arguments");
|
|
return true;
|
|
} else if (!n && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
if (n == 2 && args->args[0]) {
|
|
char fn[_MAX_PATH];
|
|
long fl;
|
|
|
|
memcpy(fn, args->args[0], args->lengths[0]);
|
|
fn[args->lengths[0]] = 0;
|
|
fl = GetFileLength(fn);
|
|
more += fl * 3;
|
|
} else if (n != 3)
|
|
more += args->lengths[0] * 3;
|
|
|
|
if (!JsonInit(initid, args, message, true, reslen, memlen, more)) {
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
// This is a constant function
|
|
g->N = (initid->const_item) ? 1 : 0;
|
|
|
|
// This is to avoid double execution when using prepared statements
|
|
if (IsJson(args, 0) > 1)
|
|
initid->const_item = 0;
|
|
|
|
g->Alchecked = 0;
|
|
return false;
|
|
} else
|
|
return true;
|
|
|
|
} // end of json_set_item_init
|
|
|
|
char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *p)
|
|
{
|
|
strcpy(result, "$set");
|
|
return handle_item(initid, args, result, res_length, is_null, p);
|
|
} // end of json_set_item
|
|
|
|
void json_set_item_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_set_item_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Insert Json items of a Json document according to path. */
|
|
/*********************************************************************************/
|
|
my_bool json_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
return json_set_item_init(initid, args, message);
|
|
} // end of json_insert_item_init
|
|
|
|
char *json_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *p)
|
|
{
|
|
strcpy(result, "$insert");
|
|
return handle_item(initid, args, result, res_length, is_null, p);
|
|
} // end of json_insert_item
|
|
|
|
void json_insert_item_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_insert_item_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Update Json items of a Json document according to path. */
|
|
/*********************************************************************************/
|
|
my_bool json_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
return json_set_item_init(initid, args, message);
|
|
} // end of json_update_item_init
|
|
|
|
char *json_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *p)
|
|
{
|
|
strcpy(result, "$update");
|
|
return handle_item(initid, args, result, res_length, is_null, p);
|
|
} // end of json_update_item
|
|
|
|
void json_update_item_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_update_item_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Returns a json file as a json string. */
|
|
/*********************************************************************************/
|
|
my_bool json_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, fl, more = 1024;
|
|
|
|
if (args->arg_count < 1 || args->arg_count > 4) {
|
|
strcpy(message, "This function only accepts 1 to 4 arguments");
|
|
return true;
|
|
} else if (args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a string (file name)");
|
|
return true;
|
|
} // endif's args[0]
|
|
|
|
for (unsigned int i = 1; i < args->arg_count; i++) {
|
|
if (!(args->arg_type[i] == INT_RESULT || args->arg_type[i] == STRING_RESULT)) {
|
|
sprintf(message, "Argument %d is not an integer or a string (pretty or path)", i);
|
|
return true;
|
|
} // endif arg_type
|
|
|
|
// Take care of eventual memory argument
|
|
if (args->arg_type[i] == INT_RESULT && args->args[i])
|
|
more += (ulong)*(longlong*)args->args[i];
|
|
|
|
} // endfor i
|
|
|
|
initid->maybe_null = 1;
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
if (args->args[0])
|
|
fl = GetFileLength(args->args[0]);
|
|
else
|
|
fl = 100; // What can be done here?
|
|
|
|
reslen += fl;
|
|
|
|
if (initid->const_item)
|
|
more += fl;
|
|
|
|
if (args->arg_count > 1)
|
|
more += fl * M;
|
|
|
|
memlen += more;
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of json_file_init
|
|
|
|
char *json_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *str, *fn;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (g->N) {
|
|
str = (char*)g->Xchk;
|
|
goto fin;
|
|
} else if (initid->const_item)
|
|
g->N = 1;
|
|
|
|
PlugSubSet(g->Sarea, g->Sarea_Size);
|
|
fn = MakePSZ(g, args, 0);
|
|
|
|
if (args->arg_count > 1) {
|
|
int len, pretty, pty = 3;
|
|
PJSON jsp;
|
|
PJVAL jvp = NULL;
|
|
|
|
pretty = (args->arg_type[1] == INT_RESULT) ? (int)*(longlong*)args->args[1]
|
|
: (args->arg_count > 2 && args->arg_type[2] == INT_RESULT)
|
|
? (int)*(longlong*)args->args[2] : 3;
|
|
|
|
/*******************************************************************************/
|
|
/* Parse the json file and allocate its tree structure. */
|
|
/*******************************************************************************/
|
|
if (!(jsp = ParseJsonFile(g, fn, &pty, len))) {
|
|
PUSH_WARNING(g->Message);
|
|
str = NULL;
|
|
goto fin;
|
|
} // endif jsp
|
|
|
|
if (pty == 3)
|
|
PUSH_WARNING("File pretty format cannot be determined");
|
|
else if (pretty != 3 && pty != pretty)
|
|
PUSH_WARNING("File pretty format doesn't match the specified pretty value");
|
|
else if (pretty == 3)
|
|
pretty = pty;
|
|
|
|
// Check whether a path was specified
|
|
if (CheckPath(g, args, jsp, jvp, 1)) {
|
|
PUSH_WARNING(g->Message);
|
|
str = NULL;
|
|
goto fin;
|
|
} else if (jvp)
|
|
jsp = jvp->GetJson();
|
|
|
|
if (!(str = Serialize(g, jsp, NULL, 0)))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
} else
|
|
if (!(str = GetJsonFile(g, fn)))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Xchk = str;
|
|
|
|
fin:
|
|
if (!str) {
|
|
*res_length = 0;
|
|
*is_null = 1;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of json_file
|
|
|
|
void json_file_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_file_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Make a json file from a json item. */
|
|
/*********************************************************************************/
|
|
my_bool jfile_make_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count < 1 || args->arg_count > 3) {
|
|
strcpy(message, "Wrong number of arguments");
|
|
return true;
|
|
} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} // endif
|
|
|
|
CalcLen(args, false, reslen, memlen);
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jfile_make_init
|
|
|
|
char *jfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *)
|
|
{
|
|
char *p, *str = NULL, *fn = NULL;
|
|
int n, pretty = 2;
|
|
PJSON jsp;
|
|
PJVAL jvp;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (g->N) {
|
|
str = (char*)g->Activityp;
|
|
goto fin;
|
|
} else if (initid->const_item)
|
|
g->N = 1;
|
|
|
|
if ((n = IsJson(args, 0)) == 3) {
|
|
// Get default file name and pretty
|
|
PBSON bsp = (PBSON)args->args[0];
|
|
|
|
fn = bsp->Filename;
|
|
pretty = bsp->Pretty;
|
|
} else if (n == 2)
|
|
fn = args->args[0];
|
|
|
|
if (!g->Xchk) {
|
|
if (CheckMemory(g, initid, args, 1, true)) {
|
|
PUSH_WARNING("CheckMemory error");
|
|
goto fin;
|
|
} else
|
|
jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!strchr("[{ \t\r\n", *p)) {
|
|
// Is this a file name?
|
|
if (!(p = GetJsonFile(g, p))) {
|
|
PUSH_WARNING(g->Message);
|
|
goto fin;
|
|
} else
|
|
fn = jvp->GetString(g);
|
|
|
|
} // endif p
|
|
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
PUSH_WARNING(g->Message);
|
|
goto fin;
|
|
} // endif jsp
|
|
|
|
jvp->SetValue(jsp);
|
|
} // endif p
|
|
|
|
if (g->Mrr) { // First argument is a constant
|
|
g->Xchk = jvp;
|
|
JsonMemSave(g);
|
|
} // endif Mrr
|
|
|
|
} else
|
|
jvp = (PJVAL)g->Xchk;
|
|
|
|
for (uint i = 1; i < args->arg_count; i++)
|
|
switch (args->arg_type[i]) {
|
|
case STRING_RESULT:
|
|
fn = MakePSZ(g, args, i);
|
|
break;
|
|
case INT_RESULT:
|
|
pretty = (int)*(longlong*)args->args[i];
|
|
break;
|
|
default:
|
|
PUSH_WARNING("Unexpected argument type in jfile_make");
|
|
} // endswitch arg_type
|
|
|
|
if (fn) {
|
|
if (!Serialize(g, jvp->GetJson(), fn, pretty))
|
|
PUSH_WARNING(g->Message);
|
|
} else
|
|
PUSH_WARNING("Missing file name");
|
|
|
|
str= fn;
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Activityp = (PACTIVITY)str;
|
|
|
|
fin:
|
|
if (!str) {
|
|
*res_length = 0;
|
|
*is_null = 1;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of jfile_make
|
|
|
|
void jfile_make_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jfile_make_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Make and return a binary Json array containing all the parameters. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_array_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
CalcLen(args, false, reslen, memlen);
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jbin_array_init
|
|
|
|
char *jbin_array(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (!bsp || bsp->Changed) {
|
|
if (!CheckMemory(g, initid, args, args->arg_count, false)) {
|
|
PJAR arp;
|
|
|
|
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->AddValue(g, MakeValue(g, args, i));
|
|
|
|
arp->InitArray(g);
|
|
} // endif arp && bsp
|
|
|
|
} else
|
|
bsp = NULL;
|
|
|
|
if (!bsp && (bsp = JbinAlloc(g, args, initid->max_length, NULL)))
|
|
strncpy(bsp->Msg, g->Message, BMX);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? bsp : NULL;
|
|
} // endif bsp
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_array
|
|
|
|
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->AddValue(gb, jvp);
|
|
top = arp;
|
|
} // endif arp
|
|
|
|
} else
|
|
arp = jvp->GetArray();
|
|
|
|
for (uint i = 1; i < args->arg_count; i++)
|
|
arp->AddValue(gb, MakeValue(gb, args, i));
|
|
|
|
arp->InitArray(gb);
|
|
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, top))) {
|
|
strcat(bsp->Msg, " array");
|
|
bsp->Jsp = arp;
|
|
} // endif bsp
|
|
|
|
} else
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
|
|
strncpy(bsp->Msg, g->Message, BMX);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? bsp : NULL;
|
|
} // endif bsp
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_array_add_values
|
|
|
|
void jbin_array_add_values_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_array_add_values_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Add one value to a Json array. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "This function must have at least 2 arguments");
|
|
return true;
|
|
//} else if (!IsJson(args, 0)) {
|
|
// strcpy(message, "First argument must be a json item");
|
|
// return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen, true);
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jbin_array_add_init
|
|
|
|
char *jbin_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
int n = 2;
|
|
PJSON top = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (bsp && !bsp->Changed) {
|
|
// This constant function was recalled
|
|
*res_length = sizeof(BSON);
|
|
return (char*)bsp;
|
|
} // endif bsp
|
|
|
|
if (!CheckMemory(g, initid, args, 2, false, false, true)) {
|
|
int *x = NULL;
|
|
uint n = 2;
|
|
// PJSON jsp;
|
|
PJVAL jvp;
|
|
PJAR arp;
|
|
|
|
jvp = 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->AddValue(gb, (PJVAL)JvalNew(gb, TYPE_JVAL, jvp));
|
|
jvp->SetValue(arp);
|
|
|
|
if (!top)
|
|
top = arp;
|
|
|
|
} // endif arp
|
|
|
|
} else
|
|
arp = jvp->GetArray();
|
|
|
|
arp->AddValue(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->SetValue(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)))
|
|
strncpy(bsp->Msg, g->Message, BMX);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? bsp : NULL;
|
|
} // endif bsp
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_object
|
|
|
|
void jbin_object_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_object_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Make a Json Object containing all not null parameters. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_object_nonull_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
CalcLen(args, true, reslen, memlen);
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jbin_object_nonull_init
|
|
|
|
char *jbin_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (!bsp || bsp->Changed) {
|
|
if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
|
|
PJVAL jvp;
|
|
PJOB objp;
|
|
|
|
if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
|
|
for (uint i = 0; i < args->arg_count; i++)
|
|
if (!(jvp = MakeValue(g, args, i))->IsNull())
|
|
objp->SetValue(g, jvp, MakeKey(g, args, i));
|
|
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
|
|
strcat(bsp->Msg, " object");
|
|
|
|
} else
|
|
bsp = NULL;
|
|
|
|
} else
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
|
|
strncpy(bsp->Msg, g->Message, BMX);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? bsp : NULL;
|
|
} // endif bsp
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_object_nonull
|
|
|
|
void jbin_object_nonull_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_object_nonull_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Make a Json Object containing all the key/value parameters. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_object_key_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count % 2) {
|
|
strcpy(message, "This function must have an even number of arguments");
|
|
return true;
|
|
} // endif arg_count
|
|
|
|
CalcLen(args, true, reslen, memlen);
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jbin_object_key_init
|
|
|
|
char *jbin_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (!bsp || bsp->Changed) {
|
|
if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
|
|
PJOB objp;
|
|
|
|
if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
|
|
for (uint i = 0; i < args->arg_count; i += 2)
|
|
objp->SetValue(g, MakeValue(g, args, i + 1), MakePSZ(g, args, i));
|
|
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
|
|
strcat(bsp->Msg, " object");
|
|
|
|
} else
|
|
bsp = NULL;
|
|
|
|
} else
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
|
|
strncpy(bsp->Msg, g->Message, BMX);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? bsp : NULL;
|
|
} // endif bsp
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_object_key
|
|
|
|
void jbin_object_key_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_object_key_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Add or replace a value in a Json Object. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "This function must have at least 2 arguments");
|
|
return true;
|
|
} else if (!IsJson(args, 0)) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else
|
|
CalcLen(args, true, reslen, memlen, true);
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jbin_object_add_init
|
|
|
|
char *jbin_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
PJSON top = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (bsp && !bsp->Changed) {
|
|
// This constant function was recalled
|
|
bsp = (PBSON)g->Xchk;
|
|
*res_length = sizeof(BSON);
|
|
return (char*)bsp;
|
|
} // endif bsp
|
|
|
|
if (!CheckMemory(g, initid, args, 2, false, true, true)) {
|
|
PCSZ key;
|
|
PJOB jobp;
|
|
PJVAL jvp = MakeValue(g, args, 0, &top);
|
|
PJSON jsp = jvp->GetJson();
|
|
|
|
if (CheckPath(g, args, jsp, jvp, 2))
|
|
PUSH_WARNING(g->Message);
|
|
else if (jvp && jvp->GetValType() == TYPE_JOB) {
|
|
PGLOBAL gb = GetMemPtr(g, args, 0);
|
|
|
|
jobp = jvp->GetObject();
|
|
jvp = MakeValue(gb, args, 1);
|
|
key = MakeKey(gb, args, 1);
|
|
jobp->SetValue(gb, jvp, key);
|
|
} else {
|
|
PUSH_WARNING("First argument target is not an object");
|
|
// if (g->Mrr) *error = 1; (only if no path)
|
|
} // endif jvp
|
|
|
|
} // endif CheckMemory
|
|
|
|
// In case of error unchanged argument will be returned
|
|
bsp = MakeBinResult(g, args, top, initid->max_length);
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Xchk = bsp;
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_object_add
|
|
|
|
void jbin_object_add_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_object_add_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Delete a value from a Json object. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "This function must have 2 or 3 arguments");
|
|
return true;
|
|
} else if (!IsJson(args, 0)) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (args->arg_type[1] != STRING_RESULT) {
|
|
strcpy(message, "Second argument must be a key string");
|
|
return true;
|
|
} else
|
|
CalcLen(args, true, reslen, memlen, true);
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jbin_object_delete_init
|
|
|
|
char *jbin_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
PJSON top = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (bsp && !bsp->Changed) {
|
|
// This constant function was recalled
|
|
bsp = (PBSON)g->Xchk;
|
|
*res_length = sizeof(BSON);
|
|
return (char*)bsp;
|
|
} // endif bsp
|
|
|
|
if (!CheckMemory(g, initid, args, 1, false, true, true)) {
|
|
PCSZ key;
|
|
PJOB jobp;
|
|
PJVAL jvp = MakeValue(g, args, 0, &top);
|
|
PJSON jsp = jvp->GetJson();
|
|
|
|
if (CheckPath(g, args, top, jvp, 2))
|
|
PUSH_WARNING(g->Message);
|
|
else if (jvp && jvp->GetValType() == TYPE_JOB) {
|
|
key = MakeKey(g, args, 1);
|
|
jobp = jvp->GetObject();
|
|
jobp->DeleteKey(key);
|
|
} else {
|
|
PUSH_WARNING("First argument target is not an object");
|
|
// if (g->Mrr) *error = 1; (only if no path)
|
|
} // endif jvp
|
|
|
|
} // endif CheckMemory
|
|
|
|
// In case of error unchanged argument will be returned
|
|
bsp = MakeBinResult(g, args, top, initid->max_length);
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Xchk = bsp;
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_object_delete
|
|
|
|
void jbin_object_delete_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_object_delete_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Returns an array of the Json object keys. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
return json_object_list_init(initid, args, message);
|
|
} // end of jbin_object_list_init
|
|
|
|
char *jbin_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
PJAR jarp = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (!bsp || bsp->Changed) {
|
|
if (!CheckMemory(g, initid, args, 1, true, true)) {
|
|
char *p;
|
|
PJSON jsp;
|
|
PJVAL jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
PUSH_WARNING(g->Message);
|
|
return NULL;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = jvp->GetJson();
|
|
|
|
if (jsp->GetType() == TYPE_JOB) {
|
|
jarp = ((PJOB)jsp)->GetKeyList(g);
|
|
} else {
|
|
PUSH_WARNING("First argument is not an object");
|
|
if (g->Mrr) *error = 1;
|
|
} // endif jsp type
|
|
|
|
} // endif CheckMemory
|
|
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, jarp)))
|
|
strcat(bsp->Msg, " array");
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? bsp : NULL;
|
|
} // endif bsp
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_object_list
|
|
|
|
void jbin_object_list_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_object_list_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Get a Json item from a Json document. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
return json_get_item_init(initid, args, message);
|
|
} // end of jbin_get_item_init
|
|
|
|
char *jbin_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *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_VAL, jvp->GetValue());
|
|
|
|
if ((bsp = JbinAlloc(g, args, initid->max_length, jsp)))
|
|
strcat(bsp->Msg, " item");
|
|
else
|
|
*error = 1;
|
|
|
|
} // endif jvp
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Activityp = (PACTIVITY)bsp;
|
|
|
|
fin:
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_get_item
|
|
|
|
void jbin_get_item_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_get_item_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Merge two arrays or objects. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count < 2) {
|
|
strcpy(message, "This function must have at least 2 arguments");
|
|
return true;
|
|
} else if (!IsJson(args, 0)) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else if (!IsJson(args, 1)) {
|
|
strcpy(message, "Second argument must be a json item");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen, true);
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen);
|
|
} // end of jbin_item_merge_init
|
|
|
|
char *jbin_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
PJSON top = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (bsp && !bsp->Changed) {
|
|
// This constant function was recalled
|
|
*res_length = sizeof(BSON);
|
|
return (char*)bsp;
|
|
} // endif bsp
|
|
|
|
if (!CheckMemory(g, initid, args, 2, false, false, true)) {
|
|
PJVAL jvp;
|
|
PJSON jsp[2] = {NULL, NULL};
|
|
PGLOBAL gb = GetMemPtr(g, args, 0);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
jvp = MakeValue(g, args, i);
|
|
if (!i) top = jvp->GetJson();
|
|
|
|
if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) {
|
|
sprintf(g->Message, "Argument %d is not an array or object", i);
|
|
PUSH_WARNING(g->Message);
|
|
} else
|
|
jsp[i] = jvp->GetJsp();
|
|
|
|
} // endfor i
|
|
|
|
if (jsp[0] && jsp[0]->Merge(gb, jsp[1]))
|
|
PUSH_WARNING(gb->Message);
|
|
|
|
} // endif CheckMemory
|
|
|
|
// In case of error unchanged first argument will be returned
|
|
bsp = MakeBinResult(g, args, top, initid->max_length);
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Xchk = bsp;
|
|
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*error = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_item_merge
|
|
|
|
void jbin_item_merge_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_item_merge_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* This function is used by the jbin_set/insert/update functions. */
|
|
/*********************************************************************************/
|
|
char *bin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *p, *path;
|
|
int w;
|
|
my_bool b = true;
|
|
PJSON jsp;
|
|
PJSNX jsx;
|
|
PJVAL jvp = NULL;
|
|
PBSON bsp = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PGLOBAL gb = GetMemPtr(g, args, 0);
|
|
|
|
if (g->N) {
|
|
bsp = (PBSON)g->Activityp;
|
|
goto fin;
|
|
} else if (initid->const_item)
|
|
g->N = 1;
|
|
|
|
if (!strcmp(result, "$set"))
|
|
w = 0;
|
|
else if (!strcmp(result, "$insert"))
|
|
w = 1;
|
|
else if (!strcmp(result, "$update"))
|
|
w = 2;
|
|
else {
|
|
PUSH_WARNING("Logical error, please contact CONNECT developer");
|
|
goto fin;
|
|
} // endelse
|
|
|
|
if (!g->Xchk) {
|
|
if (CheckMemory(g, initid, args, 1, true, false, true)) {
|
|
PUSH_WARNING("CheckMemory error");
|
|
goto fin;
|
|
} else
|
|
jvp = MakeValue(g, args, 0);
|
|
|
|
if ((p = jvp->GetString(g))) {
|
|
if (!(jsp = ParseJson(g, p, strlen(p)))) {
|
|
PUSH_WARNING(g->Message);
|
|
goto fin;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = jvp->GetJson();
|
|
|
|
if (g->Mrr) { // First argument is a constant
|
|
g->Xchk = jsp;
|
|
JsonMemSave(g);
|
|
} // endif Mrr
|
|
|
|
} else
|
|
jsp = (PJSON)g->Xchk;
|
|
|
|
jsx = new(g)JSNX(g, jsp, TYPE_STRING, initid->max_length, 0, true);
|
|
|
|
for (uint i = 1; i+1 < args->arg_count; i += 2) {
|
|
jvp = MakeValue(gb, args, i);
|
|
path = MakePSZ(g, args, i+1);
|
|
|
|
if (jsx->SetJpath(g, path, false)) {
|
|
PUSH_WARNING(g->Message);
|
|
continue;
|
|
} // endif SetJpath
|
|
|
|
if (w) {
|
|
jsx->ReadValue(g);
|
|
b = jsx->GetValue()->IsNull();
|
|
b = (w == 1) ? b : !b;
|
|
} // endif w
|
|
|
|
if (b && jsx->WriteValue(gb, jvp))
|
|
PUSH_WARNING(g->Message);
|
|
|
|
} // endfor i
|
|
|
|
if (!(bsp = MakeBinResult(g, args, jsp, initid->max_length, INT_MAX32)))
|
|
*error = 1;
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Activityp = (PACTIVITY)bsp;
|
|
|
|
fin:
|
|
if (!bsp) {
|
|
*is_null = 1;
|
|
*res_length = 0;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of bin_handle_item
|
|
|
|
/*********************************************************************************/
|
|
/* Set Json items of a Json document according to path. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, more = 0;
|
|
int n = IsJson(args, 0);
|
|
|
|
if (!(args->arg_count % 2)) {
|
|
strcpy(message, "This function must have an odd number of arguments");
|
|
return true;
|
|
} else if (!n && args->arg_type[0] != STRING_RESULT) {
|
|
strcpy(message, "First argument must be a json item");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
if (n == 2 && args->args[0]) {
|
|
char fn[_MAX_PATH];
|
|
long fl;
|
|
|
|
memcpy(fn, args->args[0], args->lengths[0]);
|
|
fn[args->lengths[0]] = 0;
|
|
fl = GetFileLength(fn);
|
|
more = fl * 3;
|
|
} else if (n != 3)
|
|
more = args->lengths[0] * 3;
|
|
|
|
return JsonInit(initid, args, message, true, reslen, memlen, more);
|
|
} // end of jbin_set_item_init
|
|
|
|
char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *p)
|
|
{
|
|
strcpy(result, "$set");
|
|
return bin_handle_item(initid, args, result, res_length, is_null, p);
|
|
} // end of jbin_set_item
|
|
|
|
void jbin_set_item_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_set_item_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Insert Json items of a Json document according to path. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
return json_set_item_init(initid, args, message);
|
|
} // end of jbin_insert_item_init
|
|
|
|
char *jbin_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *p)
|
|
{
|
|
strcpy(result, "$insert");
|
|
return bin_handle_item(initid, args, result, res_length, is_null, p);
|
|
} // end of jbin_insert_item
|
|
|
|
void jbin_insert_item_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_insert_item_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Update Json items of a Json document according to path. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
return json_set_item_init(initid, args, message);
|
|
} // end of jbin_update_item_init
|
|
|
|
char *jbin_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *p)
|
|
{
|
|
strcpy(result, "$update");
|
|
return bin_handle_item(initid, args, result, res_length, is_null, p);
|
|
} // end of jbin_update_item
|
|
|
|
void jbin_update_item_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_update_item_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Returns a json file as a json item. */
|
|
/*********************************************************************************/
|
|
my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen, fl, more = 1024;
|
|
|
|
if (args->arg_count < 1 || args->arg_count > 4) {
|
|
strcpy(message, "This function only accepts 1 to 4 arguments");
|
|
return true;
|
|
} else if (args->arg_type[0] != STRING_RESULT || !args->args[0]) {
|
|
strcpy(message, "First argument must be a constant string (file name)");
|
|
return true;
|
|
} else if (args->arg_count > 1 && args->arg_type[1] != STRING_RESULT) {
|
|
strcpy(message, "Second argument is not a string (path)");
|
|
return true;
|
|
} else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
|
|
strcpy(message, "Third argument is not an integer (pretty)");
|
|
return true;
|
|
} else if (args->arg_count > 3) {
|
|
if (args->arg_type[3] != INT_RESULT) {
|
|
strcpy(message, "Fourth argument is not an integer (memory)");
|
|
return true;
|
|
} else
|
|
more += (ulong)*(longlong*)args->args[3];
|
|
|
|
} // endifs
|
|
|
|
initid->maybe_null = 1;
|
|
CalcLen(args, false, reslen, memlen);
|
|
fl = GetFileLength(args->args[0]);
|
|
reslen += fl;
|
|
more += fl * M;
|
|
//memlen += more;
|
|
return JsonInit(initid, args, message, true, reslen, memlen, more);
|
|
} // end of jbin_file_init
|
|
|
|
char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *error)
|
|
{
|
|
char *fn;
|
|
int pretty, len = 0, pty = 3;
|
|
PJSON jsp;
|
|
PJVAL jvp = NULL;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
PBSON bsp = (PBSON)g->Xchk;
|
|
|
|
if (bsp && !bsp->Changed)
|
|
goto fin;
|
|
|
|
PlugSubSet(g->Sarea, g->Sarea_Size);
|
|
g->Xchk = NULL;
|
|
fn = MakePSZ(g, args, 0);
|
|
pretty = (args->arg_count > 2 && args->args[2]) ? (int)*(longlong*)args->args[2] : 3;
|
|
|
|
/*********************************************************************************/
|
|
/* Parse the json file and allocate its tree structure. */
|
|
/*********************************************************************************/
|
|
if (!(jsp = ParseJsonFile(g, fn, &pty, len))) {
|
|
PUSH_WARNING(g->Message);
|
|
*error = 1;
|
|
goto fin;
|
|
} // endif jsp
|
|
|
|
if (pty == 3)
|
|
PUSH_WARNING("File pretty format cannot be determined");
|
|
else if (pretty != 3 && pty != pretty)
|
|
PUSH_WARNING("File pretty format doesn't match the specified pretty value");
|
|
else if (pretty == 3)
|
|
pretty = pty;
|
|
|
|
if ((bsp = JbinAlloc(g, args, len, jsp))) {
|
|
strcat(bsp->Msg, " file");
|
|
bsp->Filename = fn;
|
|
bsp->Pretty = pretty;
|
|
} else {
|
|
*error = 1;
|
|
goto fin;
|
|
} // endif bsp
|
|
|
|
// Check whether a path was specified
|
|
if (CheckPath(g, args, jsp, jvp, 1)) {
|
|
PUSH_WARNING(g->Message);
|
|
bsp = NULL;
|
|
goto fin;
|
|
} else if (jvp)
|
|
bsp->Jsp = jvp->GetJsp();
|
|
|
|
if (initid->const_item)
|
|
// Keep result of constant function
|
|
g->Xchk = bsp;
|
|
|
|
fin:
|
|
if (!bsp) {
|
|
*res_length = 0;
|
|
*is_null = 1;
|
|
} else
|
|
*res_length = sizeof(BSON);
|
|
|
|
return (char*)bsp;
|
|
} // end of jbin_file
|
|
|
|
void jbin_file_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of jbin_file_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Serialize a Json document. . */
|
|
/*********************************************************************************/
|
|
my_bool json_serialize_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
unsigned long reslen, memlen;
|
|
|
|
if (args->arg_count != 1) {
|
|
strcpy(message, "This function must have 1 argument");
|
|
return true;
|
|
} else if (args->args[0] && IsJson(args, 0) != 3) {
|
|
strcpy(message, "Argument must be a Jbin tree");
|
|
return true;
|
|
} else
|
|
CalcLen(args, false, reslen, memlen);
|
|
|
|
return JsonInit(initid, args, message, false, reslen, memlen);
|
|
} // end of json_serialize_init
|
|
|
|
char *json_serialize(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *, char *error)
|
|
{
|
|
char *str;
|
|
PGLOBAL g = (PGLOBAL)initid->ptr;
|
|
|
|
if (!g->Xchk) {
|
|
if (IsJson(args, 0) == 3) {
|
|
PBSON bsp = (PBSON)args->args[0];
|
|
|
|
JsonSubSet(g);
|
|
|
|
if (!(str = Serialize(g, bsp->Jsp, NULL, 0)))
|
|
str = strcpy(result, g->Message);
|
|
|
|
// Keep result of constant function
|
|
g->Xchk = (initid->const_item) ? str : NULL;
|
|
} else {
|
|
*error = 1;
|
|
str = strcpy(result, "Argument is not a Jbin tree");
|
|
} // endif
|
|
|
|
} else
|
|
str = (char*)g->Xchk;
|
|
|
|
*res_length = strlen(str);
|
|
return str;
|
|
} // end of json_serialize
|
|
|
|
void json_serialize_deinit(UDF_INIT* initid)
|
|
{
|
|
JsonFreeMem((PGLOBAL)initid->ptr);
|
|
} // end of json_serialize_deinit
|
|
|
|
/*********************************************************************************/
|
|
/* Utility function returning an environment variable value. */
|
|
/*********************************************************************************/
|
|
my_bool envar_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
|
|
{
|
|
if (args->arg_count != 1) {
|
|
strcpy(message, "Unique argument must be an environment variable name");
|
|
return true;
|
|
} else {
|
|
initid->maybe_null = true;
|
|
return false;
|
|
} // endif count
|
|
|
|
} // end of envar_init
|
|
|
|
char *envar(UDF_INIT *initid, UDF_ARGS *args, char *result,
|
|
unsigned long *res_length, char *is_null, char *)
|
|
{
|
|
char *str, name[256];
|
|
int n = MY_MIN(args->lengths[0], sizeof(name) - 1);
|
|
|
|
memcpy(name, args->args[0], n);
|
|
name[n] = 0;
|
|
|
|
if (!(str = getenv(name))) {
|
|
*res_length = 0;
|
|
*is_null = 1;
|
|
} else
|
|
*res_length = strlen(str);
|
|
|
|
return str;
|
|
} // end of envar
|
|
|
|
#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 *result,
|
|
unsigned long *res_length, char *is_null, char *)
|
|
{
|
|
PSZ str1, str2;
|
|
char *s;
|
|
long long n = 0;
|
|
size_t lg;
|
|
|
|
lg = (size_t)args->lengths[0];
|
|
s = str1 = (PSZ)malloc(lg + 1);
|
|
memcpy(str1, args->args[0], lg);
|
|
str1[lg] = 0;
|
|
|
|
lg = (size_t)args->lengths[1];
|
|
str2 = (PSZ)malloc(lg + 1);
|
|
memcpy(str2, args->args[1], lg);
|
|
str2[lg] = 0;
|
|
|
|
while (s = strstr(s, str2)) {
|
|
n++;
|
|
s += lg;
|
|
} // endwhile
|
|
|
|
free(str1);
|
|
free(str2);
|
|
return n;
|
|
} // end of countin
|