Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/MariaDB/storage/connect/   (MariaDB Server Version 8.1-8.4©)  Datei vom 1.9.2026 mit Größe 175 kB image not shown  

Quelle  jsonudf.cpp   Sprache: C

 

/****************** jsonudf C++ Program Source Code File (.CPP) ******************/
/*  PROGRAM NAME: jsonudf     Version 1.8                                        */
/*  (C) Copyright to the author Olivier BERTRAND          2015-2019              */
/*  This program are the JSON User Defined Functions     .                       */
/*********************************************************************************/

/*********************************************************************************/
/*  Include relevant sections of the MariaDB header file.                        */
/*********************************************************************************/
#include <my_global.h>
#include <mysqld.h>
#include <mysqld_error.h>
#include <mysql.h>
#include <sql_error.h>
#include <m_string.h>

#include "jsonudf.h"
#include "filechk.h"

#if defined(UNIX) || defined(UNIV_LINUX)
#define _O_RDONLY O_RDONLY
#endif

#define MEMFIX  4096
#if defined(connect_EXPORTS)
#define PUSH_WARNING(M) push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, M)
#else
#define PUSH_WARNING(M) htrc(M)
#endif
#define M 9

static char *handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error);
static char *bin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error);
static PJSON JsonNew(PGLOBAL g, JTYP type);
static PJVAL JvalNew(PGLOBAL g, JTYP type, void *vp = NULL);
static PJSNX JsnxNew(PGLOBAL g, PJSON jsp, int type, int len = 64);
uint GetJsonGroupSize(void);
static void SetChanged(PBSON bsp);

uint JsonGrpSize = 10;

/*********************************************************************************/
/*  SubAlloc a new JSNX class with protection against memory exhaustion.         */
/*********************************************************************************/
static PJSNX JsnxNew(PGLOBAL g, PJSON jsp, int type, int len)
{
 PJSNX jsx;

 try {
  jsx = new(g) JSNX(g, jsp, type, len);
 } catch (...) {
  if (trace(1023))
   htrc("%s\n", g->Message);

  PUSH_WARNING(g->Message);
  jsx = NULL;
 } // end try/catch

 return jsx;
} /* end of JsnxNew */

/* ----------------------------------- JSNX ------------------------------------ */

/*********************************************************************************/
/*  JSNX public constructor.                                                     */
/*********************************************************************************/
JSNX::JSNX(PGLOBAL g, PJSON row, int type, int len, int prec, my_bool wr)
{
 Row = row;
 Jvalp = NULL;
 Jpnp = NULL;
 Jp = NULL;
 Nodes = NULL;
 Value = AllocateValue(g, type, len, prec);
 //MulVal = NULL;
 Jpath = NULL;
 Buf_Type = type;
 Long = len;
 Prec = prec;
 Nod = 0;
 Xnod = -1;
 K = 0;
 I = -1;
 Imax = 9;
 B = 0;
 Xpd = false;
 Parsed = false;
 Found = false;
 Wr = wr;
 Jb = false;
} // end of JSNX constructor

/*********************************************************************************/
/*  SetJpath: set and parse the json path.                                       */
/*********************************************************************************/
my_bool JSNX::SetJpath(PGLOBAL g, char *path, my_bool jb)
{
 // Check Value was allocated
 if (!Value)
 {
  snprintf(g->Message, sizeof(g->Message), MSG(NO_MEMORY));
  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)
{
 int     n = (int)strlen(p);
 my_bool dg = true, b = false;
 PJNODE  jnp = &Nodes[i];

 if (*p) {
  if (p[n - 1] == ']') {
   p[--n] = 0;
  } else if (!IsNum(p)) {
   // Wrong array specification
   snprintf(g->Message, sizeof(g->Message), "Invalid array specification %s", p);
   return true;
  } // endif p

 } else
  b = true;

 // To check whether a numeric Rank was specified
 dg = IsNum(p);

 if (!n) {
  // Default specifications
  if (jnp->Op != OP_EXP) {
   if (Wr) {
    // Force append
    jnp->Rank = INT_MAX32;
    jnp->Op = OP_LE;
   } else if (Jb) {
    // Return a Json item
    jnp->Op = OP_XX;
   } else if (b) {
    // Return 1st value (B is the index base)
    jnp->Rank = B;
    jnp->Op = OP_LE;
   } else if (!Value->IsTypeNum()) {
    jnp->CncVal = AllocateValue(g, PlugDup(g, ", "), TYPE_STRING);
    jnp->Op = OP_CNC;
   } else
    jnp->Op = OP_ADD;

  } // endif OP

 } else if (dg) {
  // Return nth value
  jnp->Rank = atoi(p) - B;
  jnp->Op = OP_EQ;
 } else if (Wr) {
  snprintf(g->Message, sizeof(g->Message), "Invalid specification %s in a write path", p);
  return true;
 } else if (n == 1) {
  // Set the Op value;
  switch (*p) {
  case '+': jnp->Op = OP_ADD;  break;
  case 'x': jnp->Op = OP_MULT; break;
  case '>': jnp->Op = OP_MAX;  break;
  case '<': jnp->Op = OP_MIN;  break;
  case '!': jnp->Op = OP_SEP;  break; // Average
  case '#': jnp->Op = OP_NUM;  break;
  case '*': // Expand this array
   snprintf(g->Message, sizeof(g->Message), "Expand not supported by this function");
   return true;
  default:
   snprintf(g->Message, sizeof(g->Message), "Invalid function specification %c", *p);
   return true;
  } // endswitch *p

 } else if (*p == '"' && p[n - 1] == '"') {
  // This is a concat specification
  jnp->Op = OP_CNC;

  if (n > 2) {
   // Set concat intermediate string
   p[n - 1] = 0;

   if (trace(1))
    htrc("Concat string=%s\n", p + 1);

   jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
  } // endif n

 } else {
  snprintf(g->Message, sizeof(g->Message), "Wrong array specification");
  return true;
 } // endif's

 return false;
} // end of SetArrayOptions

/*********************************************************************************/
/*  Parse the eventual passed Jpath information.                                 */
/*  This information can be specified in the Fieldfmt column option when         */
/*  creating the table. It permits to indicate the position of the node          */
/*  corresponding to that column.                                                */
/*********************************************************************************/
my_bool JSNX::ParseJpath(PGLOBAL g)
{
 char   *p, *p1 = NULL, *p2 = NULL, *pbuf = NULL;
 int     i;
 my_bool a;

 if (Parsed)
  return false;                       // Already done
 else if (!Jpath)
 {
  // Jpath = Name;
  snprintf(g->Message, sizeof(g->Message), MSG(ARG_IS_NULL));
  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))
    return true;

  } else if (*p == '*') {
   if (Wr) {
    snprintf(g->Message, sizeof(g->Message), "Invalid specification %c in a write path", *p);
    return true;
   } else        // Return JSON
    Nodes[i].Op = OP_XX;

  } else {
   Nodes[i].Key = p;
   Nodes[i].Op = OP_EXIST;
  } // endif's

 } // endfor i, p

 Nod = i;
 //MulVal = AllocateValue(g, Value);

 if (trace(1))
  for (i = 0; i < Nod; i++)
   htrc("Node(%d) Key=%s Op=%d Rank=%d\n",
    i, SVP(Nodes[i].Key), Nodes[i].Op, Nodes[i].Rank);

 Parsed = true;
 return false;
} // end of ParseJpath

/*********************************************************************************/
/*  SetValue: Set a value from a JVALUE contains.                                */
/*********************************************************************************/
void JSNX::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val)
{
 if (val) {
  vp->SetNull(false);

  if (Jb) {
   vp->SetValue_psz(Serialize(g, val->GetJsp(), NULL, 0));
   Jb = false;
  } else switch (val->GetValType()) {
   case TYPE_DTM:
   case TYPE_STRG:
    vp->SetValue_psz(val->GetString(g));
    break;
   case TYPE_INTG:
    vp->SetValue(val->GetInteger());
    break;
   case TYPE_BINT:
    vp->SetValue(val->GetBigint());
    break;
   case TYPE_DBL:
    if (vp->IsTypeNum())
     vp->SetValue(val->GetFloat());
    else // Get the proper number of decimals
     vp->SetValue_psz(val->GetString(g));

    break;
   case TYPE_BOOL:
    if (vp->IsTypeNum())
     vp->SetValue(val->GetInteger() ? 1 : 0);
    else
     vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));

    break;
   case TYPE_JAR:
    vp->SetValue_psz(val->GetArray()->GetText(g, NULL));
    break;
   case TYPE_JOB:
    vp->SetValue_psz(val->GetObject()->GetText(g, NULL));
    break;
   case TYPE_NULL:
    vp->SetNull(true);
                                /* falls through */
   default:
    vp->Reset();
   } // endswitch Type

 } else {
  vp->SetNull(true);
  vp->Reset();
 } // endif val

} // end of SetJsonValue

/*********************************************************************************/
/*  MakeJson: Serialize the json item and set value to it.                       */
/*********************************************************************************/
PJVAL JSNX::MakeJson(PGLOBAL g, PJSON jsp, int n)
{
 Jb = false;

 if (Value->IsTypeNum()) {
  snprintf(g->Message, sizeof(g->Message), "Cannot make Json for a numeric value");
  return NULL;
 } else if (jsp->GetType() != TYPE_JAR && jsp->GetType() != TYPE_JOB) {
  snprintf(g->Message, sizeof(g->Message), "Target is not an array or object");
  return NULL;
 } else  if (n < Nod -1) {
  if (jsp->GetType() == TYPE_JAR) {
   int    ars = jsp->GetSize(false);
   PJNODE jnp = &Nodes[n];
   PJAR jarp = new(g) JARRAY;

   jnp->Op = OP_EQ;

   for (jnp->Rank = 0; jnp->Rank < ars; jnp->Rank++)
    jarp->AddArrayValue(g, GetRowValue(g, jsp, n));

   jarp->InitArray(g);
   jnp->Op = OP_XX;
   jnp->Rank = 0;
   jsp = jarp;
  } else if(jsp->GetType() == TYPE_JOB) {
   PJSON jp;
   PJOB  jobp = new(g) JOBJECT;

   for (PJPR prp = ((PJOB)jsp)->GetFirst(); prp; prp = prp->Next) {
    jp = (prp->Val->DataType == TYPE_JSON) ? prp->Val->Jsp : prp->Val;
    jobp->SetKeyValue(g, GetRowValue(g, jp, n + 1), prp->Key);
   } // endfor prp

   jsp = jobp;
  } // endif Type

 } // endif

 Jb = true;
 return new(g) JVALUE(jsp);
} // end of MakeJson

/*********************************************************************************/
/*  GetJson:                                                                     */
/*********************************************************************************/
PJVAL JSNX::GetJson(PGLOBAL g)
{
 return GetRowValue(g, Row, 0);
} // end of GetJson

/*********************************************************************************/
/*  ReadValue:                                                                   */
/*********************************************************************************/
void JSNX::ReadValue(PGLOBAL g)
{
 Value->SetValue_pval(GetColumnValue(g, Row, 0));
} // end of ReadValue

/*********************************************************************************/
/*  GetColumnValue:                                                              */
/*********************************************************************************/
PVAL JSNX::GetColumnValue(PGLOBAL g, PJSON row, int i)
{
 PJVAL val = NULL;

 val = GetRowValue(g, row, i);
 SetJsonValue(g, Value, val);
 return Value;
} // end of GetColumnValue

/*********************************************************************************/
/*  GetRowValue:                                                                 */
/*********************************************************************************/
PJVAL JSNX::GetRowValue(PGLOBAL g, PJSON row, int i, my_bool b)
{
 PJAR    arp;
 PJVAL   val = NULL;

 for (; i < Nod && row; i++) {
  if (Nodes[i].Op == OP_NUM) {
   Value->SetValue(row->GetType() == TYPE_JAR ? ((PJAR)row)->size() : 1);
   val = new(g) JVALUE(g, Value);
   return val;
  } else if (Nodes[i].Op == OP_XX) {
   return MakeJson(g, row, i);
  } else switch (row->GetType()) {
   case TYPE_JOB:
    if (!Nodes[i].Key) {
     // Expected Array was not there
     if (Nodes[i].Op == OP_LE) {
      if (i < Nod-1)
       continue;
      else
       val = new(g)JVALUE(row);

     } else {
      snprintf(g->Message, sizeof(g->Message), "Unexpected object");
      val = NULL;
     } //endif Op

    } else
     val = ((PJOB)row)->GetKeyValue(Nodes[i].Key);

    break;
   case TYPE_JAR:
    arp = (PJAR)row;

    if (!Nodes[i].Key) {
     if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
      val = arp->GetArrayValue(Nodes[i].Rank);
     else if (Nodes[i].Op == OP_EXP)
      return (PJVAL)ExpandArray(g, arp, i);
     else
      return new(g) JVALUE(g, CalculateArray(g, arp, i));

    } else {
     // Unexpected array, unwrap it as [0]
     val = arp->GetArrayValue(0);
     i--;
    } // endif's

    break;
   case TYPE_JVAL:
    val = (PJVAL)row;
    break;
   default:
    snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->GetType());
    val = NULL;
   } // endswitch Type

  if (i < Nod-1)
   if (!(row = (val) ? val->GetJsp() : NULL))
    val = NULL;
// row = (val) ? val->GetJson() : NULL;

 } // endfor i

 // SetJsonValue(g, Value, val);
 return val;
} // end of GetRowValue

/*********************************************************************************/
/*  ExpandArray:                                                                 */
/*********************************************************************************/
PVAL JSNX::ExpandArray(PGLOBAL g, PJAR arp, int n)
{
 snprintf(g->Message, sizeof(g->Message), "Expand cannot be done by this function");
 return NULL;
} // end of ExpandArray

/*********************************************************************************/
/*  Get the value used for calculating the array.                                */
/*********************************************************************************/
PVAL JSNX::GetCalcValue(PGLOBAL g, PJAR jap, int n)
{
 // For calculated arrays, a local Value must be used
 int     lng = 0;
 short   type= 0, prec= 0;
 bool    b = n < Nod - 1;
 PVAL    valp;
 PJVAL   vlp, vp;
 OPVAL   op = Nodes[n].Op;

 switch (op) {
  case OP_NUM:
   type = TYPE_INT;
   break;
  case OP_ADD:
  case OP_MULT:
   if (!IsTypeNum(Buf_Type)) {
    type = TYPE_INT;
    prec = 0;

    for (vlp = jap->GetArrayValue(0); vlp; vlp = vlp->Next) {
     vp = (b && vlp->GetJsp()) ? GetRowValue(g, vlp, n + 1) : vlp;

     switch (vp->DataType) {
      case TYPE_BINT:
       if (type == TYPE_INT)
        type = TYPE_BIGINT;

       break;
      case TYPE_DBL:
      case TYPE_FLOAT:
       type = TYPE_DOUBLE;
       prec = MY_MAX(prec, vp->Nd);
       break;
      default:
       break;
     } // endswitch Type

    } // endfor vlp

   } else {
    type = Buf_Type;
    prec = GetPrecision();
   } // endif Buf_Type

   break;
  case OP_SEP:
   if (IsTypeChar(Buf_Type)) {
    type = TYPE_DOUBLE;
    prec = 2;
   } else {
    type = Buf_Type;
    prec = GetPrecision();
   } // endif Buf_Type

   break;
  case OP_MIN:
  case OP_MAX:
   type = Buf_Type;
   lng = Long;
   prec = GetPrecision();
   break;
  case OP_CNC:
   type = TYPE_STRING;

   if (IsTypeChar(Buf_Type)) {
    lng = (Long) ? Long : 512;
    prec = GetPrecision();
   } else
    lng = 512;

   break;
  default:
   break;
 } // endswitch Op

 return valp = AllocateValue(g, type, lng, prec);
} // end of GetCalcValue

/*********************************************************************************/
/*  CalculateArray:                                                              */
/*********************************************************************************/
PVAL JSNX::CalculateArray(PGLOBAL g, PJAR arp, int n)
{
 int     i, ars = arp->size(), nv = 0;
 bool    err;
 OPVAL   op = Nodes[n].Op;
 PVAL    val[2], vp = GetCalcValue(g, arp, n);
 PVAL    mulval = AllocateValue(g, vp);
 PJVAL   jvrp, jvp;
 JVALUE  jval;

 vp->Reset();

 if (trace(1))
  htrc("CalculateArray size=%d op=%d\n", ars, op);

 for (i = 0; i < ars; i++) {
  jvrp = arp->GetArrayValue(i);

  if (trace(1))
   htrc("i=%d nv=%d\n", i, nv);

  if (!jvrp->IsNull() || (op == OP_CNC && GetJsonNull())) {
   if (jvrp->IsNull()) {
    jvrp->SetString(g, GetJsonNull(), 0);
    jvp = jvrp;
   } else if (n < Nod - 1 && jvrp->GetJson()) {
    jval.SetValue(g, GetColumnValue(g, jvrp->GetJson(), n + 1));
    jvp = &jval;
   } else
    jvp = jvrp;

   if (trace(1))
    htrc("jvp=%s null=%d\n",
     jvp->GetString(g), jvp->IsNull() ? 1 : 0);

   if (!nv++) {
    SetJsonValue(g, vp, jvp);
    continue;
   } else
    SetJsonValue(g, mulval, jvp);

   if (!mulval->IsNull()) {
    switch (op) {
     case OP_CNC:
      if (Nodes[n].CncVal) {
       val[0] = Nodes[n].CncVal;
       err = vp->Compute(g, val, 1, op);
      } // endif CncVal

      val[0] = mulval;
      err = vp->Compute(g, val, 1, op);
      break;
//        case OP_NUM:
     case OP_SEP:
      val[0] = vp;
      val[1] = mulval;
      err = vp->Compute(g, val, 2, OP_ADD);
      break;
     default:
      val[0] = vp;
      val[1] = mulval;
      err = vp->Compute(g, val, 2, op);
    } // endswitch Op

    if (err)
     vp->Reset();

    if (trace(1)) {
     char buf(32);

     htrc("vp='%s' err=%d\n",
      vp->GetCharString(&buf), err ? 1 : 0);
    } // endif trace

   } // endif Zero

  } // endif jvrp

 } // endfor i

 if (op == OP_SEP) {
  // Calculate average
  mulval->SetValue(nv);
  val[0] = vp;
  val[1] = mulval;

  if (vp->Compute(g, val, 2, OP_DIV))
   vp->Reset();

 } // endif Op

 return vp;
} // end of CalculateArray

/*********************************************************************************/
/* CheckPath: Checks whether the path exists in the document.                    */
/*********************************************************************************/
my_bool JSNX::CheckPath(PGLOBAL g)
{
 PJVAL   val= NULL;
 PJSON   row = Row;

 for (int i = 0; i < Nod && row; i++) {
  val = NULL;

  if (Nodes[i].Op == OP_NUM || Nodes[i].Op == OP_XX) {
  } else switch (row->GetType()) {
  case TYPE_JOB:
   if (Nodes[i].Key)
    val = ((PJOB)row)->GetKeyValue(Nodes[i].Key);

   break;
  case TYPE_JAR:
   if (!Nodes[i].Key)
    if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
     val = ((PJAR)row)->GetArrayValue(Nodes[i].Rank);

   break;
  case TYPE_JVAL:
   val = (PJVAL)row;
   break;
  default:
   snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->GetType());
  } // endswitch Type

  if (i < Nod-1)
   if (!(row = (val) ? val->GetJsp() : NULL))
    val = NULL;

 } // endfor i

 return (val != NULL);
} // end of CheckPath

/***********************************************************************/
/*  GetRow: Set the complete path of the object to be set.             */
/***********************************************************************/
PJSON JSNX::GetRow(PGLOBAL g)
{
 PJVAL val = NULL;
 PJAR  arp;
 PJSON nwr, row = Row;

 for (int i = 0; i < Nod - 1 && row; i++) {
  if (Nodes[i].Op == OP_XX)
   break;
  else switch (row->GetType()) {
  case TYPE_JOB:
   if (!Nodes[i].Key)
    // Expected Array was not there, wrap the value
    continue;

   val = ((PJOB)row)->GetKeyValue(Nodes[i].Key);
   break;
  case TYPE_JAR:
   arp = (PJAR)row;

   if (!Nodes[i].Key) {
    if (Nodes[i].Op == OP_EQ)
     val = arp->GetArrayValue(Nodes[i].Rank);
    else
     val = arp->GetArrayValue(Nodes[i].Rx);

   } else {
    // Unexpected array, unwrap it as [0]
    val = arp->GetArrayValue(0);
    i--;
   } // endif Nodes

   break;
  case TYPE_JVAL:
   val = (PJVAL)row;
   break;
  default:
   snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->GetType());
   val = NULL;
  } // endswitch Type

  if (val) {
   row = val->GetJson();
  } else {
   // Construct missing objects
   for (i++; row && i < Nod; i++) {
    if (Nodes[i].Op == OP_XX)
     break;
    else if (!Nodes[i].Key)
     // Construct intermediate array
     nwr = new(g)JARRAY;
    else
     nwr = new(g)JOBJECT;

    if (row->GetType() == TYPE_JOB) {
     ((PJOB)row)->SetKeyValue(g, new(g)JVALUE(nwr), Nodes[i-1].Key);
    } else if (row->GetType() == TYPE_JAR) {
     ((PJAR)row)->AddArrayValue(g, new(g)JVALUE(nwr));
     ((PJAR)row)->InitArray(g);
    } else {
     snprintf(g->Message, sizeof(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: 
  snprintf(g->Message, sizeof(g->Message), "Invalid target type");
  return true;
 } // endswitch Type

 if (arp) {
  if (!Nodes[Nod-1].Key) {
   if (Nodes[Nod-1].Op == OP_EQ)
    arp->SetArrayValue(g, jvalp, Nodes[Nod-1].Rank);
   else
    arp->AddArrayValue(g, jvalp);

   arp->InitArray(g);
  } // endif Key

 } else if (objp) {
  if (Nodes[Nod-1].Key)
   objp->SetKeyValue(g, jvalp, Nodes[Nod-1].Key);

 } else if (jvp)
  jvp->SetValue(jvalp);

 return false;
} // end of WriteValue

/*********************************************************************************/
/*  Locate a value in a JSON tree:                                               */
/*********************************************************************************/
PSZ JSNX::Locate(PGLOBAL g, PJSON jsp, PJVAL jvp, int k)
{
 PSZ     str = NULL;
 my_bool err = true;

 g->Message[0] = 0;

 if (!jsp) {
  snprintf(g->Message, sizeof(g->Message), "Null json tree");
  return NULL;
 } // endif jsp

 try {
  // Write to the path string
  Jp = new(g) JOUTSTR(g);
  Jp->WriteChr('$');
  Jvalp = jvp;
  K = k;

  switch (jsp->GetType()) {
   case TYPE_JAR:
    err = LocateArray(g, (PJAR)jsp);
    break;
   case TYPE_JOB:
    err = LocateObject(g, (PJOB)jsp);
    break;
   case TYPE_JVAL:
    err = LocateValue(g, (PJVAL)jsp);
    break;
   default:
    err = true;
  } // endswitch Type

  if (err) {
   if (!g->Message[0])
    snprintf(g->Message, sizeof(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) {
  snprintf(g->Message, sizeof(g->Message), "%s", msg);
 } // end catch

 return str;
} // end of Locate

/*********************************************************************************/
/*  Locate in a JSON Array.                                                      */
/*********************************************************************************/
my_bool JSNX::LocateArray(PGLOBAL g, PJAR jarp)
{
 char   s[16];
 size_t m = Jp->N;

 for (int i = 0; i < jarp->size() && !Found; i++) {
  Jp->N = m;
  snprintf(s, sizeof(s), "[%d]", i + B);

  if (Jp->WriteStr(s))
   return true;

  if (LocateValue(g, jarp->GetArrayValue(i)))
   return true;

  } // endfor i

 return false;
} // end of LocateArray

/*********************************************************************************/
/*  Locate in a JSON Object.                                                     */
/*********************************************************************************/
my_bool JSNX::LocateObject(PGLOBAL g, PJOB jobp)
{
 size_t m;

 if (Jp->WriteChr('.'))
  return true;

 m = Jp->N;

 for (PJPR pair = jobp->First; pair && !Found; pair = pair->Next) {
  Jp->N = m;

  if (Jp->WriteStr(pair->Key))
   return true;

  if (LocateValue(g, pair->Val))
   return true;

  } // endfor i

 return false;
} // end of LocateObject

/*********************************************************************************/
/*  Locate a JSON Value.                                                         */
/*********************************************************************************/
my_bool JSNX::LocateValue(PGLOBAL g, PJVAL jvp)
{
 if (CompareTree(g, Jvalp, jvp))
  Found = (--K == 0);
 else if (jvp->GetArray())
  return LocateArray(g, jvp->GetArray());
 else if (jvp->GetObject())
  return LocateObject(g, jvp->GetObject());

 return false;
} // end of LocateValue

/*********************************************************************************/
/*  Locate all occurrences of a value in a JSON tree:                            */
/*********************************************************************************/
PSZ JSNX::LocateAll(PGLOBAL g, PJSON jsp, PJVAL jvp, int mx)
{
 PSZ     str = NULL;
 my_bool err = true;
 PJPN    jnp;
 
 if (!jsp) {
  snprintf(g->Message, sizeof(g->Message), "Null json tree");
  return NULL;
 } // endif jsp

 try {
  jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx);
  memset(jnp, 0, sizeof(JPN) * mx);
  g->Message[0] = 0;

  // Write to the path string
  Jp = new(g)JOUTSTR(g);
  Jvalp = jvp;
  Imax = mx - 1;
  Jpnp = jnp;
  Jp->WriteChr('[');

  switch (jsp->GetType()) {
   case TYPE_JAR:
    err = LocateArrayAll(g, (PJAR)jsp);
    break;
   case TYPE_JOB:
    err = LocateObjectAll(g, (PJOB)jsp);
    break;
   case TYPE_JVAL:
    err = LocateValueAll(g, (PJVAL)jsp);
    break;
   default:
    err = true;
  } // endswitch Type

  if (!err) {
   if (Jp->N > 1)
    Jp->N--;

   Jp->WriteChr(']');
   Jp->WriteChr('\0');
   PlugSubAlloc(g, NULL, Jp->N);
   str = Jp->Strp;
  } else if (!g->Message[0])
   snprintf(g->Message, sizeof(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) {
  snprintf(g->Message, sizeof(g->Message), "%s", msg);
 } // end catch

 return str;
} // end of LocateAll

/*********************************************************************************/
/*  Locate in a JSON Array.                                                      */
/*********************************************************************************/
my_bool JSNX::LocateArrayAll(PGLOBAL g, PJAR jarp)
{
 if (I < Imax) {
  Jpnp[++I].Type = TYPE_JAR;

  for (int i = 0; i < jarp->size(); i++) {
   Jpnp[I].N = i;

   if (LocateValueAll(g, jarp->GetArrayValue(i)))
    return true;

  } // endfor i

  I--;
 } // endif I

 return false;
} // end of LocateArrayAll

/*********************************************************************************/
/*  Locate in a JSON Object.                                                     */
/*********************************************************************************/
my_bool JSNX::LocateObjectAll(PGLOBAL g, PJOB jobp)
{
 if (I < Imax) {
  Jpnp[++I].Type = TYPE_JOB;

  for (PJPR pair = jobp->First; pair; pair = pair->Next) {
   Jpnp[I].Key = pair->Key;

   if (LocateValueAll(g, pair->Val))
    return true;

  } // endfor i

  I--;
 } // endif I

 return false;
} // end of LocateObjectAll

/*********************************************************************************/
/*  Locate a JSON Value.                                                         */
/*********************************************************************************/
my_bool JSNX::LocateValueAll(PGLOBAL g, PJVAL jvp)
{
 if (CompareTree(g, Jvalp, jvp))
  return AddPath();
 else if (jvp->GetArray())
  return LocateArrayAll(g, jvp->GetArray());
 else if (jvp->GetObject())
  return LocateObjectAll(g, jvp->GetObject());

 return false;
} // end of LocateValueAll

/*********************************************************************************/
/*  Compare two JSON trees.                                                      */
/*********************************************************************************/
my_bool JSNX::CompareTree(PGLOBAL g, PJSON jp1, PJSON jp2)
{
 if (!jp1 || !jp2 || jp1->GetType() != jp2->GetType()
                 || jp1->size() != jp2->size())
  return false;

 my_bool found = true;

 if (jp1->GetType() == TYPE_JVAL) {
// PVL v1 = ((PJVAL)jp1)->GetVal(), v2 = ((PJVAL)jp2)->GetVal();

  if (((PJVAL)jp1)->DataType == TYPE_JSON && ((PJVAL)jp2)->DataType == TYPE_JSON)
   found = CompareTree(g, jp1->GetJsp(), jp2->GetJsp());
  else
   found = CompareValues(((PJVAL)jp1), ((PJVAL)jp2));

 } else if (jp1->GetType() == TYPE_JAR) {
  for (int i = 0; found && i < jp1->size(); i++)
   found = (CompareTree(g, jp1->GetArrayValue(i), jp2->GetArrayValue(i)));

 } else if (jp1->GetType() == TYPE_JOB) {
  PJPR p1 = jp1->GetFirst(), p2 = jp2->GetFirst();

  for (; found && p1 && p2; p1 = p1->Next, p2 = p2->Next)
   found = CompareTree(g, p1->Val, p2->Val);

 } else
  found = false;

 return found;
} // end of CompareTree

/*********************************************************************************/
/*  Compare two VAL values and return true if they are equal.                    */
/*********************************************************************************/
my_bool JSNX::CompareValues(PJVAL v1, PJVAL v2)
{
 my_bool b = false;

 switch (v1->DataType) {
 case TYPE_STRG:
  if (v2->DataType == TYPE_STRG) {
   if (v1->Nd || v2->Nd)  // Case insensitive
    b = (!stricmp(v1->Strp, v2->Strp));
   else
    b = (!strcmp(v1->Strp, v2->Strp));

  } // endif Type

  break;
 case TYPE_DTM:
  if (v2->DataType == TYPE_DTM)
   b = (!strcmp(v1->Strp, v2->Strp));

  break;
 case TYPE_INTG:
  if (v2->DataType == TYPE_INTG)
   b = (v1->N == v2->N);
  else if (v2->DataType == TYPE_BINT)
   b = (v1->N == v2->LLn);

  break;
 case TYPE_BINT:
  if (v2->DataType == TYPE_INTG)
   b = (v1->LLn == v2->N);
  else if (v2->DataType == TYPE_BINT)
   b = (v1->LLn == v2->LLn);

  break;
 case TYPE_DBL:
  if (v2->DataType == TYPE_DBL)
   b = (v1->F == v2->F);
  
  break;
 case TYPE_BOOL:
  if (v2->DataType == TYPE_BOOL)
    b = (v1->B == v2->B);

  break;
 case TYPE_NULL:
  if (v2->DataType == TYPE_NULL)
   b = true;

  break;
 default:
  break;
 } // endswitch Type

 return b;
} // end of CompareValues

/*********************************************************************************/
/*  Add the found path to the list.                                              */
/*********************************************************************************/
my_bool JSNX::AddPath(void) {
 char s[16];

 if (Jp->WriteStr("\"$"))
  return true;

 for (int i = 0; i <= I; i++) {
  if (Jpnp[i].Type == TYPE_JAR) {
   snprintf(s, sizeof(s), "[%d]", Jpnp[i].N + B);

   if (Jp->WriteStr(s))
    return true;

  } else {
   if (Jp->WriteChr('.'))
    return true;

   if (Jp->WriteStr(Jpnp[i].Key))
    return true;

  } // endif's

 } // endfor i

 if (Jp->WriteStr("\","))
  return true;

 return false;
} // end of AddPath

/* --------------------------------- JSON UDF ---------------------------------- */

/*********************************************************************************/
/*  Allocate and initialize a BSON structure.                                    */
/*********************************************************************************/
PBSON JbinAlloc(PGLOBAL g, UDF_ARGS *args, ulong len, PJSON jsp)
{
 PBSON bsp = (PBSON)PlgDBSubAlloc(g, NULL, sizeof(BSON));

 if (bsp) {
  strcpy(bsp->Msg, "Binary Json");
  bsp->Msg[BMX] = 0;
  bsp->Filename = NULL;
  bsp->G = g;
  bsp->Pretty = 2;
  bsp->Reslen = len;
  bsp->Changed = false;
  bsp->Top = bsp->Jsp = jsp;
  bsp->Bsp = (args && IsJson(args, 0) == 3) ? (PBSON)args->args[0] : NULL;
 } else
  PUSH_WARNING(g->Message);

 return bsp;
} /* end of JbinAlloc */

/*********************************************************************************/
/*  Set the BSON chain as changed.                                               */
/*********************************************************************************/
static void SetChanged(PBSON bsp)
{
 if (bsp->Bsp)
  SetChanged(bsp->Bsp);

 bsp->Changed = true;
} /* end of SetChanged */

/*********************************************************************************/
/*  Replaces GetJsonGrpSize not usable when CONNECT is not installed.            */
/*********************************************************************************/
uint GetJsonGroupSize(void)
{
 return (JsonGrpSize) ? JsonGrpSize : GetJsonGrpSize();
} // end of GetJsonGroupSize

/*********************************************************************************/
/*  Program for SubSet re-initialization of the memory pool.                     */
/*********************************************************************************/
my_bool JsonSubSet(PGLOBAL g, my_bool b)
{
 PPOOLHEADER pph = (PPOOLHEADER)g->Sarea;

 pph->To_Free = (g->Saved_Size) ? g->Saved_Size : sizeof(POOLHEADER);
 pph->FreeBlk = g->Sarea_Size - pph->To_Free;

 if (b)
  g->Saved_Size = 0;

 return FALSE;
} /* end of JsonSubSet */

/*********************************************************************************/
/*  Program for saving the status of the memory pools.                           */
/*********************************************************************************/
inline void JsonMemSave(PGLOBAL g)
{
 g->Saved_Size = ((PPOOLHEADER)g->Sarea)->To_Free;
} /* end of JsonMemSave */

/*********************************************************************************/
/*  Program for freeing the memory pools.                                        */
/*********************************************************************************/
inline void JsonFreeMem(PGLOBAL g)
{
 g->Activityp = NULL;
 g = PlugExit(g);
} /* end of JsonFreeMem */

/*********************************************************************************/
/*  SubAlloc a new JSON item with protection against memory exhaustion.          */
/*********************************************************************************/
static PJSON JsonNew(PGLOBAL g, JTYP type)
{
 PJSON jsp = NULL;

 try {
  switch (type) {
   case TYPE_JAR:
    jsp = new(g) JARRAY;
    break;
   case TYPE_JOB:
    jsp = new(g) JOBJECT;
    break;
   default:
    break;
  } // endswitch type

 } catch (...) {
  if (trace(1023))
   htrc("%s\n", g->Message);

  PUSH_WARNING(g->Message);
 } // end try/catch

 return jsp;
} /* end of JsonNew */

/*********************************************************************************/
/*  SubAlloc a new JValue with protection against memory exhaustion.             */
/*********************************************************************************/
static PJVAL JvalNew(PGLOBAL g, JTYP type, void *vp)
{
 PJVAL jvp = NULL;

 try {
  if (!vp)
   jvp = new (g) JVALUE;
  else switch (type) {
   case TYPE_JSON:
   case TYPE_JVAL:
   case TYPE_JAR:
   case TYPE_JOB:
    jvp = new(g) JVALUE((PJSON)vp);
    break;
// case TYPE_VAL:
// jvp = new(g) JVALUE(g, (PVAL)vp);
// break;
   case TYPE_DTM:
   case TYPE_STRG:
    jvp = new(g) JVALUE(g, (PCSZ)vp);
    break;
   default:
    break;
  } // endswitch type

 } catch (...) {
  if (trace(1023))
   htrc("%s\n", g->Message);

  PUSH_WARNING(g->Message);
 } // end try/catch

 return jvp;
} /* end of JvalNew */

/*********************************************************************************/
/*  Allocate and initialise the memory area.                                     */
/*********************************************************************************/
my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args, char *message, my_bool mbn,
                 unsigned long reslen, unsigned long memlen, unsigned long more)
{
  PGLOBAL g = PlugInit(NULL, (size_t)memlen + more + 500); // +500 to avoid CheckMem

  if (!g) {
    strcpy(message, "Allocation error");
    return true;
  } else if (g->Sarea_Size == 0) {
  strcpy(message, g->Message);
  g = PlugExit(g);
  return true;
  } // endif g

 g->Mrr = (args->arg_count && args->args[0]) ? 1 : 0;
 g->More = more;
  initid->maybe_null = mbn;
  initid->max_length = reslen;
 initid->ptr = (char*)g;
 return false;
} // end of JsonInit

/*********************************************************************************/
/*  Check if a path was specified and set jvp according to it.                   */
/*********************************************************************************/
static my_bool CheckPath(PGLOBAL g, UDF_ARGS *args, PJSON jsp, PJVAL& jvp, int n)
{
 for (uint i = n; i < args->arg_count; i++)
  if (args->arg_type[i] == STRING_RESULT && args->args[i]) {
   // A path to a subset of the json tree is given
   char *path = MakePSZ(g, args, i);

   if (path) {
    PJSNX jsx = new(g)JSNX(g, jsp, TYPE_STRING);

    if (jsx->SetJpath(g, path))
     return true;

    if (!(jvp = jsx->GetJson(g))) {
     snprintf(g->Message, sizeof(g->Message), "No sub-item at '%s'", path);
     return true;
    } // endif jvp

   } else {
    snprintf(g->Message, sizeof(g->Message), "Path argument is null");
    return true;
   } // endif path

   break;
  } // endif type

 return false;
} // end of CheckPath

/*********************************************************************************/
/*  Make the result according to the first argument type.                        */
/*********************************************************************************/
static char *MakeResult(PGLOBAL g, UDF_ARGS *args, PJSON top, uint n = 2)
{
 char *str;

 if (IsJson(args, 0) == 2) {
  // Make the change in the json file
  int pretty = 2;

  for (uint i = n; i < args->arg_count; i++)
   if (args->arg_type[i] == INT_RESULT) {
    pretty = (int)*(longlong*)args->args[i];
    break;
   } // endif type

  if (!Serialize(g, top, MakePSZ(g, args, 0), pretty))
   PUSH_WARNING(g->Message);

  str = NULL;
 } else if (IsJson(args, 0) == 3) {
  PBSON bsp = (PBSON)args->args[0];

  if (bsp->Filename) {
   // Make the change in the json file
   if (!Serialize(g, top, bsp->Filename, bsp->Pretty))
    PUSH_WARNING(g->Message);

   str = bsp->Filename;
  } else if (!(str = Serialize(g, top, NULL, 0)))
   PUSH_WARNING(g->Message);

  SetChanged(bsp);
 } else if (!(str = Serialize(g, top, NULL, 0)))
   PUSH_WARNING(g->Message);

 return str;
} // end of MakeResult

/*********************************************************************************/
/*  Make the binary result according to the first argument type.                 */
/*********************************************************************************/
static PBSON MakeBinResult(PGLOBAL g, UDF_ARGS *args, PJSON top, ulong len, int n = 2)
{
 PBSON bsnp = JbinAlloc(g, args, len, top);

 if (!bsnp)
  return NULL;

 if (IsJson(args, 0) == 2) {
  int pretty = 0;

  for (uint i = n; i < args->arg_count; i++)
   if (args->arg_type[i] == INT_RESULT) {
    pretty = (int)*(longlong*)args->args[i];
    break;
   } // endif type

  bsnp->Pretty = pretty;

  if ((bsnp->Filename = (char*)args->args[0])) {
   bsnp->Filename = MakePSZ(g, args, 0);
   strmake(bsnp->Msg, bsnp->Filename, BMX-1);
  } else
   strmake(bsnp->Msg, "null filename", BMX-1);

 } else if (IsJson(args, 0) == 3) {
  PBSON bsp = (PBSON)args->args[0];

  if (bsp->Filename) {
   bsnp->Filename = bsp->Filename;
   strmake(bsnp->Msg, bsp->Filename, BMX-1);
   bsnp->Pretty = bsp->Pretty;
  } else
   strcpy(bsnp->Msg, "Json Binary item");

 } else
  strcpy(bsnp->Msg, "Json Binary item");

 return bsnp;
} // end of MakeBinResult

/*********************************************************************************/
/*  Returns a pointer to the first integer argument found from the nth argument. */
/*********************************************************************************/
static int *GetIntArgPtr(PGLOBAL g, UDF_ARGS *args, uint& n)
{
 int *x = NULL;

 for (uint i = n; i < args->arg_count; i++)
  if (args->arg_type[i] == INT_RESULT) {
   if (args->args[i]) {
    if ((x = (int*)PlgDBSubAlloc(g, NULL, sizeof(int))))
     *x = (int)*(longlong*)args->args[i];
    else
     PUSH_WARNING(g->Message);

   } // endif args

   n = i + 1;
   break;
  } // endif arg_type

 return x;
} // end of GetIntArgPtr

/*********************************************************************************/
/*  Returns not 0 if the argument is a JSON item or file name.                   */
/*********************************************************************************/
int IsJson(UDF_ARGS *args, uint i, bool b)
{
 const char *pat = args->attributes[i];
 int   n = 0;

 if (*pat == '@') {
  pat++;

  if (*pat == '\'' || *pat == '"')
   pat++;

 } // endif pat

 if (i >= args->arg_count || args->arg_type[i] != STRING_RESULT) {
 } else if (!strnicmp(pat, "Json_", 5)) {
  if (!args->args[i] || strchr("[{ \t\r\n", *args->args[i]))
   n = 1;      // arg should be is a json item
  else
   n = 2;           // A file name may have been returned

 } else if (!strnicmp(pat, "Jbin_", 5)) {
  if (args->lengths[i] == sizeof(BSON))
   n = 3;      // arg is a binary json item
  else
   n = 2;           // A file name may have been returned

 } else if (!strnicmp(pat, "Jfile_", 6)) {
  n = 2;        // arg is a json file name
 } else if (b) {
  char   *sap;
  PGLOBAL g = PlugInit(NULL, (size_t)args->lengths[i] * M + 1024);

// JsonSubSet(g);
  sap = MakePSZ(g, args, i);

  if (ParseJson(g, sap, strlen(sap)))
   n = 4;

  JsonFreeMem(g);
 } // endif's

 return n;
} // end of IsJson

/*********************************************************************************/
/*  GetMemPtr: returns the memory pointer used by this argument.                 */
/*********************************************************************************/
static PGLOBAL GetMemPtr(PGLOBAL g, UDF_ARGS *args, uint i)
{
 return (IsJson(args, i) == 3) ? ((PBSON)args->args[i])->G : g;
} // end of GetMemPtr

/*********************************************************************************/
/*  GetFileLength: returns file size in number of bytes.                         */
/*********************************************************************************/
static long GetFileLength(char *fn)
{
 int  h;
 long len;

 h= open(fn, _O_RDONLY);

 if (h != -1) {
  if ((len = _filelength(h)) < 0)
   len = 0;

  close(h);
 } else
  len = 0;

 return len;
} // end of GetFileLength

/*********************************************************************************/
/*  Calculate the reslen and memlen needed by a function.                        */
/*********************************************************************************/
my_bool CalcLen(UDF_ARGS *args, my_bool obj, unsigned long& reslen,
                                            unsigned long& memlen, my_bool mod)
{
 char fn[_MAX_PATH];
  unsigned long i, k, m, n;
 long fl = 0, j = -1;

  reslen = args->arg_count + 2;

  // Calculate the result max length
  for (i = 0; i < args->arg_count; i++) {
  n = IsJson(args, i);

    if (obj) {
      if (!(k = args->attribute_lengths[i]))
        k = strlen(args->attributes[i]);

      reslen += (k + 3);     // For quotes and :
      } // endif obj

    switch (args->arg_type[i]) {
      case STRING_RESULT:
    if (n == 2 && args->args[i]) {
     if (!mod) {
      m = MY_MIN(args->lengths[i], sizeof(fn) - 1);
      memcpy(fn, args->args[i], m);
      fn[m] = 0;
      j = i;
      fl = GetFileLength(fn);
      reslen += fl;
     } else
      reslen += args->lengths[i];

    } else if (n == 3 && args->args[i])
     reslen += ((PBSON)args->args[i])->Reslen;
    else if (n == 1)
          reslen += args->lengths[i];
    else
          reslen += (args->lengths[i] + 1) * 2;   // Pessimistic !
  
        break;
      case INT_RESULT:
        reslen += 20;
        break;
      case REAL_RESULT:
        reslen += 31;
        break;
      case DECIMAL_RESULT:
        reslen += (args->lengths[i] + 7);   // 6 decimals
        break;
      case TIME_RESULT:
      case ROW_RESULT:
      default:
        // What should we do here ?
        break;
      } // endswitch arg_type

    } // endfor i

 // Calculate the amount of memory needed
 memlen = MEMFIX + sizeof(JOUTSTR) + reslen;

  for (i = 0; i < args->arg_count; i++) {
  n = IsJson(args, i);
  memlen += (args->lengths[i] + sizeof(JVALUE));

    if (obj) {
      if (!(k = args->attribute_lengths[i]))
        k = strlen(args->attributes[i]);

      memlen += (k + sizeof(JOBJECT) + sizeof(JPAIR));
    } else
      memlen += sizeof(JARRAY);

  switch (args->arg_type[i]) {
   case STRING_RESULT:
    if (n == 2 && args->args[i]) {
     if ((signed)i != j) {
      m = MY_MIN(args->lengths[i], sizeof(fn) - 1);
      memcpy(fn, args->args[i], m);
      fn[m] = 0;
      j = -1;
      fl = GetFileLength(fn);
     } // endif i

     memlen += fl * M;
    } else if (n == 1) {
     if (i == 0)
      memlen += sizeof(BSON);     // For Jbin functions

     memlen += args->lengths[i] * M;  // Estimate parse memory
    } else if (n == 3)
     memlen += sizeof(JVALUE);

        memlen += sizeof(TYPVAL<PSZ>);
        break;
      case INT_RESULT:
        memlen += sizeof(TYPVAL<int>);
        break;
      case REAL_RESULT:
      case DECIMAL_RESULT:
        memlen += sizeof(TYPVAL<double>);
        break;
      case TIME_RESULT:
      case ROW_RESULT:
      default:
        // What should we do here ?
        break;
      } // endswitch arg_type

    } // endfor i

 return false;
} // end of CalcLen

/*********************************************************************************/
/*  Check if the calculated memory is enough.                                    */
/*********************************************************************************/
my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n,
                   my_bool m, my_bool obj, my_bool mod)
{
 unsigned long rl, ml;
 my_bool       b = false;

 n = MY_MIN(n, args->arg_count);                       

 for (uint i = 0; i < n; i++)
  if (IsJson(args, i) == 2 ||
    (b = (m && !i && args->arg_type[0] == STRING_RESULT && !IsJson(args, 0)))) {
   if (CalcLen(args, obj, rl, ml, mod))
    return true;
   else if (b) {
    ulong len;
    char *p = args->args[0];

    // Is this a file name?
    if (p && !strchr("[{ \t\r\n", *p) && (len = GetFileLength(p)))
     ml += len * (M + 1);
    else
     ml += args->lengths[0] * M;

   } // endif b

   ml += g->More;

   if (ml > g->Sarea_Size) {
    FreeSarea(g);

    if (AllocSarea(g, ml)) {
     char errmsg[MAX_STR];

     snprintf(errmsg, sizeof(errmsg) - 1, MSG(WORK_AREA), g->Message);
     snprintf(g->Message, sizeof(g->Message), "%s", errmsg);
     return true;
     } // endif SareaAlloc

    g->Saved_Size = 0;
    g->Xchk = NULL;
    initid->max_length = rl;
   } // endif Size

   break;
  } // endif IsJson

 JsonSubSet(g);
 return false;
} // end of CheckMemory

/*********************************************************************************/
/*  Make a zero terminated string from the passed argument.                      */
/*********************************************************************************/
PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i)
{
 if (args->arg_count > (unsigned)i && args->args[i]) {
    int n = args->lengths[i];
    PSZ s = (PSZ)PlgDBSubAlloc(g, NULL, n + 1);

  if (s) {
   memcpy(s, args->args[i], n);
   s[n] = 0;
  } else
   PUSH_WARNING(g->Message);

    return s;
  } else
    return NULL;

} // end of MakePSZ

/*********************************************************************************/
/*  Make a valid key from the passed argument.                                   */
/*********************************************************************************/
static PCSZ MakeKey(PGLOBAL g, UDF_ARGS *args, int i)
{
 if (args->arg_count > (unsigned)i) {
  int     j = 0, n = args->attribute_lengths[i];
  my_bool b;  // true if attribute is zero terminated
  PSZ     p;
  PCSZ    s = args->attributes[i];

  if (s && *s && (n || *s == '\'')) {
   if ((b = (!n || !s[n])))
    n = strlen(s);

   if (IsJson(args, i))
    j = (int)(strchr(s, '_') - s + 1);

   if (j && n > j) {
    s += j;
    n -= j;
   } else if (*s == '\'' && s[n-1] == '\'') {
    s++;
    n -= 2;
    b = false;
   } // endif *s

   if (n < 1)
        return (PCSZ) "Key";

   if (!b) {
    if ((p = (PSZ)PlgDBSubAlloc(g, NULL, n + 1))) {
     memcpy(p, s, n);
     p[n] = 0;
    } else
     PUSH_WARNING(g->Message);

    s = p;
   } // endif b

  } // endif s

  return s;
 } // endif count

  return (PCSZ) "Key";
} // end of MakeKey

/*********************************************************************************/
/*  Parse a json file.                                                 */
/*********************************************************************************/
static PJSON ParseJsonFile(PGLOBAL g, char *fn, int *pretty, size_t& len)
{
 char   *memory;
 HANDLE  hFile;
 MEMMAP  mm;
 PJSON   jsp;

 /*******************************************************************************/
 /*  Create the mapping file object.                                            */
 /*******************************************************************************/
 hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);

 if (hFile == INVALID_HANDLE_VALUE) {
  DWORD rc = GetLastError();

  if (!(*g->Message))
   snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR), "map", (int)rc, fn);

  return NULL;
 } // endif hFile

 /*******************************************************************************/
 /*  Get the file size.                                                         */
 /*******************************************************************************/
 len = (size_t)mm.lenL;

 if (mm.lenH)
  len += ((size_t)mm.lenH * 0x000000001LL);

 memory = (char *)mm.memory;

 if (!len) {              // Empty or deleted file
  CloseFileHandle(hFile);
  return NULL;
 } // endif len

 if (!memory) {
  CloseFileHandle(hFile);
  snprintf(g->Message, sizeof(g->Message), MSG(MAP_VIEW_ERROR), fn, GetLastError());
  return NULL;
 } // endif Memory

 CloseFileHandle(hFile);                    // Not used anymore

 /*********************************************************************************/
 /*  Parse the json file and allocate its tree structure.                         */
 /*********************************************************************************/
 g->Message[0] = 0;
 jsp = ParseJson(g, memory, len, pretty);
 CloseMemMap(memory, len);
 return jsp;
} // end of ParseJsonFile

/*********************************************************************************/
/*  Return a json file contains.                                                 */
/*********************************************************************************/
char *GetJsonFile(PGLOBAL g, char *fn)
{
 char   *str;
 int     h, n, len;

#if defined(UNIX) || defined(UNIV_LINUX)
 h= open(fn, O_RDONLY);
#else
 h= open(fn, _O_RDONLY, _O_TEXT);
#endif

 if (h == -1) {
  snprintf(g->Message, sizeof(g->Message), "Error %d opening %-.1024s", errno, fn);
  return NULL;
 } // endif h

 if ((len = _filelength(h)) < 0) {
  snprintf(g->Message, sizeof(g->Message), MSG(FILELEN_ERROR), "_filelength", fn);
  close(h);
  return NULL;
 } // endif len

 if ((str = (char*)PlgDBSubAlloc(g, NULL, len + 1))) {
  if ((n = read(h, str, len)) < 0) {
   snprintf(g->Message, sizeof(g->Message), "Error %d reading %d bytes from %-.1024s", errno, len, fn);
   return NULL;
  } // endif n

  str[n] = 0;
  close(h);
 } // endif str
 
 return str;
} // end of GetJsonFile

/*********************************************************************************/
/*  Make a JSON value from the passed argument.                                  */
/*********************************************************************************/
static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, uint i, PJSON *top = NULL)
{
 char *sap = (args->arg_count > i) ? args->args[i] : NULL;
 int   n, len;
 int   ci;
 long long bigint;
  PJSON jsp;
  PJVAL jvp = new(g) JVALUE;

 if (top)
  *top = NULL;

  if (sap) switch (args->arg_type[i]) {
    case STRING_RESULT:
      if ((len = args->lengths[i])) {
    if ((n = IsJson(args, i)) < 3)
     sap = MakePSZ(g, args, i);

        if (n) {
     if (n == 3) {
      if (top)
       *top = ((PBSON)sap)->Top;

      jsp = ((PBSON)sap)->Jsp;
     } else {
      if (n == 2) {
       if (!(sap = GetJsonFile(g, sap))) {
        PUSH_WARNING(g->Message);
        return jvp;
       } // endif sap

       len = strlen(sap);
      } // endif n

      if (!(jsp = ParseJson(g, sap, strlen(sap))))
       PUSH_WARNING(g->Message);
      else if (top)
       *top = jsp;

     } // endif's n

          if (jsp && jsp->GetType() == TYPE_JVAL)
            jvp = (PJVAL)jsp;
          else
            jvp->SetValue(jsp);

    } else {
     ci = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1;
     jvp->SetString(g, sap, ci);
    } // endif n

      } // endif len

      break;
    case INT_RESULT:
   bigint = *(long long*)sap;

   if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) ||
     (bigint == 1LL && !strcmp(args->attributes[i], "TRUE")))
    jvp->SetBool(g, (char)bigint);
   else
    jvp->SetBigint(g, bigint);

      break;
    case REAL_RESULT:
      jvp->SetFloat(g, *(double*)sap);
      break;
    case DECIMAL_RESULT:
      jvp->SetFloat(g, atof(MakePSZ(g, args, i)));
      break;
    case TIME_RESULT:
    case ROW_RESULT:
    default:
      break;
    } // endswitch arg_type

  return jvp;
} // end of MakeValue

/*********************************************************************************/
/*  Try making a JSON value of the passed type from the passed argument.         */
/*********************************************************************************/
static PJVAL MakeTypedValue(PGLOBAL g, UDF_ARGS *args, uint i, 
 JTYP type, PJSON *top = NULL)
{
 char *sap;
 PJSON jsp;
 PJVAL jvp = MakeValue(g, args, i, top);

 //if (type == TYPE_JSON) {
 // if (jvp->GetValType() >= TYPE_JSON)
 // return jvp;

 //} else if (jvp->GetValType() == type)
 // return jvp;
 
 if (jvp->GetValType() == TYPE_STRG) {
  sap = jvp->GetString(g);

  if ((jsp = ParseJson(g, sap, strlen(sap)))) {
   if ((type == TYPE_JSON && jsp->GetType() != TYPE_JVAL) || jsp->GetType() == type) {
    if (top)
     *top = jsp;

    jvp->SetValue(jsp);
   } // endif Type

  } // endif jsp

 } // endif Type

 return jvp;
} // end of MakeTypedValue

/* ------------------------------ The JSON UDF's ------------------------------- */

/*********************************************************************************/
/*  Make a Json value containing the parameter.                                  */
/*********************************************************************************/
my_bool jsonvalue_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
  unsigned long reslen, memlen;

  if (args->arg_count > 1) {
    strcpy(message, "Cannot accept more than 1 argument");
    return true;
  } else
    CalcLen(args, false, reslen, memlen);

  return JsonInit(initid, args, message, false, reslen, memlen);
} // end of jsonvalue_init

char *jsonvalue(UDF_INIT *initid, UDF_ARGS *args, char *result, 
                unsigned long *res_length, uchar *, uchar *)
{
  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, uchar *, uchar *)
{
  char   *str;
  PGLOBAL g = (PGLOBAL)initid->ptr;

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, false)) {
   PJAR arp = new(g)JARRAY;

   for (uint i = 0; i < args->arg_count; i++)
    arp->AddArrayValue(g, MakeValue(g, args, i));

   arp->InitArray(g);

   if (!(str = Serialize(g, arp, NULL, 0)))
    str = strcpy(result, g->Message);

  } else
   str = strcpy(result, g->Message);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? str : NULL;
 } else
  str = (char*)g->Xchk;

 *res_length = strlen(str);
  return str;
} // end of json_make_array

void json_make_array_deinit(UDF_INIT* initid)
{
  JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_make_array_deinit

/*********************************************************************************/
/*  Add one or several values to a Json array.                                   */
/*********************************************************************************/
my_bool json_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 //} else if (!IsJson(args, 0, true)) {
 // strcpy(message, "First argument must be a valid json string or item");
 // return true;
 } else
  CalcLen(args, false, reslen, memlen);

 if (!JsonInit(initid, args, message, true, reslen, memlen)) {
  PGLOBAL g = (PGLOBAL)initid->ptr;

  // This is a constant function
  g->N = (initid->const_item) ? 1 : 0;

  // This is to avoid double execution when using prepared statements
  if (IsJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of json_array_add_values_init

char *json_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *)
{
 char   *str = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, true)) {
   PJSON top;
   PJAR  arp;
   PJVAL jvp = MakeTypedValue(g, args, 0, TYPE_JAR, &top);
   
   if (jvp->GetValType() != TYPE_JAR) {
    arp = new(g)JARRAY;
    arp->AddArrayValue(g, jvp);
    top = arp;
   } else
    arp = jvp->GetArray();

   for (uint i = 1; i < args->arg_count; i++)
    arp->AddArrayValue(g, MakeValue(g, args, i));

   arp->InitArray(g);
   str = MakeResult(g, args, top, args->arg_count);
  } // endif CheckMemory

  if (!str) {
   PUSH_WARNING(g->Message);
   str = args->args[0];
  } // endif str

  // Keep result of constant function
  g->Xchk = (g->N) ? str : NULL;
 } else
  str = (char*)g->Xchk;

 if (!str) {
  *res_length = 0;
  *is_null = 1;
 } else
  *res_length = strlen(str);

 return str;
} // end of json_array_add_values

void json_array_add_values_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_array_add_values_deinit

/*********************************************************************************/
/*  Add one value to a Json array.                                               */
/*********************************************************************************/
my_bool json_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 //} else if (!IsJson(args, 0, true)) {
 // strcpy(message, "First argument is not a valid Json item");
 // return true;
 } else
    CalcLen(args, false, reslen, memlen, true);

 if (!JsonInit(initid, args, message, true, reslen, memlen)) {
  PGLOBAL g = (PGLOBAL)initid->ptr;

  // This is a constant function
  g->N = (initid->const_item) ? 1 : 0;

  // This is to avoid double execution when using prepared statements
  if (IsJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of json_array_add_init

char *json_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result, 
                     unsigned long *res_length, uchar *is_null, uchar *error)
{
 char   *str = NULL;
  PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->Xchk) {
  // This constant function was recalled
  str = (char*)g->Xchk;
  goto fin;
 } // endif Xchk

 if (!CheckMemory(g, initid, args, 2, false, false, true)) {
  int  *x;
  uint n = 2;
  PJSON jsp, top;
  PJVAL jvp;
  PJAR  arp;

  jvp = MakeTypedValue(g, args, 0, TYPE_JSON, &top);
  jsp = jvp->GetJson();
  x = GetIntArgPtr(g, args, n);

  if (CheckPath(g, args, jsp, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp) {
   PGLOBAL gb = GetMemPtr(g, args, 0);

   if (jvp->GetValType() != TYPE_JAR) {
    if ((arp = (PJAR)JsonNew(gb, TYPE_JAR))) {
     arp->AddArrayValue(gb, JvalNew(gb, TYPE_JVAL, jvp));
     jvp->SetValue(arp);

     if (!top)
      top = arp;

    } // endif arp

   } else
    arp = jvp->GetArray();

   if (arp) {
    arp->AddArrayValue(gb, MakeValue(gb, args, 1), x);
    arp->InitArray(gb);
    str = MakeResult(g, args, top, n);
   } else
    PUSH_WARNING(gb->Message);

  } else {
   PUSH_WARNING("Target is not an array");
   // if (g->Mrr) *error = 1;  (only if no path)
  } // endif jvp

 } // endif CheckMemory

 // In case of error or file, return unchanged argument
 if (!str)
  str = MakePSZ(g, args, 0);

 if (g->N)
  // Keep result of constant function
  g->Xchk = str;

 fin:
 if (!str) {
  *res_length = 0;
  *is_null = 1;
  *error = 1;
 } else
  *res_length = strlen(str);

 return str;
} // end of json_array_add

void json_array_add_deinit(UDF_INIT* initid)
{
  JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_array_add_deinit

/*********************************************************************************/
/*  Delete a value from a Json array.                                            */
/*********************************************************************************/
my_bool json_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
  unsigned long reslen, memlen;

 if (args->arg_count < 2) {
    strcpy(message, "This function must have at least 2 arguments");
    return true;
 } else
    CalcLen(args, false, reslen, memlen, true);

 if (!JsonInit(initid, args, message, true, reslen, memlen)) {
  PGLOBAL g = (PGLOBAL)initid->ptr;

  // This is a constant function
  g->N = (initid->const_item) ? 1 : 0;

  // This is to avoid double execution when using prepared statements
  if (IsJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of json_array_delete_init

char *json_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, 
                        unsigned long *res_length, uchar *is_null, uchar *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, uchar *is_null, uchar *error)
{
 long long n = 0LL;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  if (!g->Activityp) {
   *is_null = 1;
   return 0LL;
  } else
   return *(long long*)g->Activityp;

 } else if (initid->const_item)
  g->N = 1;

 if (!CheckMemory(g, initid, args, 1, false, false, true)) {
  PJVAL jvp = MakeValue(g, args, 0);

  if (jvp && jvp->GetValType() == TYPE_JAR) {
   PJAR arp = jvp->GetArray();

   for (int i = 0; i < arp->size(); i++)
    n += arp->GetArrayValue(i)->GetBigint();

  } else {
   PUSH_WARNING("First argument target is not an array");
  } // endif jvp

 } else {
  *error = 1;
  n = -1LL;
 } // end of CheckMemory

 if (g->N) {
  // Keep result of constant function
  long long *np;
  
  if ((np = (long long*)PlgDBSubAlloc(g, NULL, sizeof(long long)))) {
   *np = n;
   g->Activityp = (PACTIVITY)np;
  } else
   PUSH_WARNING(g->Message);

 } // endif const_item

 return n;
} // end of jsonsum_int

void jsonsum_int_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jsonsum_int_deinit

/*********************************************************************************/
/*  Sum big integer values from a Json array.                                    */
/*********************************************************************************/
my_bool jsonsum_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more;

 if (args->arg_count != 1) {
  strcpy(message, "This function must have 1 argument");
  return true;
 } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else
  CalcLen(args, false, reslen, memlen);

 // TODO: calculate this
 more = (IsJson(args, 0) != 3) ? 1000 : 0;

 return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jsonsum_real_init

double jsonsum_real(UDF_INIT *initid, UDF_ARGS *args, uchar *is_null, uchar *error)
{
 double  n = 0.0;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  if (!g->Activityp) {
   *is_null = 1;
   return 0.0;
  } else
   return *(double*)g->Activityp;

 } else if (initid->const_item)
  g->N = 1;

 if (!CheckMemory(g, initid, args, 1, false, false, true)) {
  PJVAL jvp = MakeValue(g, args, 0);

  if (jvp && jvp->GetValType() == TYPE_JAR) {
   PJAR arp = jvp->GetArray();

   for (int i = 0; i < arp->size(); i++)
    n += arp->GetArrayValue(i)->GetFloat();

  } else {
   PUSH_WARNING("First argument target is not an array");
  } // endif jvp

 } else {
  *error = 1;
  n = -1.0;
 } // endif CheckMemory

 if (g->N) {
  // Keep result of constant function
  double *np;

  if ((np = (double*)PlgDBSubAlloc(g, NULL, sizeof(double)))) {
   *np = n;
   g->Activityp = (PACTIVITY)np;
  } else {
   PUSH_WARNING(g->Message);
   *error = 1;
   n = -1.0;
  } // endif np

 } // endif const_item

 return n;
} // end of jsonsum_real

void jsonsum_real_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jsonsum_real_deinit

/*********************************************************************************/
/*  Returns the average of big integer values from a Json array.                 */
/*********************************************************************************/
my_bool jsonavg_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return jsonsum_real_init(initid, args, message);
} // end of jsonavg_real_init

double jsonavg_real(UDF_INIT *initid, UDF_ARGS *args, uchar *is_null, uchar *error)
{
 double  n = 0.0;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  if (!g->Activityp) {
   *is_null = 1;
   return 0.0;
  } else
   return *(double*)g->Activityp;

 } else if (initid->const_item)
  g->N = 1;

 if (!CheckMemory(g, initid, args, 1, false, false, true)) {
  PJVAL jvp = MakeValue(g, args, 0);

  if (jvp && jvp->GetValType() == TYPE_JAR) {
   PJAR arp = jvp->GetArray();

   if (arp->size()) {
    for (int i = 0; i < arp->size(); i++)
     n += arp->GetArrayValue(i)->GetFloat();

    n /= arp->size();
   } // endif size

  } else {
   PUSH_WARNING("First argument target is not an array");
  } // endif jvp

 } else {
  *error = 1;
  n = -1.0;
 } // endif CheckMemory

 if (g->N) {
  // Keep result of constant function
  double *np;
  
  if ((np = (double*)PlgDBSubAlloc(g, NULL, sizeof(double)))) {
   *np = n;
   g->Activityp = (PACTIVITY)np;
  } else {
   *error = 1;
   n = -1.0;
  } // endif np

 } // endif const_item

 return n;
} // end of jsonavg_real

void jsonavg_real_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jsonavg_real_deinit

/*********************************************************************************/
/*  Make a Json Object containing all the parameters.                            */
/*********************************************************************************/
my_bool json_make_object_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
  unsigned long reslen, memlen;

  CalcLen(args, true, reslen, memlen);
  return JsonInit(initid, args, message, false, reslen, memlen);
} // end of json_make_object_init

char *json_make_object(UDF_INIT *initid, UDF_ARGS *args, char *result,
                       unsigned long *res_length, uchar *, uchar *)
{
  char   *str = NULL;
  PGLOBAL g = (PGLOBAL)initid->ptr;

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, false, false, true)) {
   PJOB objp;
   
   if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     objp->SetKeyValue(g, MakeValue(g, args, i), MakeKey(g, args, i));

    str = Serialize(g, objp, NULL, 0);
   } // endif objp

  } // endif CheckMemory

  if (!str)
   str = strcpy(result, g->Message);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? str : NULL;
 } else
  str = (char*)g->Xchk;

 *res_length = strlen(str);
  return str;
} // end of json_make_object

void json_make_object_deinit(UDF_INIT* initid)
{
  JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_make_object_deinit

/*********************************************************************************/
/*  Make a Json Object containing all not null parameters.                       */
/*********************************************************************************/
my_bool json_object_nonull_init(UDF_INIT *initid, UDF_ARGS *args,
                                char *message)
{
  unsigned long reslen, memlen;

  CalcLen(args, true, reslen, memlen);
  return JsonInit(initid, args, message, false, reslen, memlen);
} // end of json_object_nonull_init

char *json_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result, 
                         unsigned long *res_length, uchar *, uchar *)
{
  char   *str = NULL;
  PGLOBAL g = (PGLOBAL)initid->ptr;

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
   PJVAL jvp;
   PJOB  objp;
   
   if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     if (!(jvp = MakeValue(g, args, i))->IsNull())
      objp->SetKeyValue(g, jvp, MakeKey(g, args, i));

    str = Serialize(g, objp, NULL, 0);
   } // endif objp

  } // endif CheckMemory

  if (!str)
   str = strcpy(result, g->Message);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? str : NULL;
 } else
  str = (char*)g->Xchk;

 *res_length = strlen(str);
  return str;
} // end of json_object_nonull

void json_object_nonull_deinit(UDF_INIT* initid)
{
  JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_object_nonull_deinit

/*********************************************************************************/
/*  Make a Json Object containing all the key/value parameters.                  */
/*********************************************************************************/
my_bool json_object_key_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count % 2) {
  strcpy(message, "This function must have an even number of arguments");
  return true;
 } // endif arg_count

 CalcLen(args, true, reslen, memlen);
 return JsonInit(initid, args, message, false, reslen, memlen);
} // end of json_object_key_init

char *json_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *, uchar *)
{
 char   *str = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
   PJOB objp;

   if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i += 2)
     objp->SetKeyValue(g, MakeValue(g, args, i + 1), MakePSZ(g, args, i));

    str = Serialize(g, objp, NULL, 0);
   } // endif objp

  } // endif CheckMemory

  if (!str)
   str = strcpy(result, g->Message);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? str : NULL;
 } else
  str = (char*)g->Xchk;

 *res_length = strlen(str);
 return str;
} // end of json_object_key

void json_object_key_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_object_key_deinit

/*********************************************************************************/
/*  Add or replace a value in a Json Object.                                     */
/*********************************************************************************/
my_bool json_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 } else if (!IsJson(args, 0)) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else
  CalcLen(args, true, reslen, memlen, true);

 if (!JsonInit(initid, args, message, true, reslen, memlen)) {
  PGLOBAL g = (PGLOBAL)initid->ptr;

  // This is a constant function
  g->N = (initid->const_item) ? 1 : 0;

  // This is to avoid double execution when using prepared statements
  if (IsJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of json_object_add_init

char *json_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
                          unsigned long *res_length, uchar *is_null,
                          uchar *error)
{
 PCSZ    key;
 char   *str = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->Xchk) {
  // This constant function was recalled
  str = (char*)g->Xchk;
  goto fin;
 } // endif Xchk

 if (!CheckMemory(g, initid, args, 2, false, true, true)) {
  PJOB    jobp;
  PJVAL   jvp;
  PJSON   jsp, top;
  PGLOBAL gb = GetMemPtr(g, args, 0);

  jvp = MakeValue(g, args, 0, &top);
  jsp = jvp->GetJson();

  if (CheckPath(g, args, jsp, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp && jvp->GetValType() == TYPE_JOB) {
   jobp = jvp->GetObject();
   jvp = MakeValue(gb, args, 1);
   key = MakeKey(gb, args, 1);
   jobp->SetKeyValue(gb, jvp, key);
   str = MakeResult(g, args, top);
  } else {
   PUSH_WARNING("First argument target is not an object");
// if (g->Mrr) *error = 1;  (only if no path)
  } // endif jvp

 } // endif CheckMemory

 // In case of error or file, return unchanged argument
 if (!str)
  str = MakePSZ(g, args, 0);

 if (g->N)
  // Keep result of constant function
  g->Xchk = str;

 fin:
 if (!str) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = strlen(str);

 return str;
} // end of json_object_add

void json_object_add_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_object_add_deinit

/*********************************************************************************/
/*  Delete a value from a Json object.                                           */
/*********************************************************************************/
my_bool json_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have 2 or 3 arguments");
  return true;
 } else if (!IsJson(args, 0)) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else if (args->arg_type[1] != STRING_RESULT) {
  strcpy(message, "Second argument must be a key string");
  return true;
 } else
  CalcLen(args, true, reslen, memlen, true);

 if (!JsonInit(initid, args, message, true, reslen, memlen)) {
  PGLOBAL g = (PGLOBAL)initid->ptr;

  // This is a constant function
  g->N = (initid->const_item) ? 1 : 0;

  // This is to avoid double execution when using prepared statements
  if (IsJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of json_object_delete_init

char *json_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
                         unsigned long *res_length, uchar *is_null, uchar *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, uchar *is_null, uchar *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, uchar *is_null, uchar *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, uchar *, uchar *)
{
 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, uchar *, uchar *)
{
 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, uchar *, uchar *)
{
  PGLOBAL g = (PGLOBAL)initid->ptr;
  PJAR    arp = (PJAR)g->Activityp;

  if (arp && g->N-- > 0)
    arp->AddArrayValue(g, MakeValue(g, args, 0));

} // end of json_array_grp_add

char *json_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result, 
                     unsigned long *res_length, uchar *, uchar *)
{
  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, uchar *, uchar *)
{
  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, uchar *, uchar *)
{
  PGLOBAL g = (PGLOBAL)initid->ptr;
  PJOB    objp = (PJOB)g->Activityp;

 if (g->N-- > 0)
  objp->SetKeyValue(g, MakeValue(g, args, 1), MakePSZ(g, args, 0));

} // end of json_object_grp_add

char *json_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result, 
                      unsigned long *res_length, uchar *, uchar *)
{
  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, uchar *, uchar *)
{
  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, uchar *is_null, uchar *error)
{
 char   *str = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->Xchk) {
  // This constant function was recalled
  str = (char*)g->Xchk;
  goto fin;
 } // endif Xchk

 if (!CheckMemory(g, initid, args, 2, false, false, true)) {
  PJSON top = NULL;
  PJVAL jvp;
  PJSON jsp[2] = {NULL, NULL};

  for (int i = 0; i < 2; i++) {
   jvp = MakeValue(g, args, i);
   if (!i) top = jvp->GetJson();

   if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) {
    snprintf(g->Message, sizeof(g->Message), "Argument %d is not an array or object", i);
    PUSH_WARNING(g->Message);
   } else
    jsp[i] = jvp->GetJsp();

  } // endfor i

  if (jsp[0]) {
   if (jsp[0]->Merge(GetMemPtr(g, args, 0), jsp[1]))
    PUSH_WARNING(GetMemPtr(g, args, 0)->Message);
   else
    str = MakeResult(g, args, top);

  } // endif jsp

 } // endif CheckMemory

 // In case of error or file, return unchanged argument
 if (!str)
  str = MakePSZ(g, args, 0);

 if (g->N)
  // Keep result of constant function
  g->Xchk = str;

 fin:
 if (!str) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = strlen(str);

 return str;
} // end of json_item_merge

void json_item_merge_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_item_merge_deinit

/*********************************************************************************/
/*  Get a Json item from a Json document.                                        */
/*********************************************************************************/
my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more;
 int n = IsJson(args, 0);

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 } else if (!n && args->arg_type[0] != STRING_RESULT) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else if (args->arg_type[1] != STRING_RESULT) {
  strcpy(message, "Second argument is not a string (jpath)");
  return true;
 } else
  CalcLen(args, false, reslen, memlen);

 if (n == 2 && args->args[0]) {
  char fn[_MAX_PATH];
  long fl;

  memcpy(fn, args->args[0], args->lengths[0]);
  fn[args->lengths[0]] = 0;
  fl = GetFileLength(fn);
  more = fl * 3;
 } else if (n != 3) {
  more = args->lengths[0] * 3;
 } else
  more = 0;

 return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of json_get_item_init

char *json_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
              unsigned long *res_length, uchar *is_null, uchar *)
{
 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, uchar *is_null, uchar *)
{
 char   *p, *path, *str = NULL;
 PJSON   jsp;
 PJSNX   jsx;
 PJVAL   jvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  str = (char*)g->Activityp;
  goto err;
 } else if (initid->const_item)
  g->N = 1;

 try {
  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, true)) {
    PUSH_WARNING("CheckMemory error");
    goto err;
   } else
    jvp = MakeValue(g, args, 0);

   if ((p = jvp->GetString(g))) {
    if (!(jsp = ParseJson(g, p, strlen(p)))) {
     PUSH_WARNING(g->Message);
     goto err;
    } // endif jsp

   } else
    jsp = jvp->GetJson();

   if (g->Mrr) {    // First argument is a constant
    g->Xchk = jsp;
    JsonMemSave(g);
   } // endif Mrr

  } else
   jsp = (PJSON)g->Xchk;

  path = MakePSZ(g, args, 1);
  jsx = JsnxNew(g, jsp, TYPE_STRING, initid->max_length);

  if (!jsx || jsx->SetJpath(g, path)) {
   PUSH_WARNING(g->Message);
   goto err;
  } // endif SetJpath

  jsx->ReadValue(g);

  if (!jsx->GetValue()->IsNull())
   str = jsx->GetValue()->GetCharValue();

  if (initid->const_item)
   // Keep result of constant function
   g->Activityp = (PACTIVITY)str;

 } catch (int n) {
   if (trace(1))
    htrc("Exception %d: %-.256s\n", n, g->Message);

  PUSH_WARNING(g->Message);
  str = NULL;
 } catch (const char *msg) {
   snprintf(g->Message, sizeof(g->Message), "%s", 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,
               uchar *is_null, uchar *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,
                    uchar *is_null, uchar *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, uchar *is_null, uchar *error)
{
 char   *path = NULL;
 int     k;
 PJVAL   jvp, jvp2;
 PJSON   jsp;
 PJSNX   jsx;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  if (g->Activityp) {
   path = (char*)g->Activityp;
   *res_length = strlen(path);
   return path;
  } else {
   *res_length = 0;
   *is_null = 1;
   return NULL;
  } // endif Activityp

 } else if (initid->const_item)
  g->N = 1;

 try {
  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, !g->Xchk)) {
    PUSH_WARNING("CheckMemory error");
    *error = 1;
    goto err;
   } else
    jvp = MakeTypedValue(g, args, 0, TYPE_JSON);

   //if ((p = jvp->GetString(g))) {
   // if (!(jsp = ParseJson(g, p, strlen(p)))) {
   // PUSH_WARNING(g->Message);
   // goto err;
   // } // endif jsp
   //} else
   // jsp = jvp->GetJson();

   if (!(jsp = jvp->GetJson())) {
    PUSH_WARNING("First argument is not a valid JSON item");
    goto err;
   } // endif jsp

   if (g->Mrr) {    // First argument is a constant
    g->Xchk = jsp;
    JsonMemSave(g);
   } // endif Mrr

  } else
   jsp = (PJSON)g->Xchk;

  // The item to locate
  jvp2 = MakeValue(g, args, 1);

  k = (args->arg_count > 2) ? (int)*(long long*)args->args[2] : 1;

  jsx = new(g) JSNX(g, jsp, TYPE_STRING);
  path = jsx->Locate(g, jsp, jvp2, k);

  if (initid->const_item)
   // Keep result of constant function
   g->Activityp = (PACTIVITY)path;

 } catch (int n) {
   if (trace(1))
    htrc("Exception %d: %-.256s\n", n, g->Message);

  PUSH_WARNING(g->Message);
  *error = 1;
  path = NULL;
 } catch (const char *msg) {
  snprintf(g->Message, sizeof(g->Message), "%s", 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, uchar *is_null, uchar *error)
{
 char   *p, *path = NULL;
 int     mx = 10;
 PJVAL   jvp, jvp2;
 PJSON   jsp;
 PJSNX   jsx;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  if (g->Activityp) {
   path = (char*)g->Activityp;
   *res_length = strlen(path);
   return path;
  } else {
   *error = 1;
   *res_length = 0;
   *is_null = 1;
   return NULL;
  } // endif Activityp

 } else if (initid->const_item)
  g->N = 1;

 try {
  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, true)) {
    PUSH_WARNING("CheckMemory error");
    *error = 1;
    goto err;
   } else
    jvp = MakeValue(g, args, 0);

   if ((p = jvp->GetString(g))) {
    if (!(jsp = ParseJson(g, p, strlen(p)))) {
     PUSH_WARNING(g->Message);
     goto err;
    } // endif jsp

   } else
    jsp = jvp->GetJson();

   if (g->Mrr) {    // First argument is a constant
    g->Xchk = jsp;
    JsonMemSave(g);
   } // endif Mrr

  } else
   jsp = (PJSON)g->Xchk;

  // The item to locate
  jvp2 = MakeValue(g, args, 1);

  if (args->arg_count > 2)
   mx = (int)*(long long*)args->args[2];

  jsx = new(g) JSNX(g, jsp, TYPE_STRING);
  path = jsx->LocateAll(g, jsp, jvp2, mx);

  if (initid->const_item)
   // Keep result of constant function
   g->Activityp = (PACTIVITY)path;

 } catch (int n) {
  if (trace(1))
   htrc("Exception %d: %-.256s\n", n, g->Message);

  PUSH_WARNING(g->Message);
  *error = 1;
  path = NULL;
  } catch (const char *msg) {
  snprintf(g->Message, sizeof(g->Message), "%s", 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, uchar *, uchar *error)
{
 unsigned char isn;
 char          res[256];
 unsigned long reslen;

 isn = 0;
 jsonlocate(initid, args, res, &reslen, &isn, error);
 return (isn) ? 0LL : 1LL;
} // end of jsoncontains

void jsoncontains_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jsoncontains_deinit

/*********************************************************************************/
/*  Check whether the document contains a path.                                  */
/*********************************************************************************/
my_bool jsoncontains_path_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more = 1024;
 int n = IsJson(args, 0);

 if (args->arg_count < 2) {
  strcpy(message, "At least 2 arguments required");
  return true;
 } else if (!n && args->arg_type[0] != STRING_RESULT) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else if (args->arg_type[1] != STRING_RESULT) {
  strcpy(message, "Second argument is not a string (path)");
  return true;
 } else if (args->arg_count > 2) {
  if (args->arg_type[2] == INT_RESULT && args->args[2])
   more += (unsigned long)*(long long*)args->args[2];
  else
   strcpy(message, "Third argument is not an integer (memory)");

 } // endif's

 CalcLen(args, false, reslen, memlen);
//memlen += more;

 // TODO: calculate this
 more += (IsJson(args, 0) != 3 ? 1000 : 0);

 return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jsoncontains_path_init

long long jsoncontains_path(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *error)
{
 char   *p, *path;
 long long n;
 PJSON   jsp;
 PJSNX   jsx;
 PJVAL   jvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  if (!g->Activityp) {
   return 0LL;
  } else
   return *(long long*)g->Activityp;

 } else if (initid->const_item)
  g->N = 1;

 if (!g->Xchk) {
  if (CheckMemory(g, initid, args, 1, true)) {
   PUSH_WARNING("CheckMemory error");
   goto err;
  } else
   jvp = MakeValue(g, args, 0);

  if ((p = jvp->GetString(g))) {
   if (!(jsp = ParseJson(g, p, strlen(p)))) {
    PUSH_WARNING(g->Message);
    goto err;
   } // endif jsp

  } else
   jsp = jvp->GetJson();

  if (g->Mrr) {    // First argument is a constant
   g->Xchk = jsp;
   JsonMemSave(g);
  } // endif Mrr

 } else
  jsp = (PJSON)g->Xchk;

 path = MakePSZ(g, args, 1);
 jsx = JsnxNew(g, jsp, TYPE_BIGINT);

 if (!jsx || jsx->SetJpath(g, path)) {
  PUSH_WARNING(g->Message);
  goto err;
 } // endif SetJpath

 n = (jsx->CheckPath(g)) ? 1LL : 0LL;

 if (initid->const_item) {
  // Keep result of constant function
  long long *np = (long long*)PlgDBSubAlloc(g, NULL, sizeof(long long));

  if (np) {
   *np = n;
   g->Activityp = (PACTIVITY)np;
  } else
   PUSH_WARNING(g->Message);

 } // endif const_item

 return n;

 err:
 if (g->Mrr) *error = 1;
 return 0LL;
} // end of jsoncontains_path

void jsoncontains_path_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jsoncontains_path_deinit

/*********************************************************************************/
/*  This function is used by the json_set/insert/update_item functions.          */
/*********************************************************************************/
char *handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 char   *p, *path, *str = NULL;
 int     w;
 my_bool b = true;
 PJSON   jsp;
 PJSNX   jsx;
 PJVAL   jvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PGLOBAL gb = GetMemPtr(g, args, 0);

 if (g->Alchecked) {
  str = (char*)g->Activityp;
  goto fin;
 } else if (g->N)
  g->Alchecked = 1;

 if (!strcmp(result, "$set"))
  w = 0;
 else if (!strcmp(result, "$insert"))
  w = 1;
 else if (!strcmp(result, "$update"))
  w = 2;
 else {
  PUSH_WARNING("Logical error, please contact CONNECT developer");
  goto fin;
 } // endelse

 try {
  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, true, false, true)) {
    PUSH_WARNING("CheckMemory error");
    throw 1;
   } else
    jvp = MakeValue(g, args, 0);

   if ((p = jvp->GetString(g))) {
    if (!(jsp = ParseJson(g, p, strlen(p)))) {
     throw 2;
    } // endif jsp

   } else
    jsp = jvp->GetJson();

   if (g->Mrr) {    // First argument is a constant
    g->Xchk = jsp;
    JsonMemSave(g);
   } // endif Mrr

  } else
   jsp = (PJSON)g->Xchk;

  jsx = new(g)JSNX(g, jsp, TYPE_STRING, initid->max_length, 0, true);

  for (uint i = 1; i + 1 < args->arg_count; i += 2) {
   jvp = MakeValue(gb, args, i);
   path = MakePSZ(g, args, i + 1);

   if (jsx->SetJpath(g, path, false)) {
    PUSH_WARNING(g->Message);
    continue;
   } // endif SetJpath

   if (w) {
    jsx->ReadValue(g);
    b = jsx->GetValue()->IsNull();
    b = (w == 1) ? b : !b;
   } // endif w

   if (b && jsx->WriteValue(gb, jvp))
    PUSH_WARNING(g->Message);

  } // endfor i

  // In case of error or file, return unchanged argument
  if (!(str = MakeResult(g, args, jsp, INT_MAX32)))
   str = MakePSZ(g, args, 0);

  if (g->N)
   // Keep result of constant function
   g->Activityp = (PACTIVITY)str;

 } catch (int n) {
   if (trace(1))
    htrc("Exception %d: %-.256s\n", n, g->Message);

  PUSH_WARNING(g->Message);
  str = NULL;
 } catch (const char *msg) {
   snprintf(g->Message, sizeof(g->Message), "%s", 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, uchar *is_null, uchar *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, uchar *is_null, uchar *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, uchar *is_null, uchar *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)) {
   snprintf(message, MYSQL_ERRMSG_SIZE, "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, uchar *is_null, uchar *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 (!fn) {
  PUSH_WARNING("Missing file name");
  *is_null = 1;
  return NULL;
 } else if (!connect_can_access_file(fn)) {
  PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
  *is_null = 1;
  return NULL;
 }

 if (args->arg_count > 1) {
  int    pretty = 3, pty = 3;
  size_t len;
  PJSON  jsp;
  PJVAL  jvp = NULL;

  for (unsigned int i = 1; i < args->arg_count; i++)
   if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) {
    pretty = (int) * (longlong*)args->args[i];
    break;
   } // endif type

  /*******************************************************************************/
  /*  Parse the json file and allocate its tree structure.                       */
  /*******************************************************************************/
  if (!(jsp = ParseJsonFile(g, fn, &pty, len))) {
   PUSH_WARNING(g->Message);
   str = NULL;
   goto fin;
  } // endif jsp

  if (pty == 3)
   PUSH_WARNING("File pretty format cannot be determined");
  else if (pretty != 3 && pty != pretty)
   PUSH_WARNING("File pretty format doesn't match the specified pretty value");
  else if (pretty == 3)
   pretty = pty;

  // Check whether a path was specified
  if (CheckPath(g, args, jsp, jvp, 1)) {
   PUSH_WARNING(g->Message);
   str = NULL;
   goto fin;
  } else if (jvp)
   jsp = jvp->GetJson();

  if (!(str = Serialize(g, jsp, NULL, 0)))
   PUSH_WARNING(g->Message);

 } else
  if (!(str = GetJsonFile(g, fn)))
   PUSH_WARNING(g->Message);

 if (initid->const_item)
  // Keep result of constant function
  g->Xchk = str;

 fin:
 if (!str) {
  *res_length = 0;
  *is_null = 1;
 } else
  *res_length = strlen(str);

 return str;
} // end of json_file

void json_file_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_file_deinit

/*********************************************************************************/
/*  Make a json file from a json item.                                           */
/*********************************************************************************/
my_bool jfile_make_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 1 || args->arg_count > 3) {
  strcpy(message, "Wrong number of arguments");
  return true;
 } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
  strcpy(message, "First argument must be a json item");
  return true;
 } // endif

 CalcLen(args, false, reslen, memlen);
 memlen = memlen + 5000;  // To take care of not pretty files 
 return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jfile_make_init

char *jfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *)
{
 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 (!connect_can_access_file(fn)) {
   PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
   fn = NULL;
  } else 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, uchar *is_null, uchar *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))) {
    safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");

    for (uint i = 0; i < args->arg_count; i++)
     arp->AddArrayValue(g, MakeValue(g, args, i));

    arp->InitArray(g);
   } // endif arp && bsp

  } else
   bsp = NULL;

  if (!bsp && (bsp = JbinAlloc(g, args, initid->max_length, NULL)))
   strmake(bsp->Msg, g->Message, BMX-1);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? bsp : NULL;
 } // endif bsp

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_array

void jbin_array_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_array_deinit

/*********************************************************************************/
/*  Add one or several values to a Json array.                                   */
/*********************************************************************************/
my_bool jbin_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 } else
  CalcLen(args, false, reslen, memlen);

 return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jbin_array_add_values_init

char *jbin_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (!bsp || bsp->Changed) {
  if (!CheckMemory(g, initid, args, args->arg_count, true)) {
   PJSON   top;
   PJAR    arp;
   PJVAL   jvp = MakeTypedValue(g, args, 0, TYPE_JAR, &top);
   PGLOBAL gb = GetMemPtr(g, args, 0);

   if (jvp->GetValType() != TYPE_JAR) {
    if ((arp = (PJAR)JsonNew(gb, TYPE_JAR))) {
     arp->AddArrayValue(gb, jvp);
     top = arp;
    } // endif arp

   } else
    arp = jvp->GetArray();

   for (uint i = 1; i < args->arg_count; i++)
    arp->AddArrayValue(gb, MakeValue(gb, args, i));

   arp->InitArray(gb);

   if ((bsp = JbinAlloc(g, args, initid->max_length, top))) {
    safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");
    bsp->Jsp = arp;
   } // endif bsp

  } else
   if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
    strmake(bsp->Msg, g->Message, BMX-1);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? bsp : NULL;
 } // endif bsp

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_array_add_values

void jbin_array_add_values_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_array_add_values_deinit

/*********************************************************************************/
/*  Add one value to a Json array.                                               */
/*********************************************************************************/
my_bool jbin_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 //} else if (!IsJson(args, 0)) {
 // strcpy(message, "First argument must be a json item");
 // return true;
 } else
  CalcLen(args, false, reslen, memlen, true);

 return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jbin_array_add_init

char *jbin_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 int     n = 2;
 PJSON   top = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (bsp && !bsp->Changed) {
  // This constant function was recalled
  *res_length = sizeof(BSON);
  return (char*)bsp;
 } // endif bsp

 if (!CheckMemory(g, initid, args, 2, false, false, true)) {
  int  *x = NULL;
  uint n = 2;
// PJSON jsp;
  PJVAL jvp;
  PJAR  arp;

  jvp = MakeTypedValue(g, args, 0, TYPE_JSON, &top);
  // jsp = jvp->GetJson();
  x = GetIntArgPtr(g, args, n);

  if (CheckPath(g, args, top, jvp, n))
   PUSH_WARNING(g->Message);
  else if (jvp) {
   PGLOBAL gb = GetMemPtr(g, args, 0);

   if (jvp->GetValType() != TYPE_JAR) {
    if ((arp = (PJAR)JsonNew(gb, TYPE_JAR))) {
     arp->AddArrayValue(gb, (PJVAL)JvalNew(gb, TYPE_JVAL, jvp));
     jvp->SetValue(arp);

     if (!top)
      top = arp;

    } // endif arp

   } else
    arp = jvp->GetArray();

   arp->AddArrayValue(gb, MakeValue(gb, args, 1), x);
   arp->InitArray(gb);
  } else {
   PUSH_WARNING("First argument target is not an array");
// if (g->Mrr) *error = 1;  (only if no path)
  } // endif jvp

 } // endif CheckMemory

 // In case of error unchanged argument will be returned
 bsp = MakeBinResult(g, args, top, initid->max_length, n);

 if (initid->const_item)
  // Keep result of constant function
  g->Xchk = bsp;

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_array_add

void jbin_array_add_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_array_add_deinit

/*********************************************************************************/
/*  Delete a value from a Json array.                                            */
/*********************************************************************************/
my_bool jbin_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 } else
  CalcLen(args, false, reslen, memlen, true);

 return JsonInit(initid, args, message, true, reslen, memlen);
 } // end of jbin_array_delete_init

char *jbin_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *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, uchar *is_null, uchar *error)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (!bsp || bsp->Changed) {
  if (!CheckMemory(g, initid, args, args->arg_count, true)) {
   PJOB objp;

   if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     objp->SetKeyValue(g, MakeValue(g, args, i), MakeKey(g, args, i));


    if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
     safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");

   } else
    bsp = NULL;

  } else
   if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
    strmake(bsp->Msg, g->Message, BMX-1);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? bsp : NULL;
 } // endif bsp

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_object

void jbin_object_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_object_deinit

/*********************************************************************************/
/*  Make a Json Object containing all not null parameters.                       */
/*********************************************************************************/
my_bool jbin_object_nonull_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 CalcLen(args, true, reslen, memlen);
 return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jbin_object_nonull_init

char *jbin_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (!bsp || bsp->Changed) {
  if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
   PJVAL jvp;
   PJOB  objp;

   if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     if (!(jvp = MakeValue(g, args, i))->IsNull())
      objp->SetKeyValue(g, jvp, MakeKey(g, args, i));

    if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
     safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");

   } else
    bsp = NULL;

  } else
   if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
    strmake(bsp->Msg, g->Message, BMX-1);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? bsp : NULL;
 } // endif bsp

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_object_nonull

void jbin_object_nonull_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_object_nonull_deinit

/*********************************************************************************/
/*  Make a Json Object containing all the key/value parameters.                  */
/*********************************************************************************/
my_bool jbin_object_key_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count % 2) {
  strcpy(message, "This function must have an even number of arguments");
  return true;
 } // endif arg_count

 CalcLen(args, true, reslen, memlen);
 return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jbin_object_key_init

char *jbin_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (!bsp || bsp->Changed) {
  if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
   PJOB objp;

   if ((objp = (PJOB)JsonNew(g, TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i += 2)
     objp->SetKeyValue(g, MakeValue(g, args, i + 1), MakePSZ(g, args, i));

    if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
     safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");

   } else
    bsp = NULL;

  } else
   if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
    strmake(bsp->Msg, g->Message, BMX-1);

  // Keep result of constant function
  g->Xchk = (initid->const_item) ? bsp : NULL;
 } // endif bsp

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_object_key

void jbin_object_key_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_object_key_deinit

/*********************************************************************************/
/*  Add or replace a value in a Json Object.                                     */
/*********************************************************************************/
my_bool jbin_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
 } else if (!IsJson(args, 0)) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else
  CalcLen(args, true, reslen, memlen, true);

 return JsonInit(initid, args, message, true, reslen, memlen);
 } // end of jbin_object_add_init

char *jbin_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PJSON   top = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (bsp && !bsp->Changed) {
  // This constant function was recalled
  bsp = (PBSON)g->Xchk;
  *res_length = sizeof(BSON);
  return (char*)bsp;
 } // endif bsp

 if (!CheckMemory(g, initid, args, 2, false, true, true)) {
  PCSZ  key;
  PJOB  jobp;
  PJVAL jvp = MakeValue(g, args, 0, &top);
  PJSON jsp = jvp->GetJson();

  if (CheckPath(g, args, jsp, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp && jvp->GetValType() == TYPE_JOB) {
   PGLOBAL gb = GetMemPtr(g, args, 0);

   jobp = jvp->GetObject();
   jvp = MakeValue(gb, args, 1);
   key = MakeKey(gb, args, 1);
   jobp->SetKeyValue(gb, jvp, key);
  } else {
   PUSH_WARNING("First argument target is not an object");
// if (g->Mrr) *error = 1;  (only if no path)
  } // endif jvp

 } // endif CheckMemory

 // In case of error unchanged argument will be returned
 bsp = MakeBinResult(g, args, top, initid->max_length);

 if (initid->const_item)
  // Keep result of constant function
  g->Xchk = bsp;

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_object_add

void jbin_object_add_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_object_add_deinit

/*********************************************************************************/
/*  Delete a value from a Json object.                                           */
/*********************************************************************************/
my_bool jbin_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  strcpy(message, "This function must have 2 or 3 arguments");
  return true;
 } else if (!IsJson(args, 0)) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else if (args->arg_type[1] != STRING_RESULT) {
  strcpy(message, "Second argument must be a key string");
  return true;
 } else
  CalcLen(args, true, reslen, memlen, true);

 return JsonInit(initid, args, message, true, reslen, memlen);
 } // end of jbin_object_delete_init

char *jbin_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PJSON   top = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (bsp && !bsp->Changed) {
  // This constant function was recalled
  bsp = (PBSON)g->Xchk;
  *res_length = sizeof(BSON);
  return (char*)bsp;
 } // endif bsp

 if (!CheckMemory(g, initid, args, 1, false, true, true)) {
  PCSZ  key;
  PJOB  jobp;
  PJVAL jvp = MakeValue(g, args, 0, &top);
  (void) jvp->GetJson();          // XXX Should be removed?

  if (CheckPath(g, args, top, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp && jvp->GetValType() == TYPE_JOB) {
   key = MakeKey(g, args, 1);
   jobp = jvp->GetObject();
   jobp->DeleteKey(key);
  } else {
   PUSH_WARNING("First argument target is not an object");
// if (g->Mrr) *error = 1; (only if no path)
  } // endif jvp

 } // endif CheckMemory

 // In case of error unchanged argument will be returned
 bsp = MakeBinResult(g, args, top, initid->max_length);

 if (initid->const_item)
  // Keep result of constant function
  g->Xchk = bsp;

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_object_delete

void jbin_object_delete_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_object_delete_deinit

/*********************************************************************************/
/*  Returns an array of the Json object keys.                                    */
/*********************************************************************************/
my_bool jbin_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return json_object_list_init(initid, args, message);
} // end of jbin_object_list_init

char *jbin_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *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)))
   safe_strcat(bsp->Msg, sizeof(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, uchar *is_null, uchar *error)
{
 char   *path;
 PJSON   jsp;
 PJSNX   jsx;
 PJVAL   jvp;
 PBSON   bsp = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->N) {
  bsp = (PBSON)g->Activityp;
  goto fin;
 } else if (initid->const_item)
  g->N = 1;

 if (!g->Xchk) {
  if (CheckMemory(g, initid, args, 1, true, true)) {
   PUSH_WARNING("CheckMemory error");
   goto fin;
  } // endif CheckMemory

  jvp = MakeTypedValue(g, args, 0, TYPE_JSON);
  jsp = jvp->GetJson();

  if (g->Mrr) {    // First argument is a constant
   g->Xchk = jsp;
   JsonMemSave(g);
  } // endif Mrr

 } else
  jsp = (PJSON)g->Xchk;

 path = MakePSZ(g, args, 1);
 jsx = JsnxNew(g, jsp, TYPE_STRING, initid->max_length);

 if (!jsx || jsx->SetJpath(g, path, false)) {
  PUSH_WARNING(g->Message);
  goto fin;
 } // endif SetJpath

 // Get the json tree
 if ((jvp = jsx->GetRowValue(g, jsp, 0, false))) {
  jsp = (jvp->GetJsp()) ? jvp->GetJsp() : JvalNew(g, TYPE_JVAL, jvp->GetValue(g));

  if ((bsp = JbinAlloc(g, args, initid->max_length, jsp)))
   safe_strcat(bsp->Msg, sizeof(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, uchar *is_null, uchar *error)
{
 PJSON   top = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (bsp && !bsp->Changed) {
  // This constant function was recalled
  *res_length = sizeof(BSON);
  return (char*)bsp;
 } // endif bsp

 if (!CheckMemory(g, initid, args, 2, false, false, true)) {
  PJVAL   jvp;
  PJSON   jsp[2] = {NULL, NULL};
  PGLOBAL gb = GetMemPtr(g, args, 0);

  for (int i = 0; i < 2; i++) {
   jvp = MakeValue(g, args, i);
   if (!i) top = jvp->GetJson();

   if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) {
    snprintf(g->Message, sizeof(g->Message), "Argument %d is not an array or object", i);
    PUSH_WARNING(g->Message);
   } else
    jsp[i] = jvp->GetJsp();

  } // endfor i

  if (jsp[0] && jsp[0]->Merge(gb, jsp[1]))
   PUSH_WARNING(gb->Message);

 } // endif CheckMemory

 // In case of error unchanged first argument will be returned
 bsp = MakeBinResult(g, args, top, initid->max_length);

 if (initid->const_item)
  // Keep result of constant function
  g->Xchk = bsp;

 if (!bsp) {
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else
  *res_length = sizeof(BSON);

 return (char*)bsp;
} // end of jbin_item_merge

void jbin_item_merge_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jbin_item_merge_deinit

/*********************************************************************************/
/*  This function is used by the jbin_set/insert/update functions.               */
/*********************************************************************************/
char *bin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *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, uchar *is_null, uchar *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, uchar *is_null, uchar *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, uchar *is_null, uchar *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) {
  strcpy(message, "First argument must be a string (file name)");
  return true;
 } // endifs

 for (unsigned int i = 1; i < args->arg_count; i++) {
  if (!(args->arg_type[i] == INT_RESULT || args->arg_type[i] == STRING_RESULT)) {
   snprintf(message, MYSQL_ERRMSG_SIZE, "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;
 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, uchar *is_null, uchar *error)
{
 char   *fn;
 int     pretty = 3, pty = 3;
 size_t  len = 0;
 PJSON   jsp;
 PJVAL   jvp = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = (PBSON)g->Xchk;

 if (bsp && !bsp->Changed)
  goto fin;

 PlugSubSet(g->Sarea, g->Sarea_Size);
 g->Xchk = NULL;
 fn = MakePSZ(g, args, 0);

 if (!fn) {
  PUSH_WARNING("Missing file name");
  *error = 1;
  goto fin;
 } else if (!connect_can_access_file(fn)) {
  PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
  *error = 1;
  goto fin;
 }

 for (unsigned int i = 1; i < args->arg_count; i++)
  if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) {
   pretty = (int) * (longlong*)args->args[i];
   break;
  } // endif type

 /*********************************************************************************/
 /*  Parse the json file and allocate its tree structure.                         */
 /*********************************************************************************/
 if (!(jsp = ParseJsonFile(g, fn, &pty, len))) {
  PUSH_WARNING(g->Message);
  *error = 1;
  goto fin;
 } // endif jsp

 if (pty == 3)
  PUSH_WARNING("File pretty format cannot be determined");
 else if (pretty != 3 && pty != pretty)
  PUSH_WARNING("File pretty format doesn't match the specified pretty value");
 else if (pretty == 3)
  pretty = pty;

 if ((bsp = JbinAlloc(g, args, len, jsp))) {
  safe_strcat(bsp->Msg, sizeof(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, uchar *, uchar *error)
{
 char   *str;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (!g->Xchk) {
  if (IsJson(args, 0) == 3) {
   PBSON bsp = (PBSON)args->args[0];

   JsonSubSet(g);

   if (!(str = Serialize(g, bsp->Jsp, NULL, 0)))
    str = strcpy(result, g->Message);

   // Keep result of constant function
   g->Xchk = (initid->const_item) ? str : NULL;
  } else {
   // *error = 1;
   str = strcpy(result, "Argument is not a Jbin tree");
  } // endif

 } else
  str = (char*)g->Xchk;

 *res_length = strlen(str);
 return str;
} // end of json_serialize

void json_serialize_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_serialize_deinit

/*********************************************************************************/
/*  Convert a prettiest Json file to Pretty=0.                                   */
/*********************************************************************************/
my_bool jfile_convert_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
 unsigned long reslen, memlen;

 if (args->arg_count != 3) {
  strcpy(message, "This function must have 3 arguments");
  return true;
 } else if (args->arg_type[2] != INT_RESULT) {
  strcpy(message, "Third Argument must be an integer (LRECL)");
  return true;
 } else for (int i = 0; i < 2; i++)
  if (args->arg_type[i] != STRING_RESULT) {
   snprintf(message, MYSQL_ERRMSG_SIZE, "Arguments %d must be a string (file name)", i+1);
   return true;
  } // endif args

 CalcLen(args, false, reslen, memlen);
 return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jfile_convert_init

char *jfile_convert(UDF_INIT* initid, UDF_ARGS* args, char* result,
                   unsigned long *res_length, uchar *is_null, uchar *error) {
 char   *str, *fn, *ofn;
 int     lrecl = (int)*(longlong*)args->args[2];
 PGLOBAL g = (PGLOBAL)initid->ptr;

 PlugSubSet(g->Sarea, g->Sarea_Size);
 fn = MakePSZ(g, args, 0);
 ofn = MakePSZ(g, args, 1);

 if (!g->Xchk) {
  JUP* jup = new(g) JUP(g);

  str = jup->UnprettyJsonFile(g, fn, ofn, lrecl);
  g->Xchk = str;
 } else
  str = (char*)g->Xchk;

 if (!str) {
  PUSH_WARNING(g->Message[0] != '\0' ? g->Message : "Unexpected error");
  *is_null = 1;
  *error = 1;
  *res_length = 0;
 } else {
  strcpy(result, str);
  *res_length = strlen(str);
 } // endif str

 return str;
} // end of jfile_convert

void jfile_convert_deinit(UDF_INIT* initid) {
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jfile_convert_deinit

/*********************************************************************************/
/*  Convert a prettiest Json file to Pretty=0.                                   */
/*********************************************************************************/
my_bool jfile_bjson_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
 unsigned long reslen, memlen;

 if (args->arg_count != 2 && args->arg_count != 3) {
  strcpy(message, "This function must have 2 or 3 arguments");
  return true;
 } else if (args->arg_count == 3 && args->arg_type[2] != INT_RESULT) {
  strcpy(message, "Third Argument must be an integer (LRECL)");
  return true;
 } else for (int i = 0; i < 2; i++)
  if (args->arg_type[i] != STRING_RESULT) {
   snprintf(message, MYSQL_ERRMSG_SIZE, "Arguments %d must be a string (file name)", i + 1);
   return true;
  } // endif args

 CalcLen(args, false, reslen, memlen);
 memlen = memlen * M;
 memlen += (args->arg_count == 3) ? (ulong)*(longlong*)args->args[2] : 1024;
 return JsonInit(initid, args, message, false, reslen, memlen);
} // end of jfile_bjson_init

char *jfile_bjson(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *, uchar *error) {
 char   *fn, *ofn, *buf, *str = NULL;
 bool    loop;
 ssize_t len, newloc;
 size_t  lrecl, *binszp;
 PJSON  jsp;
 SWAP   *swp;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 PlugSubSet(g->Sarea, g->Sarea_Size);
 fn = MakePSZ(g, args, 0);
 ofn = MakePSZ(g, args, 1);

 if (args->arg_count == 3)
  lrecl = (size_t)*(longlong*)args->args[2];
 else
  lrecl = 1024;

 if (!g->Xchk) {
  int  msgid = MSGID_OPEN_MODE_STRERROR;
  FILE *fout = NULL;
  FILE *fin;

  if (!(fin = global_fopen(g, msgid, fn, "rt")))
   str = strcpy(result, g->Message);
  else if (!(fout = global_fopen(g, msgid, ofn, "wb")))
   str = strcpy(result, g->Message);
  else if ((buf = (char*)PlgDBSubAlloc(g, NULL, lrecl)) &&
       (binszp = (size_t*)PlgDBSubAlloc(g, NULL, sizeof(size_t)))) {
   JsonMemSave(g);

   try {
    do {
     loop = false;
     JsonSubSet(g);

     if (!fgets(buf, lrecl, fin)) {
      if (!feof(fin)) {
       snprintf(g->Message, sizeof(g->Message), "Error %d reading %zu bytes from %s", errno, lrecl, fn);
       str = strcpy(result, g->Message);
      } else
       str = strcpy(result, ofn);

     } else if ((len = strlen(buf))) {
      if ((jsp = ParseJson(g, buf, len))) {
       newloc = (size_t)PlugSubAlloc(g, NULL, 0);
       *binszp = newloc - (size_t)jsp;

       swp = new(g) SWAP(g, jsp);
       swp->SwapJson(jsp, true);

       if (fwrite(binszp, sizeof(binszp), 1, fout) != 1) {
        snprintf(g->Message, sizeof(g->Message), "Error %d writing %zu bytes to %s",
                  errno, sizeof(binszp), ofn);
        str = strcpy(result, g->Message);
       } else if (fwrite(jsp, *binszp, 1, fout) != 1) {
        snprintf(g->Message, sizeof(g->Message), "Error %d writing %zu bytes to %s",
                  errno, *binszp, ofn);
        str = strcpy(result, g->Message);
       } else
        loop = true;

      } else {
       str = strcpy(result, g->Message);
      } // endif jsp

     } else
      loop = true;

    } while (loop);

   } catch (int) {
    str = strcpy(result, g->Message);
   } catch (const char* msg) {
    str = strcpy(result, msg);
   } // end catch

  } else
   str = strcpy(result, g->Message);

  if (fin) fclose(fin);
  if (fout) fclose(fout);
  g->Xchk = str;
 } else
  str = (char*)g->Xchk;

 if (!str) {
  if (g->Message[0] != '\0')
   str = strcpy(result, g->Message);
  else
   str = strcpy(result, "Unexpected error");

 } // endif str

 *res_length = strlen(str);
 return str;
} // end of jfile_bjson

void jfile_bjson_deinit(UDF_INIT* initid) {
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of jfile_bjson_deinit

/* --------------------------------- Class JUP --------------------------------- */

#define ARGS       MY_MIN(24,(int)len-i),s+MY_MAX(i-3,0)

/*********************************************************************************/
/*  JUP public constructor.                                                      */
/*********************************************************************************/
JUP::JUP(PGLOBAL g) {
 fs = NULL;
 s = buff = NULL;
 len = 0;
 k = recl = 0;
 i = 0;
} // end of JUP constructor

/*********************************************************************************/
/*  Copy a json file to another with pretty = 0.                                 */
/*********************************************************************************/
char* JUP::UnprettyJsonFile(PGLOBAL g, char *fn, char *outfn, int lrecl) {
 char   *ret = NULL;
 HANDLE  hFile;
 MEMMAP  mm;

 /*******************************************************************************/
 /*  Create the mapping file object.                                            */
 /*******************************************************************************/
 hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);

 if (hFile == INVALID_HANDLE_VALUE) {
  DWORD rc = GetLastError();

  if (!(*g->Message))
   snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR), "map", (int)rc, fn);

  return NULL;
 } // endif hFile

 /*******************************************************************************/
 /*  Get the file size (assuming file is smaller than 4 GB)                     */
 /*******************************************************************************/
 if (!mm.lenL && !mm.lenH) {              // Empty or deleted file
  CloseFileHandle(hFile);
  return NULL;
 } else {
  len = (size_t)mm.lenL;

  if (mm.lenH)
   len += ((size_t)mm.lenH * 0x000000001LL);

 } // endif size

 if (!mm.memory) {
  CloseFileHandle(hFile);
  snprintf(g->Message, sizeof(g->Message), MSG(MAP_VIEW_ERROR), fn, GetLastError());
  return NULL;
 } else
  s = (char*)mm.memory;

 CloseFileHandle(hFile);                    // Not used anymore

 /*********************************************************************************/
 /*  Parse the json file and allocate its tree structure.                         */
 /*********************************************************************************/
 if (!(fs = fopen(outfn, "wb"))) {
  snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR)": %s",
   "w", (int)errno, outfn, strerror(errno));
  CloseMemMap(mm.memory, len);
  return NULL;
 } // endif fs

 g->Message[0] = 0;

 if (!unPretty(g, lrecl))
  ret = outfn;

 CloseMemMap(mm.memory, len);
 fclose(fs);
 return ret;
} // end of UnprettyJsonFile

/***********************************************************************/
/* Translate a json file to pretty = 0.                                */
/***********************************************************************/
bool JUP::unPretty(PGLOBAL g, int lrecl) {
 bool  go, next, rc = false;

 if (trace(1))
  htrc("UnPretty: s=%.10s len=%zd lrecl=%d\n", s, len, lrecl);

 if (!s || !len) {
  snprintf(g->Message, sizeof(g->Message), "Void JSON file");
  return true;
 } else if (*s != '[') {
  // strcpy(g->Message, "JSON file is not an array");
  s = strchr(s, '[');
  // return true;
 } // endif s

 i = 1;
 go = next = true;

 try {
  // Allocate the record
  buff = (char*)PlugSubAlloc(g, NULL, (size_t)lrecl + 3);
  recl = lrecl;

  do {
   for (k = 0; go && i < len; i++)
    switch (s[i]) {
    case '{':
     buff[k++] = s[i++];
     CopyObject(g);
     break;
    case '[':
     throw "JSON file is not an array of objects";
     break;
    case ' ':
    case '\t':
    case '\n':
    case '\r':
     break;
    case ',':
     go = false;
     break;
    case ']':
     go = next = false;
     break;
    default:
     snprintf(g->Message, sizeof(g->Message), "Unexpected '%c' near %.*s", s[i], ARGS);
     throw 4;
     break;
    }; // endswitch s[i]

   // Write the record
#ifdef __win_
   buff[k++] = '\r';
#endif
   buff[k++] = '\n';
   buff[k] = 0;

   if ((fputs(buff, fs)) == EOF) {
    snprintf(g->Message, sizeof(g->Message), MSG(FPUTS_ERROR), strerror(errno));
    throw 5;
   } // endif EOF

   go = true;
  } while (next);

 } catch (int n) {
  if (trace(1))
   htrc("Exception %d: %s\n", n, g->Message);
  rc = true;
 } catch (const char* msg) {
  snprintf(g->Message, sizeof(g->Message), "%s", msg);
  rc = true;
 } // end catch

 return rc;
} // end of unPretty

/***********************************************************************/
/* Copy a JSON Object.                                                 */
/***********************************************************************/
void JUP::CopyObject(PGLOBAL g) {
 int level = 0;

 for (; i < len; i++)
  switch (s[i]) {
  case '"':
   AddBuff(s[i++]);

   if (level < 2) {
    CopyString(g);
    level = 1;
   } else {
    snprintf(g->Message, sizeof(g->Message), "misplaced string near %.*s", ARGS);
    throw 3;
   } // endif level

   break;
  case ':':
   AddBuff(s[i++]);

   if (level == 1) {
    CopyValue(g);
    level = 2;
   } else {
    snprintf(g->Message, sizeof(g->Message), "Unexpected ':' near %.*s", ARGS);
    throw 3;
   } // endif level

   break;
  case ',':
   AddBuff(s[i]);

   if (level < 2) {
    snprintf(g->Message, sizeof(g->Message), "Unexpected ',' near %.*s", ARGS);
    throw 3;
   } else
    level = 0;

   break;
  case '}':
   AddBuff(s[i]);

   if (level == 1) {
    snprintf(g->Message, sizeof(g->Message), "Unexpected '}' near %.*s", ARGS);
    throw 3;
   } // endif level

   return;
  case '\n':
  case '\r':
  case ' ':
  case '\t':
   break;
  default:
   snprintf(g->Message, sizeof(g->Message), "Unexpected character '%c' near %.*s", s[i], ARGS);
   throw 3;
  }; // endswitch s[i]

 throw "Unexpected EOF in Object";
} // end of CopyObject

/***********************************************************************/
/* Copy a JSON Array.                                                  */
/***********************************************************************/
void JUP::CopyArray(PGLOBAL g) {
 int level = 0;

 for (; i < len; i++)
  switch (s[i]) {
  case ',':
   if (level < 2) {
    snprintf(g->Message, sizeof(g->Message), "Unexpected ',' near %.*s", ARGS);
    throw 2;
   } else
    level = 1;

   AddBuff(s[i]);
   break;
  case ']':
   if (level == 1) {
    snprintf(g->Message, sizeof(g->Message), "Unexpected ',]' near %.*s", ARGS);
    throw 2;
   } // endif level

   AddBuff(s[i]);
   return;
  case '\n':
  case '\r':
  case ' ':
  case '\t':
   break;
  default:
   if (level == 2) {
    snprintf(g->Message, sizeof(g->Message), "Unexpected value near %.*s", ARGS);
    throw 2;
   } // endif level

   CopyValue(g);
   level = 2;
   break;
  }; // endswitch s[i]

 throw "Unexpected EOF in array";
} // end of CopyArray

/***********************************************************************/
/* Copy a JSON Value.                                                  */
/***********************************************************************/
void JUP::CopyValue(PGLOBAL g) {
 for (; i < len; i++)
  switch (s[i]) {
  case '\n':
  case '\r':
  case ' ':
  case '\t':
   break;
  default:
   goto suite;
  } // endswitch

suite:
 switch (s[i]) {
 case '[':
  AddBuff(s[i++]);
  CopyArray(g);
  break;
 case '{':
  AddBuff(s[i++]);
  CopyObject(g);
  break;
 case '"':
  AddBuff(s[i++]);
  CopyString(g);
  break;
 case 't':
  if (!strncmp(s + i, "true", 4)) {
   AddBuff(s[i++]);
   AddBuff(s[i++]);
   AddBuff(s[i++]);
   AddBuff(s[i]);
  } else
   goto err;

  break;
 case 'f':
  if (!strncmp(s + i, "false", 5)) {
   AddBuff(s[i++]);
   AddBuff(s[i++]);
   AddBuff(s[i++]);
   AddBuff(s[i++]);
   AddBuff(s[i]);
  } else
   goto err;

  break;
 case 'n':
  if (!strncmp(s + i, "null", 4)) {
   AddBuff(s[i++]);
   AddBuff(s[i++]);
   AddBuff(s[i++]);
   AddBuff(s[i]);
  } else
   goto err;

  break;
 default:
  if (s[i] == '-' || isdigit(s[i]))
   CopyNumeric(g);
  else
   goto err;

 }; // endswitch s[i]

 return;

err:
 snprintf(g->Message, sizeof(g->Message), "Unexpected character '%c' near %.*s", s[i], ARGS);
 throw 1;
} // end of CopyValue

/***********************************************************************/
/*  Unescape and parse a JSON string.                                  */
/***********************************************************************/
void JUP::CopyString(PGLOBAL g) {
 for (; i < len; i++) {
  AddBuff(s[i]);

  switch (s[i]) {
  case '"':
   return;
  case '\\':
   AddBuff(s[++i]);
   break;
  default:
   break;
  }; // endswitch s[i]

 } // endfor i

 throw "Unexpected EOF in String";
} // end of CopyString

/***********************************************************************/
/* Copy a JSON numeric value.                                          */
/***********************************************************************/
void JUP::CopyNumeric(PGLOBAL g) {
 bool  has_dot = false;
 bool  has_e = false;
 bool  found_digit = false;

 for (; i < len; i++) {
  switch (s[i]) {
  case '.':
   if (!found_digit || has_dot || has_e)
    goto err;

   has_dot = true;
   break;
  case 'e':
  case 'E':
   if (!found_digit || has_e)
    goto err;

   has_e = true;
   found_digit = false;
   break;
  case '+':
   if (!has_e)
    goto err;

   // fall through
  case '-':
   if (found_digit)
    goto err;

   break;
  default:
   if (isdigit(s[i])) {
    found_digit = true;
   } else
    goto fin;

  }; // endswitch s[i]

  AddBuff(s[i]);
 } // endfor i

fin:
 if (!found_digit)
  throw "No digit found";
 else
  i--;

 return;

err:
 throw "Unexpected EOF in number";
} // end of CopyNumeric

/*********************************************************************************/
/*  Utility function returning an environment variable value.                    */
/*********************************************************************************/
my_bool envar_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 if (args->arg_count != 1) {
  strcpy(message, "Unique argument must be an environment variable name");
  return true;
 } else {
  initid->maybe_null = true;
  return false;
 } // endif count

} // end of envar_init

char *envar(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *)
{
 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, uchar *is_null, uchar *)
{
 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, uchar *is_null, uchar *)
{
 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

Messung V0.5 in Prozent
C=84 H=94 G=88

¤ Dauer der Verarbeitung: 0.300 Sekunden  (vorverarbeitet am  2026-10-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.