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 163 kB image not shown  

Quelle  bsonudf.cpp   Sprache: C

 

/****************** bsonudf C++ Program Source Code File (.CPP) ******************/
/*  PROGRAM NAME: bsonudf     Version 1.0                                        */
/*  (C) Copyright to the author Olivier BERTRAND          2020 - 2021            */
/*  This program are the BSON 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 <stdio.h>
#include <cassert>

#include "bsonudf.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 6

int  JsonDefPrec = -1;
int  GetDefaultPrec(void);
int  IsArgJson(UDF_ARGS* args, uint i);
void SetChanged(PBSON bsp);
int  GetJsonDefPrec(void);

static PBSON BbinAlloc(PGLOBAL g, ulong len, PBVAL jsp);

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

/*********************************************************************************/
/*  Replaces GetJsonGrpSize not usable when CONNECT is not installed.            */
/*********************************************************************************/
int  GetJsonDefPrec(void) {
 return (JsonDefPrec < 0) ? GetDefaultPrec() : JsonDefPrec;
} /* end of GetJsonDefPrec */

/*********************************************************************************/
/*  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 */

/*********************************************************************************/
/*  Allocate and initialize a BSON structure.                                    */
/*********************************************************************************/
static PBSON BbinAlloc(PGLOBAL g, ulong len, PBVAL jsp)
{
 PBSON bsp = (PBSON)PlgDBSubAlloc(g, NULL, sizeof(BSON));

 if (bsp) {
  snprintf(bsp->Msg, sizeof(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 = (PJSON)jsp;
  bsp->Bsp = NULL;
 } else
  PUSH_WARNING(g->Message);

 return bsp;
} /* end of BbinAlloc */

/* --------------------------- New Testing BJSON Stuff --------------------------*/

/*********************************************************************************/
/*  SubAlloc a new BJNX class with protection against memory exhaustion.         */
/*********************************************************************************/
#ifdef NOT_USED
static PBJNX BjnxNew(PGLOBAL g, PBVAL vlp, int type, int len)
{
 PBJNX bjnx;

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

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

 return bjnx;
} /* end of BjnxNew */
#endif
/* ----------------------------------- BSNX ------------------------------------ */

/*********************************************************************************/
/*  BSNX public constructor.                                                     */
/*********************************************************************************/
BJNX::BJNX(PGLOBAL g) : BDOC(g)
{
 Row = NULL;
 Bvalp = NULL;
 Jpnp = NULL;
 Jp = NULL;
 Nodes = NULL;
 Value = NULL;
 //MulVal = NULL;
 Jpath = NULL;
 Buf_Type = TYPE_STRING;
 Long = len;
 Prec = 0;
 Nod = 0;
 Xnod = -1;
 K = 0;
 I = -1;
 Imax = 9;
 B = 0;
 Xpd = false;
 Parsed = false;
 Found = false;
 Wr = false;
 Jb = false;
 Changed = false;
 Throw = false;
} // end of BJNX constructor

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

/*********************************************************************************/
/*  SetJpath: set and parse the json path.                                       */
/*********************************************************************************/
my_bool BJNX::SetJpath(PGLOBAL g, char* path, my_bool jb)
{
 // Check Value was allocated
 if (Value)
  Value->SetNullable(true);

 Jpath = path;

 // Parse the json path
 Parsed = false;
 Nod = 0;
 Jb = jb;
 return ParseJpath(g);
} // end of SetJpath

/*********************************************************************************/
/*  Analyse array processing options.                                            */
/*********************************************************************************/
my_bool BJNX::SetArrayOptions(PGLOBAL g, char* p, int i)
{
 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 '*': jnp->Op = OP_EXP;  break;
  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 BJNX::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

/*********************************************************************************/
/*  Make a valid key from the passed argument.                                   */
/*********************************************************************************/
PSZ BJNX::MakeKey(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 (IsArgJson(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 NewStr((PSZ)"Key");

   if (!b) {
    p = (PSZ)BsonSubAlloc(n + 1);
    memcpy(p, s, n);
    p[n] = 0;
    return p;
   } // endif b

  } // endif s

  return NewStr((PSZ)s);
 } // endif count

 return NewStr((PSZ)"Key");
} // end of MakeKey

/*********************************************************************************/
/*  MakeJson: Make the Json tree to serialize.                                   */
/*********************************************************************************/
PBVAL BJNX::MakeJson(PGLOBAL g, PBVAL bvp, int n)
{
 PBVAL vlp, jvp = bvp;

 Jb = false;

 if (n < Nod -1) {
  if (bvp->Type == TYPE_JAR) {
   int    ars = GetArraySize(bvp);
   PJNODE jnp = &Nodes[n];

   jvp = NewVal(TYPE_JAR);
   jnp->Op = OP_EQ;

   for (int i = 0; i < ars; i++) {
    jnp->Rank = i;
    vlp = GetRowValue(g, bvp, n);
    AddArrayValue(jvp, DupVal(vlp));
   } // endfor i

   jnp->Op = OP_XX;
   jnp->Rank = 0;
  } else if(bvp->Type == TYPE_JOB) {
   jvp = NewVal(TYPE_JOB);

   for (PBPR prp = GetObject(bvp); prp; prp = GetNext(prp)) {
    vlp = GetRowValue(g, GetVlp(prp), n + 1);
    SetKeyValue(jvp, vlp, MZP(prp->Key));
   } // endfor prp

  } // endif Type

 } // endif n

 Jb = true;
 return jvp;
} // end of MakeJson

/*********************************************************************************/
/*  SetValue: Set a value from a BVALUE contains.                                */
/*********************************************************************************/
void BJNX::SetJsonValue(PGLOBAL g, PVAL vp, PBVAL vlp)
{
 if (vlp) {
  vp->SetNull(false);

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

   break;
  case TYPE_BOOL:
   if (vp->IsTypeNum())
    vp->SetValue(GetInteger(vlp) ? 1 : 0);
   else
    vp->SetValue_psz(GetString(vlp));

   break;
  case TYPE_JAR:
   vp->SetValue_psz(GetArrayText(g, vlp, NULL));
   break;
  case TYPE_JOB:
   vp->SetValue_psz(GetObjectText(g, vlp, NULL));
   break;
  case TYPE_NULL:
   vp->SetNull(true);
                        /* fall through */
  default:
   vp->Reset();
  } // endswitch Type

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

} // end of SetJsonValue

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

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

/*********************************************************************************/
/*  GetColumnValue:                                                              */
/*********************************************************************************/
PVAL BJNX::GetColumnValue(PGLOBAL g, PBVAL row, int i)
{
 PBVAL vlp = GetRowValue(g, row, i);

 SetJsonValue(g, Value, vlp);
 return Value;
} // end of GetColumnValue

/*********************************************************************************/
/*  GetRowValue:                                                                 */
/*********************************************************************************/
PBVAL BJNX::GetRowValue(PGLOBAL g, PBVAL row, int i)
{
 PBVAL   bap;
 PBVAL   vlp = NULL;

 for (; i < Nod && row; i++) {
  if (Nodes[i].Op == OP_NUM) {
   Value->SetValue(row->Type == TYPE_JAR ? GetArraySize(row) : 1);
   vlp = NewVal(Value);
   return vlp;
  } else if (Nodes[i].Op == OP_XX) {
   return MakeJson(g, row, i);
  } else if (Nodes[i].Op == OP_EXP) {
   PUSH_WARNING("Expand not supported by this function");
   return NULL;
  } else switch (row->Type) {
  case TYPE_JOB:
   if (!Nodes[i].Key) {
    // Expected Array was not there
    if (Nodes[i].Op == OP_LE) {
     if (i < Nod - 1)
      continue;
     else
      vlp = row;  // DupVal(g, row) ???

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

   } else
    vlp = GetKeyValue(row, Nodes[i].Key);

   break;
  case TYPE_JAR:
   bap = row;

   if (!Nodes[i].Key) {
    if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
     vlp = GetArrayValue(bap, Nodes[i].Rank);
    else if (Nodes[i].Op == OP_EXP)
     return (PBVAL)ExpandArray(g, bap, i);
    else
     return NewVal(CalculateArray(g, bap, i));

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

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

  row = vlp;
 } // endfor i

 return vlp;
} // end of GetRowValue

/*********************************************************************************/
/*  ExpandArray:                                                                 */
/*********************************************************************************/
PVAL BJNX::ExpandArray(PGLOBAL g, PBVAL arp, int n)
{
 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 BJNX::GetCalcValue(PGLOBAL g, PBVAL bap, 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;
 PBVAL   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 = GetArray(bap); vlp; vlp = GetNext(vlp)) {
     vp = (b && IsJson(vlp)) ? GetRowValue(g, vlp, n + 1) : vlp;

     switch (vp->Type) {
      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:
   DBUG_ASSERT(!"Implement new op type support.");
 } // endswitch Op

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

/*********************************************************************************/
/*  CalculateArray                                                               */
/*********************************************************************************/
PVAL BJNX::CalculateArray(PGLOBAL g, PBVAL bap, int n)
{
 int     i, ars = GetArraySize(bap), nv = 0;
 bool    err;
 OPVAL   op = Nodes[n].Op;
 PVAL    val[2], vp = GetCalcValue(g, bap, n);
 PVAL    mulval = AllocateValue(g, vp);
 PBVAL   bvrp, bvp;
 BVAL    bval;

 vp->Reset();
 xtrc(1, "CalculateArray size=%d op=%d\n", ars, op);

 try {
  for (i = 0; i < ars; i++) {
   bvrp = GetArrayValue(bap, i);
   xtrc(1, "i=%d nv=%d\n", i, nv);

   if (!IsValueNull(bvrp) || (op == OP_CNC && GetJsonNull())) {
    if (IsValueNull(bvrp)) {
     SetString(bvrp, NewStr(GetJsonNull()), 0);
     bvp = bvrp;
    } else if (n < Nod - 1 && IsJson(bvrp)) {
     SetValue(&bval, GetColumnValue(g, bvrp, n + 1));
     bvp = &bval;
    } else
     bvp = bvrp;

    if (trace(1))
     htrc("bvp=%s null=%d\n",
      GetString(bvp), IsValueNull(bvp) ? 1 : 0);

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

    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

 } catch (int n) {
  xtrc(1, "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 vp;
} // end of CalculateArray

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

 for (int i = 0; i < Nod - 1 && row; i++) {
  if (Nodes[i].Op == OP_XX)
   break;
  else if (Nodes[i].Op == OP_EXP) {
   PUSH_WARNING("Expand not supported by this function");
   return NULL;
  } else switch (row->Type) {
  case TYPE_JOB:
   if (!Nodes[i].Key)
    // Expected Array was not there, wrap the value
    continue;

   val = GetKeyValue(row, Nodes[i].Key);
   break;
  case TYPE_JAR:
   arp = row;

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

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

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

  if (val) {
   row = val;
  } else {
   // Construct missing objects
   for (i++; row && i < Nod; i++) {
    if (Nodes[i].Op == OP_XX)
     break;

    // Construct new row
    nwr = NewVal();

    if (row->Type == TYPE_JOB) {
     SetKeyValue(row, MOF(nwr), Nodes[i - 1].Key);
    } else if (row->Type == TYPE_JAR) {
     AddArrayValue(row, MOF(nwr));
    } 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 BJNX::WriteValue(PGLOBAL g, PBVAL jvalp)
{
 PBVAL objp = NULL;
 PBVAL arp = NULL;
 PBVAL jvp = NULL;
 PBVAL row = GetRow(g);

 if (!row)
  return true;

 switch (row->Type) {
 case TYPE_JOB:  objp = row; break;
 case TYPE_JAR:  arp = row;  break;
 case TYPE_JVAL: jvp = MVP(row->To_Val);  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)
    SetArrayValue(arp, jvalp, Nodes[Nod - 1].Rank);
   else
    AddArrayValue(arp, MOF(jvalp));

  } // endif Key

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

 } else if (jvp)
  SetValueVal(jvp, jvalp);

 return false;
} // end of WriteValue

/*********************************************************************************/
/*  GetRowValue:                                                                 */
/*********************************************************************************/
my_bool BJNX::DeleteItem(PGLOBAL g, PBVAL row)
{
 int     n = -1;
 my_bool b = false;
 bool    loop;
 PBVAL   vlp, pvp, rwp;

 do {
  loop = false;
  vlp = NULL;
  pvp = rwp = row;

  for (int i = 0; i < Nod && rwp; i++) {
   if (Nodes[i].Op == OP_XX)
    break;
   else switch (rwp->Type) {
    case TYPE_JOB:
     if (!Nodes[i].Key) {
      vlp = NULL;
     } else
      vlp = GetKeyValue(rwp, Nodes[i].Key);

     break;
    case TYPE_JAR:
     if (!Nodes[i].Key) {
      if (Nodes[i].Op == OP_EXP) {
       if (loop) {
        PUSH_WARNING("Only one expand can be handled");
        return b;
       } // endif loop

       n++;
      } else
       n = Nodes[i].Rank;

      vlp = GetArrayValue(rwp, n);

      if (GetNext(vlp) && Nodes[i].Op == OP_EXP)
       loop = true;

     } else
      vlp = NULL;

     break;
    case TYPE_JVAL:
     vlp = rwp;
     break;
    default:
     vlp = NULL;
   } // endswitch Type

   pvp = rwp;
   rwp = vlp;
   vlp = NULL;
  } // endfor i

  if (rwp) {
   if (Nodes[Nod - 1].Op == OP_XX) {
    if (!IsJson(rwp))
     rwp->Type = TYPE_NULL;

    rwp->To_Val = 0;
   } else switch (pvp->Type) {
    case TYPE_JOB:
     b = DeleteKey(pvp, Nodes[Nod - 1].Key);
     break;
    case TYPE_JAR:
     if (Nodes[Nod - 1].Op == OP_EXP) {
      pvp->To_Val = 0;
      loop = false;
     } else
      b = DeleteValue(pvp, n);

     break;
    default:
     break;
   } // endswitch Type

  } // endif rwp

 } while (loop);

 return b;
} // end of DeleteItem

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

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

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

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

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

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

 } // endfor i

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

/*********************************************************************************/
/*  Check if a path was specified and set jvp according to it.                   */
/*********************************************************************************/
my_bool BJNX::CheckPath(PGLOBAL g, UDF_ARGS *args, PBVAL jsp, PBVAL& 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) {
    Row = jsp;

    if (SetJpath(g, path))
     return true;

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

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

  } // endif type

 jvp = jsp;
 return false;
} // end of CheckPath

/*********************************************************************************/
/*  Locate a value in a JSON tree:                                               */
/*********************************************************************************/
PSZ BJNX::Locate(PGLOBAL g, PBVAL jsp, PBVAL 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('$');
  Bvalp = jvp;
  K = k;

  switch (jsp->Type) {
  case TYPE_JAR:
   err = LocateArray(g, jsp);
   break;
  case TYPE_JOB:
   err = LocateObject(g, jsp);
   break;
  case TYPE_JVAL:
   err = LocateValue(g, MVP(jsp->To_Val));
   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) {
  xtrc(1, "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 BJNX::LocateArray(PGLOBAL g, PBVAL jarp)
{
 char   s[16];
 int    n = GetArraySize(jarp);
 size_t m = Jp->N;

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

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

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

 } // endfor i

 return false;
} // end of LocateArray

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

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

 m = Jp->N;

 for (PBPR pair = GetObject(jobp); pair && !Found; pair = GetNext(pair)) {
  Jp->N = m;

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

  if (LocateValue(g, GetVlp(pair)))
   return true;

 } // endfor i

 return false;
} // end of LocateObject

/*********************************************************************************/
/*  Locate a JSON Value.                                                         */
/*********************************************************************************/
my_bool BJNX::LocateValue(PGLOBAL g, PBVAL jvp)
{
 if (CompareTree(g, Bvalp, jvp))
  Found = (--K == 0);
 else if (jvp->Type == TYPE_JAR)
  return LocateArray(g, jvp);
 else if (jvp->Type == TYPE_JOB)
  return LocateObject(g, jvp);

 return false;
} // end of LocateValue

/*********************************************************************************/
/*  Locate all occurrences of a value in a JSON tree:                            */
/*********************************************************************************/
PSZ BJNX::LocateAll(PGLOBAL g, PBVAL jsp, PBVAL bvp, int mx)
{
 PSZ     str = NULL;
 my_bool 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);
  Bvalp = bvp;
  Imax = mx - 1;
  Jpnp = jnp;
  Jp->WriteChr('[');

  switch (jsp->Type) {
  case TYPE_JAR:
   err = LocateArrayAll(g, jsp);
   break;
  case TYPE_JOB:
   err = LocateObjectAll(g, jsp);
   break;
  case TYPE_JVAL:
   err = LocateValueAll(g, MVP(jsp->To_Val));
   break;
  default:
   err = LocateValueAll(g, jsp);
  } // endswitch Type

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

   Jp->WriteChr(']');
   Jp->WriteChr('\0');
   PlugSubAlloc(g, NULL, Jp->N);
   str = Jp->Strp;
  } else if (!g->Message[0])
   snprintf(g->Message, sizeof(g->Message), "Invalid json tree");

 } catch (int n) {
  xtrc(1, "Exception %d: %s\n", n, g->Message);
  PUSH_WARNING(g->Message);
 } catch (const char* msg) {
  snprintf(g->Message, sizeof(g->Message), "%s", msg);
 } // end catch

 return str;
} // end of LocateAll

/*********************************************************************************/
/*  Locate in a JSON Array.                                                      */
/*********************************************************************************/
my_bool BJNX::LocateArrayAll(PGLOBAL g, PBVAL jarp)
{
 int i = 0;

 if (I < Imax) {
  Jpnp[++I].Type = TYPE_JAR;

  for (PBVAL vp = GetArray(jarp); vp; vp = GetNext(vp)) {
   Jpnp[I].N = i;

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

   i++;
  } // endfor i

  I--;
 } // endif I

 return false;
} // end of LocateArrayAll

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

  for (PBPR pair = GetObject(jobp); pair; pair = GetNext(pair)) {
   Jpnp[I].Key = MZP(pair->Key);

   if (LocateValueAll(g, GetVlp(pair)))
    return true;

  } // endfor i

  I--;
 } // endif I

 return false;
} // end of LocateObjectAll

/*********************************************************************************/
/*  Locate a JSON Value.                                                         */
/*********************************************************************************/
my_bool BJNX::LocateValueAll(PGLOBAL g, PBVAL jvp)
{
 if (CompareTree(g, Bvalp, jvp))
  return AddPath();
 else if (jvp->Type == TYPE_JAR)
  return LocateArrayAll(g, jvp);
 else if (jvp->Type == TYPE_JOB)
  return LocateObjectAll(g, jvp);

 return false;
} // end of LocateValueAll

/*********************************************************************************/
/*  Compare two JSON trees.                                                      */
/*********************************************************************************/
my_bool BJNX::CompareTree(PGLOBAL g, PBVAL jp1, PBVAL jp2)
{
 if (!jp1 || !jp2 || jp1->Type != jp2->Type || GetSize(jp1) != GetSize(jp2))
  return false;

 my_bool found = true;

 if (jp1->Type == TYPE_JAR) {
  for (int i = 0; found && i < GetArraySize(jp1); i++)
   found = (CompareValues(g, GetArrayValue(jp1, i), GetArrayValue(jp2, i)));

 } else if (jp1->Type == TYPE_JOB) {
  PBPR p1 = GetObject(jp1), p2 = GetObject(jp2);

  // Keys can be differently ordered
  for (; found && p1 && p2; p1 = GetNext(p1))
   found = CompareValues(g, GetVlp(p1), GetKeyValue(jp2, GetKey(p1)));

 } else if (jp1->Type == TYPE_JVAL) {
  found = CompareTree(g, MVP(jp1->To_Val), (MVP(jp2->To_Val)));
 } else
  found = CompareValues(g, jp1, jp2);

 return found;
} // end of CompareTree

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

 if (v1 && v2)
  switch (v1->Type) {
  case TYPE_JAR:
  case TYPE_JOB:
   if (v2->Type == v1->Type)
    b = CompareTree(g, v1, v2);

   break;
  case TYPE_STRG:
   if (v2->Type == TYPE_STRG) {
    if (v1->Nd || v2->Nd)  // Case insensitive
     b = (!stricmp(MZP(v1->To_Val), MZP(v2->To_Val)));
    else
     b = (!strcmp(MZP(v1->To_Val), MZP(v2->To_Val)));

   } // endif Type

   break;
  case TYPE_DTM:
   if (v2->Type == TYPE_DTM)
    b = (!strcmp(MZP(v1->To_Val), MZP(v2->To_Val)));

   break;
  case TYPE_INTG:
   if (v2->Type == TYPE_INTG)
    b = (v1->N == v2->N);
   else if (v2->Type == TYPE_BINT)
    b = ((longlong)v1->N == LLN(v2->To_Val));

   break;
  case TYPE_BINT:
   if (v2->Type == TYPE_INTG)
    b = (LLN(v1->To_Val) == (longlong)v2->N);
   else if (v2->Type == TYPE_BINT)
    b = (LLN(v1->To_Val) == LLN(v2->To_Val));

   break;
  case TYPE_FLOAT:
   if (v2->Type == TYPE_FLOAT)
    b = (v1->F == v2->F);
   else if (v2->Type == TYPE_DBL)
    b = ((double)v1->F == DBL(v2->To_Val));

   break;
  case TYPE_DBL:
   if (v2->Type == TYPE_DBL)
    b = (DBL(v1->To_Val) == DBL(v2->To_Val));
   else if (v2->Type == TYPE_FLOAT)
    b = (DBL(v1->To_Val) == (double)v2->F);

   break;
  case TYPE_BOOL:
   if (v2->Type == TYPE_BOOL)
    b = (v1->B == v2->B);

   break;
  case TYPE_NULL:
   b = (v2->Type == TYPE_NULL);
   break;
  default:
   break;
  } // endswitch Type

 else
  b = (!v1 && !v2);

 return b;
} // end of CompareValues

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

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

 for (int i = 0; i <= I; i++) {
  if (Jpnp[i].Type == TYPE_JAR) {
   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

/*********************************************************************************/
/*  Make a JSON value from the passed argument.                                  */
/*********************************************************************************/
PBVAL BJNX::MakeValue(UDF_ARGS *args, uint i, bool b, PBVAL *top)
{
 char *sap = (args->arg_count > i) ? args->args[i] : NULL;
 int   n, len;
 int   ci;
 long long bigint;
 PGLOBAL& g = G;
 PBVAL jvp = NewVal();

 if (top)
  *top = NULL;

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

    if (n) {
     if (n == 3) {
      PBSON bsp = (PBSON)sap;

      if (i == 0) {
       if (top)
        *top = (PBVAL)bsp->Top;

       jvp = (PBVAL)bsp->Jsp;
       G = bsp->G;
       Base = G->Sarea;
      } else {
       BJNX bnx(bsp->G);

       jvp = MoveJson(&bnx, (PBVAL)bsp->Jsp);
      } // endelse i

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

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

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

     } // endif's n

    } else {
     PBVAL bp = NULL;

     if (b) {
      if (strchr("[{ \t\r\n", *sap)) {
       // Check whether this string is a valid json string
       JsonMemSave(g);

       if (!(bp = ParseJson(g, sap, strlen(sap))))
        JsonSubSet(g);   // Recover suballocated memory

       g->Saved_Size = 0;
      } else {
       // Perhaps a file name
       char* s = GetJsonFile(g, sap);
       
       if (s)
        bp = ParseJson(g, s, strlen(s));

      } // endif's

     } // endif b
     
     if (!bp) {
      ci = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1;
      SetString(jvp, sap, ci);
     } else {
      if (top)
       *top = bp;

      jvp = bp;
     } // endif bp

    } // 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")))
    SetBool(jvp, (char)bigint);
   else
    SetBigint(jvp, bigint);

   break;
  case REAL_RESULT:
   SetFloat(jvp, *(double*)sap);
   break;
  case DECIMAL_RESULT:
   SetFloat(jvp, 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.         */
/*********************************************************************************/
PBVAL BJNX::MakeTypedValue(PGLOBAL g, UDF_ARGS *args, uint i, JTYP type, PBVAL *top)
{
 char *sap;
 PBVAL jsp;
 PBVAL jvp = MakeValue(args, i, false, top);

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

 //} else if (jvp->GetValType() == type)
 // return jvp;

 if (jvp->Type == TYPE_STRG) {
  sap = GetString(jvp);

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

    SetValueVal(jvp, jsp);
   } // endif Type

  } // endif jsp

 } // endif Type

 return jvp;
} // end of MakeTypedValue

/*********************************************************************************/
/*  Parse a json file.                                                           */
/*********************************************************************************/
PBVAL BJNX::ParseJsonFile(PGLOBAL g, char *fn, int& pty, size_t& len)
{
 char   *memory;
 HANDLE  hFile;
 MEMMAP  mm;
 PBVAL   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 += mm.lenH;

 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);
 pty = pretty;
 CloseMemMap(memory, len);
 return jsp;
} // end of ParseJsonFile

/*********************************************************************************/
/*  Make the result according to the first argument type.                        */
/*********************************************************************************/
char *BJNX::MakeResult(UDF_ARGS *args, PBVAL top, uint n)
{
 char    *str = NULL;
 PGLOBAL& g = G;

 if (IsArgJson(args, 0) == 2) {
  // Make the change in the json file
  PSZ fn = MakePSZ(g, args, 0);

  if (Changed) {
   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, fn, pretty))
    PUSH_WARNING(g->Message);

   Changed = false;
  } // endif Changed

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

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

    Changed = false;
   } // endif Changed

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

 } 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.                 */
/*********************************************************************************/
PBSON BJNX::MakeBinResult(UDF_ARGS *args, PBVAL top, ulong len, int n)
{
 char* filename = NULL;
 int   pretty = 2;
 PBSON bnp = NULL;

 if (IsArgJson(args, 0) == 3) {
  bnp = (PBSON)args->args[0];

  if (bnp->Top != (PJSON)top)
   bnp->Top = bnp->Jsp = (PJSON)top;

  return bnp;
 } // endif 3

 if (IsArgJson(args, 0) == 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

  filename = (char*)args->args[0];
 } // endif 2

 if ((bnp = BbinAlloc(G, len, top))) {
  bnp->Filename = filename;
  bnp->Pretty = pretty;
  snprintf(bnp->Msg, sizeof(bnp->Msg), "Json Binary item");
 } //endif bnp

 return bnp;
} // end of MakeBinResult

/***********************************************************************/
/* Move a Json val block from one area to the current area.            */
/***********************************************************************/
PBVAL BJNX::MoveVal(PBVAL vlp)
{
 PBVAL nvp = NewVal(vlp->Type);

 nvp->Nd = vlp->Nd;
 return nvp;
} // end of MovedVal

/***********************************************************************/
/* Move a Json tree from one area to current area.                     */
/***********************************************************************/
PBVAL BJNX::MoveJson(PBJNX bxp, PBVAL jvp)
{
 PBVAL res = NULL;

 if (jvp)
  switch (jvp->Type) {
   case TYPE_JAR:
    res = MoveArray(bxp, jvp);
    break;
   case TYPE_JOB:
    res = MoveObject(bxp, jvp);
    break;
   default:
    res = MoveValue(bxp, jvp);
    break;
  } // endswitch Type

 return res;
} // end of MoveJson

/***********************************************************************/
/* Move an array.                                                      */
/***********************************************************************/
PBVAL BJNX::MoveArray(PBJNX bxp, PBVAL jap)
{
 PBVAL vlp, vmp, jvp = NULL, jarp = MoveVal(jap);

 for (vlp = bxp->GetArray(jap); vlp; vlp = bxp->GetNext(vlp)) {
  vmp = MoveJson(bxp, vlp);

  if (jvp)
   jvp->Next = MOF(vmp);
  else
   jarp->To_Val = MOF(vmp);

  jvp = vmp;
 } // endfor vlp

 return jarp;
} // end of MoveArray

/***********************************************************************/
/* Replace all object pointers by offsets.                             */
/***********************************************************************/
PBVAL BJNX::MoveObject(PBJNX bxp, PBVAL jop)
{
 PBPR   mpp, prp, ppp = NULL;
 PBVAL  vmp, jobp = MoveVal(jop);

 for (prp = bxp->GetObject(jop); prp; prp = bxp->GetNext(prp)) {
  vmp = MoveJson(bxp, GetVlp(prp));
  mpp = NewPair(DupStr(bxp->MZP(prp->Key)));
  SetPairValue(mpp, vmp);

  if (ppp)
   ppp->Vlp.Next = MOF(mpp);
  else
   jobp->To_Val = MOF(mpp);

  ppp = mpp;
 } // endfor vlp

 return jobp;
} // end of MoffObject

/***********************************************************************/
/* Move a non json value.                                              */
/***********************************************************************/
PBVAL BJNX::MoveValue(PBJNX bxp, PBVAL jvp)
{
 double *dp;
 PBVAL   nvp = MoveVal(jvp);

 switch (jvp->Type) {
  case TYPE_STRG:
  case TYPE_DTM:
   nvp->To_Val = DupStr(bxp->MZP(jvp->To_Val));
   break;
  case TYPE_DBL:
   dp = (double*)BsonSubAlloc(sizeof(double));
   *dp = bxp->DBL(jvp->To_Val);
   nvp->To_Val = MOF(dp);
   break;
  case TYPE_JVAL:
   nvp->To_Val = MOF(MoveJson(bxp, bxp->MVP(jvp->To_Val)));
   break;
  default:
   nvp->To_Val = jvp->To_Val;
   break;
 } // endswith Type

 return nvp;
} // end of MoveValue
 
/* -----------------------------Utility functions ------------------------------ */

/*********************************************************************************/
/*  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 IsArgJson(UDF_ARGS *args, uint i)
{
 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, "Bson_", 5) || !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, "Bbin_", 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, "Bfile_", 6) || !strnicmp(pat, "Jfile_", 6)) {
  n = 2;        // arg is a json file name
#if 0
 } else if (args->lengths[i]) {
  PGLOBAL g = PlugInit(NULL, (size_t)args->lengths[i] * M + 1024);
  char   *sap = MakePSZ(g, args, i);

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

  JsonFreeMem(g);
#endif // 0
 } // endif's

 return n;
} // end of IsArgJson

/*********************************************************************************/
/*  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

/* ------------------------- Now the new Bin UDF's ----------------------------- */

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

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

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

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

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, 1, false)) {
   BJNX  bnx(g);
   PBVAL bvp = bnx.MakeValue(args, 0, true);

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

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

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

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

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

/*********************************************************************************/
/*  Make a Json array containing all the parameters.                             */
/*  Note: jvp must be set before arp because it can be a binary argument.        */
/*********************************************************************************/
my_bool bson_make_array_init(UDF_INIT* initid, UDF_ARGS* args, char* message)
{
 unsigned long reslen, memlen;

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

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

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, false)) {
   BJNX  bnx(g);
   PBVAL jvp = bnx.MakeValue(args, 0);
   PBVAL arp = bnx.NewVal(TYPE_JAR);

   for (uint i = 0; i < args->arg_count;) {
    bnx.AddArrayValue(arp, jvp);
    jvp = bnx.MakeValue(args, ++i);
   } // endfor i

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

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

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

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

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

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

 if (args->arg_count < 2) {
  strcpy(message, "This function must have at least 2 arguments");
  return true;
  //} else if (!IsArgJson(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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of bson_array_add_values_init

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

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, true)) {
   BJNX  bnx(g);
   PBVAL arp = bnx.MakeValue(args, 0, true);

   if (arp->Type != TYPE_JAR) {
    PUSH_WARNING("First argument is not an array");
    goto fin;
   } // endif arp

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

   bnx.SetChanged(true);
   str = bnx.MakeResult(args, arp, INT_MAX);
  } // 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;

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

 return str;
} // end of bson_array_add_values

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

/*********************************************************************************/
/*  Add one value to a Json array.                                               */
/*********************************************************************************/
my_bool bson_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 (!IsArgJson(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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of bson_array_add_init

char *bson_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;
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL jsp, top;
  PBVAL arp, jvp = bnx.MakeValue(args, 0, true, &top);

  jsp = jvp;
  x = GetIntArgPtr(g, args, n);

  if (bnx.CheckPath(g, args, jsp, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp) {
   if (jvp->Type != TYPE_JAR) {
    if ((arp = bnx.NewVal(TYPE_JAR))) {
     bnx.AddArrayValue(arp, jvp);

     if (!top)
      top = arp;

    } // endif arp

   } else
    arp = jvp;

   if (arp) {
    bnx.AddArrayValue(arp, bnx.MakeValue(args, 1), x);
    bnx.SetChanged(true);
    str = bnx.MakeResult(args, top, n);
   } else
    PUSH_WARNING(g->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 bson_array_add

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

/*********************************************************************************/
/*  Delete a value from a Json array.                                            */
/*********************************************************************************/
my_bool bson_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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of bson_array_delete_init

char *bson_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;
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL arp, top;
  PBVAL jvp = bnx.MakeValue(args, 0, true, &top);

  if (!(x = GetIntArgPtr(g, args, n)))
   PUSH_WARNING("Missing or null array index");
  else if (bnx.CheckPath(g, args, jvp, arp, 1))
   PUSH_WARNING(g->Message);
  else if (arp && arp->Type == TYPE_JAR) {
   bnx.DeleteValue(arp, *x);
   bnx.SetChanged(true);
   str = bnx.MakeResult(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 bson_array_delete

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

/*********************************************************************************/
/*  Make a Json Object containing all the parameters.                            */
/*********************************************************************************/
my_bool bson_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 bson_make_object_init

char *bson_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)) {
   BJNX  bnx(g);
   PBVAL objp;

   if ((objp = bnx.NewVal(TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     bnx.SetKeyValue(objp, bnx.MakeValue(args, i), bnx.MakeKey(args, i));

    str = bnx.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 bson_make_object

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

/*********************************************************************************/
/*  Make a Json Object containing all not null parameters.                       */
/*********************************************************************************/
my_bool bson_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 bson_object_nonull_init

char *bson_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)) {
   BJNX  bnx(g);
   PBVAL jvp, objp;

   if ((objp = bnx.NewVal(TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     if (!bnx.IsValueNull(jvp = bnx.MakeValue(args, i)))
      bnx.SetKeyValue(objp, jvp, bnx.MakeKey(args, i));

    str = bnx.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 bson_object_nonull

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

/*********************************************************************************/
/*  Make a Json Object containing all the key/value parameters.                  */
/*********************************************************************************/
my_bool bson_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 bson_object_key_init

char *bson_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)) {
   BJNX  bnx(g);
   PBVAL objp;

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

    str = bnx.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 bson_object_key

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

/*********************************************************************************/
/*  Add or replace a value in a Json Object.                                     */
/*********************************************************************************/
my_bool bson_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 (!IsArgJson(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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of bson_object_add_init

char *bson_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PSZ     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)) {
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL jvp, objp;
  PBVAL jsp, top;

  jsp = bnx.MakeValue(args, 0, true, &top);

  if (bnx.CheckPath(g, args, jsp, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp && jvp->Type == TYPE_JOB) {
   objp = jvp;
   jvp = bnx.MakeValue(args, 1);
   key = bnx.MakeKey(args, 1);
   bnx.SetKeyValue(objp, jvp, key);
   bnx.SetChanged(true);
   str = bnx.MakeResult(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 bson_object_add

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

/*********************************************************************************/
/*  Delete a value from a Json object.                                           */
/*********************************************************************************/
my_bool bson_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 (!IsArgJson(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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of bson_object_delete_init

char *bson_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)) {
  bool  chg;
  BJNX  bnx(g, NULL, TYPE_STRG);
  PSZ   key;
  PBVAL jsp, objp, top;
  PBVAL jvp = bnx.MakeValue(args, 0, false, &top);

  jsp = jvp;

  if (bnx.CheckPath(g, args, jsp, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp && jvp->Type == TYPE_JOB) {
   key = bnx.MakeKey(args, 1);
   objp = jvp;
   chg = bnx.DeleteKey(objp, key);
   bnx.SetChanged(chg);
   str = bnx.MakeResult(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 bson_object_delete

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

/*********************************************************************************/
/*  Returns an array of the Json object keys.                                    */
/*********************************************************************************/
my_bool bson_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 (!IsArgJson(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 bson_object_list_init

char *bson_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)) {
   BJNX  bnx(g);
   PBVAL jarp;
   PBVAL jsp = bnx.MakeValue(args, 0, true);

   if (jsp->Type == TYPE_JOB) {
    jarp = bnx.GetKeyList(jsp);

    if (!(str = bnx.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 bson_object_list

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

/*********************************************************************************/
/*  Returns an array of the Json object values.                                  */
/*********************************************************************************/
my_bool bson_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 (!IsArgJson(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 bson_object_values_init

char *bson_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)) {
   BJNX  bnx(g);
   char *p;
   PBVAL jsp, jarp;
   PBVAL jvp = bnx.MakeValue(args, 0);

   if ((p = bnx.GetString(jvp))) {
    if (!(jsp = bnx.ParseJson(g, p, strlen(p)))) {
     PUSH_WARNING(g->Message);
     return NULL;
    } // endif jsp

   } else
    jsp = jvp;

   if (jsp->Type == TYPE_JOB) {
    jarp = bnx.GetObjectValList(jsp);

    if (!(str = bnx.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 bson_object_values

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

/*********************************************************************************/
/*  Set the value of JsonGrpSize.                                                */
/*********************************************************************************/
my_bool bsonset_def_prec_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 bsonset_def_prec_init

long long bsonset_def_prec(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *)
{
 long long n = *(long long*)args->args[0];

 JsonDefPrec = (int)n;
 return (long long)GetJsonDefPrec();
} // end of bsonset_def_prec

/*********************************************************************************/
/*  Get the value of JsonGrpSize.                                                */
/*********************************************************************************/
my_bool bsonget_def_prec_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 bsonget_def_prec_init

long long bsonget_def_prec(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *)
{
 return (long long)GetJsonDefPrec();
} // end of bsonget_def_prec

/*********************************************************************************/
/*  Set the value of JsonGrpSize.                                                */
/*********************************************************************************/
my_bool bsonset_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 bsonset_grp_size_init

long long bsonset_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 bsonset_grp_size

/*********************************************************************************/
/*  Get the value of JsonGrpSize.                                                */
/*********************************************************************************/
my_bool bsonget_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 bsonget_grp_size_init

long long bsonget_grp_size(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *)
{
 return (long long)GetJsonGroupSize();
} // end of bsonget_grp_size

/*********************************************************************************/
/*  Make a Json array from values coming from rows.                              */
/*********************************************************************************/
my_bool bson_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 (IsArgJson(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;
 (void)  new(g) BJNX(g);

 JsonMemSave(g);
 return false;
} // end of bson_array_grp_init

void bson_array_grp_clear(UDF_INIT *initid, uchar *, uchar *)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBJNX   bxp = (PBJNX)((char*)g->Sarea + sizeof(POOLHEADER));

 JsonSubSet(g);
 g->Activityp = (PACTIVITY)bxp->NewVal(TYPE_JAR);
 g->N = GetJsonGroupSize();
} // end of bson_array_grp_clear

void bson_array_grp_add(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBJNX   bxp = (PBJNX)((char*)g->Sarea + sizeof(POOLHEADER));
 PBVAL   arp = (PBVAL)g->Activityp;

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

} // end of bson_array_grp_add

char *bson_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result, 
 unsigned long *res_length, uchar *, uchar *)
{
 char   *str;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBJNX   bxp = (PBJNX)((char*)g->Sarea + sizeof(POOLHEADER));
 PBVAL   arp = (PBVAL)g->Activityp;

 if (g->N < 0)
  PUSH_WARNING("Result truncated to json_grp_size values");

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

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

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

/*********************************************************************************/
/*  Make a Json object from values coming from rows.                             */
/*********************************************************************************/
my_bool bson_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 (IsArgJson(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;
 (void) new(g) BJNX(g);

 JsonMemSave(g);
 return false;
} // end of bson_object_grp_init

void bson_object_grp_clear(UDF_INIT *initid, uchar *, uchar *)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBJNX   bxp = (PBJNX)((char*)g->Sarea + sizeof(POOLHEADER));

 JsonSubSet(g);
 g->Activityp = (PACTIVITY)bxp->NewVal(TYPE_JOB);
 g->N = GetJsonGroupSize();
} // end of bson_object_grp_clear

void bson_object_grp_add(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *)
{
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBJNX   bxp = (PBJNX)((char*)g->Sarea + sizeof(POOLHEADER));
 PBVAL   bop = (PBVAL)g->Activityp;

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

} // end of bson_object_grp_add

char *bson_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result, 
 unsigned long *res_length, uchar *, uchar *)
{
 char   *str;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBJNX   bxp = (PBJNX)((char*)g->Sarea + sizeof(POOLHEADER));
 PBVAL   bop = (PBVAL)g->Activityp;

 if (g->N < 0)
  PUSH_WARNING("Result truncated to json_grp_size values");

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

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

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

/*********************************************************************************/
/*  Test BJSON parse and serialize.                                              */
/*********************************************************************************/
my_bool bson_test_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
 unsigned long reslen, memlen, more = 1000;

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

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

char* bson_test(UDF_INIT* initid, UDF_ARGS* args, char* result,
 unsigned long* res_length, uchar * is_null, uchar * error) {
 char* str = NULL, *fn = NULL;
 int     pretty = 1;
 PBVAL   bvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;

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

 try {
  BJNX  bnx(g);

  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, !g->Xchk)) {
    PUSH_WARNING("CheckMemory error");
    *error = 1;
    goto err;
   } else  // Sarea may have been reallocated
    bnx.Reset();

   bvp = bnx.MakeValue(args, 0, true);

   if (bvp->Type == TYPE_NULL) {
    PUSH_WARNING(g->Message);
    goto err;
   } // endif bvp

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

  } else
   bvp = (PBVAL)g->Xchk;

  for (uint i = 1; i < args->arg_count; i++)
   if (args->arg_type[i] == STRING_RESULT)
    fn = args->args[i];
   else if (args->arg_type[i] == INT_RESULT)
    pretty = (int)*(longlong*)args->args[i];

  // Serialize the parse tree
  str = bnx.Serialize(g, bvp, fn, pretty);

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

 } catch (int n) {
  xtrc(1, "json_test_bson: error %d: %s\n", n, g->Message);
  PUSH_WARNING(g->Message);
  *error = 1;
  str = NULL;
 } catch (const char* msg) {
  snprintf(g->Message, sizeof(g->Message), "%s", msg);
  PUSH_WARNING(g->Message);
  *error = 1;
  str = NULL;
 } // end catch

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

 return str;
} // end of bson_test

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

/*********************************************************************************/
/*  Locate a value in a Json tree.                                               */
/*********************************************************************************/
my_bool bsonlocate_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 (!IsArgJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
  strcpy(message, "Third argument is not an integer (rank)");
  return true;
 } // endifs args

 CalcLen(args, false, reslen, memlen);

 // TODO: calculate this
 if (IsArgJson(args, 0) == 3)
  more = 0;

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

char* bsonlocate(UDF_INIT* initid, UDF_ARGS* args, char* result,
 unsigned long* res_length, uchar * is_null, uchar * error) {
 char   *path = NULL;
 int     k;
 PBVAL   bvp, bvp2;
 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 {
  BJNX  bnx(g);

  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, !g->Xchk)) {
    PUSH_WARNING("CheckMemory error");
    *error = 1;
    goto err;
   } else {
    bnx.Reset();  // Sarea may have been re-allocated
    bvp = bnx.MakeValue(args, 0, true);

    if (!bvp) {
     bnx.GetMsg(g);
     PUSH_WARNING(g->Message);
     goto err;
    } else if (bvp->Type == TYPE_NULL) {
     PUSH_WARNING("First argument is not a valid JSON item");
     goto err;
    } // endif bvp

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

   } // endif CheckMemory

  } else
   bvp = (PBVAL)g->Xchk;

  // The item to locate
  bvp2 = bnx.MakeValue(args, 1, true);

  if (bvp2->Type == TYPE_NULL) {
   PUSH_WARNING("Invalid second argument");
   goto err;
  } // endif bvp

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

// bnxp = new(g) BJNX(g, bvp, TYPE_STRING);
  path = bnx.Locate(g, bvp, bvp2, k);

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

 } catch (int n) {
  xtrc(1, "Exception %d: %s\n", n, g->Message);
  PUSH_WARNING(g->Message);
  *error = 1;
  path = NULL;
 } catch (const char* msg) {
  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 bsonlocate

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

/*********************************************************************************/
/*  Locate all occurences of a value in a Json tree.                             */
/*********************************************************************************/
my_bool bson_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 (!IsArgJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
  strcpy(message, "First argument must be a json item");
  return true;
 } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
  strcpy(message, "Third argument is not an integer (Depth)");
  return true;
 } // endifs

 CalcLen(args, false, reslen, memlen);

 // TODO: calculate this
 if (IsArgJson(args, 0) == 3)
  more = 0;

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

char* bson_locate_all(UDF_INIT* initid, UDF_ARGS* args, char* result,
 unsigned long* res_length, uchar * is_null, uchar * error) {
 char* path = NULL;
 int     mx = 10;
 PBVAL   bvp, bvp2;
 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 {
  BJNX  bnx(g);

  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, true)) {
    PUSH_WARNING("CheckMemory error");
    *error = 1;
    goto err;
   } else
    bnx.Reset();

   bvp = bnx.MakeValue(args, 0, true);

   if (bvp->Type == TYPE_NULL) {
    PUSH_WARNING("First argument is not a valid JSON item");
    goto err;
   } // endif bvp

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

  } else
   bvp = (PBVAL)g->Xchk;

  // The item to locate
  bvp2 = bnx.MakeValue(args, 1, true);

  if (bvp2->Type == TYPE_NULL) {
   PUSH_WARNING("Invalid second argument");
   goto err;
  } // endif bvp

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

// bnxp = new(g) BJNX(g, bvp, TYPE_STRING);
  path = bnx.LocateAll(g, bvp, bvp2, mx);

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

 } catch (int n) {
  xtrc(1, "Exception %d: %s\n", n, g->Message);
  PUSH_WARNING(g->Message);
  *error = 1;
  path = NULL;
 } catch (const char* msg) {
  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 bson_locate_all

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

/*********************************************************************************/
/*  Check whether the document contains a value or item.                         */
/*********************************************************************************/
my_bool bson_contains_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more = 1024;
 int n = IsArgJson(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 += (IsArgJson(args, 0) != 3 ? 1000 : 0);

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

long long bson_contains(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *error)
{
 unsigned char isn;
 char          res[256];
 unsigned long reslen;

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

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

/*********************************************************************************/
/*  Check whether the document contains a path.                                  */
/*********************************************************************************/
my_bool bsoncontains_path_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more = 1024;
 int n = IsArgJson(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 += (IsArgJson(args, 0) != 3 ? 1000 : 0);

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

long long bsoncontains_path(UDF_INIT *initid, UDF_ARGS *args, uchar *, uchar *error)
{
 char   *p, *path;
 long long n;
 PBVAL   jsp;
 PBVAL   jvp;
 PBJNX  bxp = NULL;
 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 {
   BJNX bnx(g);

   jvp = bnx.MakeValue(args, 0);

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

   } else
    jsp = jvp;

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

  } // endelse CheckMemory

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

 bxp = new(g) BJNX(g, jsp, TYPE_BIGINT);
 path = MakePSZ(g, args, 1);

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

 n = (bxp->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 bsoncontains_path

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

/*********************************************************************************/
/*  Merge two arrays or objects.                                                 */
/*********************************************************************************/
my_bool bson_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 for (int i = 0; i < 2; i++)
  if (!IsArgJson(args, i) && args->arg_type[i] != STRING_RESULT) {
   snprintf(message, MYSQL_ERRMSG_SIZE, "Argument %d must be a json item", i);
   return true;
  } // endif type

 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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of bson_item_merge_init

char *bson_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)) {
  JTYP  type= TYPE_JAR;
  BJNX  bnx(g);
  PBVAL jvp = NULL, top = NULL;
  PBVAL jsp[2] = {NULL, NULL};

  for (int i = 0; i < 2; i++) {
   jvp = bnx.MakeValue(args, i, true);

   if (i) {
    if (jvp && (jvp->Type != type)) {
     PUSH_WARNING("Argument types mismatch");
     goto fin;
    } // endif type

   } else {
    type = (JTYP)jvp->Type;

    if (type != TYPE_JAR && type != TYPE_JOB) {
     PUSH_WARNING("First argument is not an array or object");
     goto fin;
    } else
     top = jvp;

   } // endif i

   jsp[i] = jvp;
  } // endfor i

  if (type == TYPE_JAR)
   bnx.MergeArray(jsp[0], jsp[1]);
  else
   bnx.MergeObject(jsp[0], jsp[1]);

  bnx.SetChanged(true);
  str = bnx.MakeResult(args, top);
 } // endif CheckMemory

 // In case of error or file, return unchanged first 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;
  *error = 1;
  *is_null = 1;
 } else
  *res_length = strlen(str);

 return str;
} // end of bson_item_merge

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

/*********************************************************************************/
/*  Get a Json item from a Json document.                                        */
/*********************************************************************************/
my_bool bson_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more;
 int n = IsArgJson(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 bson_get_item_init

char *bson_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *)
{
 char   *path, *str = NULL;
 PBVAL   jvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 BJNX    bnx(g, NULL, TYPE_STRING, initid->max_length);

 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;
  } else {
   bnx.Reset();
   jvp = bnx.MakeValue(args, 0, true);

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

  } // endelse CheckMemory

 } else
  jvp = (PBVAL)g->Xchk;

 path = MakePSZ(g, args, 1);

 if (bnx.SetJpath(g, path, true)) {
  goto fin;
 } else
  jvp = bnx.GetRowValue(g, jvp, 0);

 if (!bnx.IsJson(jvp)) {
  snprintf(g->Message, sizeof(g->Message), "Not a Json item");
 } else
  str = bnx.Serialize(g, jvp, NULL, 0);

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

fin:
 if (!str) {
  PUSH_WARNING(g->Message);
  *is_null = 1;
  *res_length = 0;
 } else
  *res_length = strlen(str);

 return str;
} // end of bson_get_item

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

/*********************************************************************************/
/*  Get a string value from a Json item.                                         */
/*********************************************************************************/
my_bool bsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more = 1024;
 int n = IsArgJson(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 bsonget_string_init

char *bsonget_string(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *)
{
 char   *p, *path, *str = NULL;
 PBVAL   jsp, jvp;
 PBJNX   bxp = NULL;
 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 {
    BJNX bnx(g);

    jvp = bnx.MakeValue(args, 0);

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

    } else
     jsp = jvp;

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

   } // endelse CheckMemory

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

  path = MakePSZ(g, args, 1);
  bxp = new(g) BJNX(g, jsp, TYPE_STRING, initid->max_length);

  if (bxp->SetJpath(g, path)) {
   PUSH_WARNING(g->Message);
   goto err;
  } else
   bxp->ReadValue(g);

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

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

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

  PUSH_WARNING(g->Message);
  str = NULL;
 } catch (const char *msg) {
  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 bsonget_string

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

/*********************************************************************************/
/*  Get an integer value from a Json item.                                       */
/*********************************************************************************/
my_bool bsonget_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 (!IsArgJson(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 = (IsArgJson(args, 0) != 3) ? 1000 : 0;

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

long long bsonget_int(UDF_INIT *initid, UDF_ARGS *args,
 uchar *is_null, uchar *error)
{
 char   *p, *path;
 long long n;
 PBVAL   jsp, jvp;
 PBJNX   bxp = NULL;
 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 {
   BJNX bnx(g);

   jvp = bnx.MakeValue(args, 0);

   if ((p = bnx.GetString(jvp))) {
    if (!(jsp = bnx.ParseJson(g, p, strlen(p)))) {
     PUSH_WARNING(g->Message);
     if (g->Mrr) *error = 1;
     *is_null = 1;
     return 0;
    } // endif jsp

   } else
    jsp = jvp;

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

  } // endelse CheckMemory

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

 path = MakePSZ(g, args, 1);
 bxp = new(g) BJNX(g, jsp, TYPE_BIGINT);

 if (bxp->SetJpath(g, path)) {
  PUSH_WARNING(g->Message);
  *is_null = 1;
  return 0;
 } else
  bxp->ReadValue(g);

 if (bxp->GetValue()->IsNull()) {
  *is_null = 1;
  return 0;
 } // endif IsNull

 n = bxp->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 bsonget_int

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

/*********************************************************************************/
/*  Get a double value from a Json item.                                         */
/*********************************************************************************/
my_bool bsonget_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 (!IsArgJson(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 = (IsArgJson(args, 0) != 3) ? 1000 : 0;

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

double bsonget_real(UDF_INIT *initid, UDF_ARGS *args,
 uchar *is_null, uchar *error)
{
 char   *p, *path;
 double  d;
 PBVAL   jsp, jvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 BJNX    bnx(g);

 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 {
   bnx.Reset();
   jvp = bnx.MakeValue(args, 0);

   if ((p = bnx.GetString(jvp))) {
    if (!(jsp = bnx.ParseJson(g, p, strlen(p)))) {
     PUSH_WARNING(g->Message);
     *is_null = 1;
     return 0.0;
    } // endif jsp

   } else
    jsp = jvp;

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

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

 path = MakePSZ(g, args, 1);
//bxp = new(g) BJNX(g, jsp, TYPE_DOUBLE, 32, jsp->Nd);

 if (bnx.SetJpath(g, path)) {
  PUSH_WARNING(g->Message);
  *is_null = 1;
  return 0.0;
 } else
  jvp = bnx.GetRowValue(g, jsp, 0);

 if (!jvp || bnx.IsValueNull(jvp)) {
  *is_null = 1;
  return 0.0;
 } else if (args->arg_count == 2) {
  d = atof(bnx.GetString(jvp));
 } else
  d = bnx.GetDouble(jvp);

 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 bsonget_real_deinit(UDF_INIT* initid)
{
 JsonFreeMem((PGLOBAL)initid->ptr);
} // end of bsonget_real_deinit

/*********************************************************************************/
/*  Delete items from a Json document.                                           */
/*********************************************************************************/
my_bool bson_delete_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->arg_count < 2) {
  if (IsArgJson(args, 0) != 3) {
   strcpy(message, "This function must have at least 2 arguments or one binary");
   return true;
  } // endif args

 } // endif count

 CalcLen(args, false, reslen, memlen, true);

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

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

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

  return false;
 } else
  return true;

} // end of bson_delete_item_init

char *bson_delete_item(UDF_INIT *initid, UDF_ARGS *args, char *result, 
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 char   *path, *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)) {
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL top, jar = NULL;
  PBVAL jvp = bnx.MakeValue(args, 0, true, &top);

  if (args->arg_count == 1) {
   // This should be coming from bbin_locate_all
   jar = jvp;   // This is the array of paths
   jvp = top;   // And this is the document
  } else if(!bnx.IsJson(jvp)) {
   PUSH_WARNING("First argument is not a JSON document");
   goto fin;
  } else if (args->arg_count == 2) {
   // Check whether this is an array of paths 
   jar = bnx.MakeValue(args, 1, true);

   if (jar && jar->Type != TYPE_JAR)
    jar = NULL;

  } // endif arg_count

  if (jar) {
   // Do the deletion in reverse order
   for(int i = bnx.GetArraySize(jar) - 1; i >= 0; i--) {
    path = bnx.GetString(bnx.GetArrayValue(jar, i));

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

    bnx.SetChanged(bnx.DeleteItem(g, jvp));
   } // endfor i

  } else for (uint i = 1; i < args->arg_count; i++) {
   path = MakePSZ(g, args, i);

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

   bnx.SetChanged(bnx.DeleteItem(g, jvp));
  } // endfor i

  str = bnx.MakeResult(args, top, INT_MAX);
 } // endif CheckMemory

 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 bson_delete_item

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

/*********************************************************************************/
/*  This function is used by the json_set/insert/update_item functions.          */
/*********************************************************************************/
static char *bson_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 char   *path, *str = NULL;
 int     w;
 my_bool b = true;
 PBJNX   bxp;
 PBVAL   jsp, jvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 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 {
    BJNX bnx(g);

    jsp = bnx.MakeValue(args, 0, true);

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

   } // endif CheckMemory

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

  bxp = new(g)BJNX(g, jsp, TYPE_STRING, initid->max_length, 0, true);

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

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

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

   if (b && bxp->WriteValue(g, jvp)) {
    PUSH_WARNING(g->Message);
    continue;
   } // endif SetJpath

   bxp->SetChanged(true);
  } // endfor i

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

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

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

  PUSH_WARNING(g->Message);
  str = NULL;
 } catch (const char *msg) {
  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 bson_handle_item

/*********************************************************************************/
/*  Set Json items of a Json document according to path.                         */
/*********************************************************************************/
my_bool bson_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen, more = 0;
 int n = IsArgJson(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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  g->Alchecked = 0;
  return false;
 } else
  return true;

} // end of bson_set_item_init

char *bson_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *p)
{
 strcpy(result, "$set");
 return bson_handle_item(initid, args, result, res_length, is_null, p);
} // end of bson_set_item

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

/*********************************************************************************/
/*  Insert Json items of a Json document according to path.                      */
/*********************************************************************************/
my_bool bson_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_set_item_init(initid, args, message);
} // end of bson_insert_item_init

char *bson_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *p)
{
 strcpy(result, "$insert");
 return bson_handle_item(initid, args, result, res_length, is_null, p);
} // end of bson_insert_item

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

/*********************************************************************************/
/*  Update Json items of a Json document according to path.                      */
/*********************************************************************************/
my_bool bson_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_set_item_init(initid, args, message);
} // end of bson_update_item_init

char *bson_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *p)
{
 strcpy(result, "$update");
 return bson_handle_item(initid, args, result, res_length, is_null, p);
} // end of bson_update_item

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

/*********************************************************************************/
/*  Returns a json file as a json string.                                        */
/*********************************************************************************/
my_bool bson_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 bson_file_init

char *bson_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 char   *fn, *str = NULL;
 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;
  PBVAL  jsp, jvp = NULL;
  BJNX   bnx(g);

  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 = bnx.ParseJsonFile(g, fn, pty, len))) {
   PUSH_WARNING(g->Message);
   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 (bnx.CheckPath(g, args, jsp, jvp, 1)) {
   PUSH_WARNING(g->Message);
   goto fin;
  } else if (jvp)
   jsp = jvp;

  if (!(str = bnx.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 bson_file

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

/*********************************************************************************/
/*  Make a json file from a json item.                                           */
/*********************************************************************************/
my_bool bfile_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 (!IsArgJson(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 bfile_make_init

char *bfile_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;
 PBVAL   jsp, jvp;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 BJNX    bnx(g);

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

 if ((n = IsArgJson(args, 0)) == 3) {
  // Get default file name and pretty
  PBSON bsp = (PBSON)args->args[0];

  fn = bsp->Filename;
  pretty = bsp->Pretty;
 } else if ((n = IsArgJson(args, 0)) == 2)
  fn = args->args[0];

 if (!g->Xchk) {
  if (CheckMemory(g, initid, args, 1, true)) {
   PUSH_WARNING("CheckMemory error");
   goto fin;
  } else
   bnx.Reset();

  jvp = bnx.MakeValue(args, 0);

  if (!n && (p = bnx.GetString(jvp))) {
   if (!strchr("[{ \t\r\n", *p)) {
    // Is this a file name?
    if (!(p = GetJsonFile(g, p))) {
     PUSH_WARNING(g->Message);
     goto fin;
    } else
     fn = bnx.GetString(jvp);

   } // endif p

   if (!(jsp = bnx.ParseJson(g, p, strlen(p)))) {
    PUSH_WARNING(g->Message);
    goto fin;
   } // endif jsp

   bnx.SetValueVal(jvp, jsp);
  } // endif p

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

 } else
  jvp = (PBVAL)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 bfile_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 (!bnx.Serialize(g, jvp, 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 bfile_make

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

/*********************************************************************************/
/*  Convert a prettiest Json file to Pretty=0.                                   */
/*********************************************************************************/
my_bool bfile_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 bfile_convert_init

char *bfile_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 bfile_convert

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

/*********************************************************************************/
/*  Convert a pretty=0 Json file to binary BJSON.                                */
/*********************************************************************************/
my_bool bfile_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 bfile_bjson_init

char *bfile_bjson(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *, uchar *error) {
 char   *buf, *str = NULL, fn[_MAX_PATH], ofn[_MAX_PATH];
 bool    loop;
 ssize_t len, newloc;
 size_t  lrecl, binszp;
 PBVAL  jsp;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 BDOC    doc(g);

 snprintf(fn, sizeof(fn), "%s",  MakePSZ(g, args, 0));
 snprintf(ofn, sizeof(ofn), "%s", 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*)malloc(lrecl))) {
   try {
    do {
     loop = false;
     PlugSubSet(g->Sarea, g->Sarea_Size);

     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 = doc.ParseJson(g, buf, len))) {
       newloc = (size_t)PlugSubAlloc(g, NULL, 0);
       binszp = newloc - (size_t)jsp;

       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

   free(buf);
  } else
   str = strcpy(result, "Buffer malloc failed");

  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 bfile_bjson

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

/*********************************************************************************/
/*  Serialize a Json document.                .                                  */
/*********************************************************************************/
my_bool bson_serialize_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 unsigned long reslen, memlen;

 if (args->args[0] && IsArgJson(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 bson_serialize_init

char *bson_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 (IsArgJson(args, 0) == 3) {
   PBSON bsp = (PBSON)args->args[0];
   BJNX  bnx(bsp->G);
   PBVAL bvp = (args->arg_count == 1) ? (PBVAL)bsp->Jsp : (PBVAL)bsp->Top;

// if (!(str = bnx.Serialize(g, bvp, bsp->Filename, bsp->Pretty)))
   if (!(str = bnx.Serialize(g, bvp, 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 bson_serialize

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

/*********************************************************************************/
/*  Make and return a binary Json array containing all the parameters.           */
/*  Note: jvp must be set before arp because it can be a binary argument.        */
/*********************************************************************************/
my_bool bbin_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, true, reslen, memlen);
} // end of bbin_make_array_init

char *bbin_make_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 = NULL;

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, false)) {
   BJNX  bnx(g);
   PBVAL jvp = bnx.MakeValue(args, 0);
   PBVAL arp = bnx.NewVal(TYPE_JAR);

   for (uint i = 0; i < args->arg_count;) {
    bnx.AddArrayValue(arp, jvp);
    jvp = bnx.MakeValue(args, ++i);
   } // endfor i

   if ((bsp = BbinAlloc(bnx.G, initid->max_length, arp))) {
    safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");

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

  } // endif CheckMemory

 } else
  bsp = (PBSON)g->Xchk;

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

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

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

/*********************************************************************************/
/*  Add one value to a Json array.                                               */
/*********************************************************************************/
my_bool bbin_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
  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 (IsArgJson(args, 0) > 1)
   initid->const_item = 0;

  return false;
 } else
  return true;

} // end of bbin_array_add_init

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

 if (g->Xchk) {
  // This constant function was recalled
  bsp = (PBSON)g->Xchk;
  *res_length = sizeof(BSON);
  return (char*)bsp;
 } else if (!CheckMemory(g, initid, args, 2, false, false, true)) {
  uint n = 2;
  int* x = GetIntArgPtr(g, args, n);
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL jarp = NULL, top = NULL, jvp = NULL;
  PBVAL jsp = bnx.MakeValue(args, 0, true, &top);

  if (bnx.CheckPath(g, args, jsp, jvp, 2))
   PUSH_WARNING(g->Message);
  else if (jvp && jvp->Type != TYPE_JAR) {
   if ((jarp = bnx.NewVal(TYPE_JAR))) {
    bnx.AddArrayValue(jarp, jvp);

    if (!top)
     top = jarp;

   } // endif jarp

  } else
   jarp = jvp;

  if (jarp) {
   bnx.AddArrayValue(jarp, bnx.MakeValue(args, 1), x);
   bnx.SetChanged(true);
   bsp = bnx.MakeBinResult(args, top, initid->max_length);

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

  } else
   PUSH_WARNING(g->Message);

 } // endif CheckMemory

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

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

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

/*********************************************************************************/
/*  Add one or several values to a Bson array.                                   */
/*********************************************************************************/
my_bool bbin_array_add_values_init(UDF_INIT* initid, UDF_ARGS* args, char* message)
{
 return bson_array_add_values_init(initid, args, message);
} // end of bbin_array_add_values_init

char* bbin_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 = NULL;

 if (!g->Xchk) {
  if (!CheckMemory(g, initid, args, args->arg_count, true)) {
   uint  i = 0;
   BJNX  bnx(g);
   PBVAL arp, top;
   PBVAL bvp = bnx.MakeValue(args, 0, true, &top);

   if (bvp->Type == TYPE_JAR) {
    arp = bvp;
    i = 1;
   } else  // First argument is not an array
    arp = bnx.NewVal(TYPE_JAR);

   for (; i < args->arg_count; i++)
    bnx.AddArrayValue(arp, bnx.MakeValue(args, i));

   bnx.SetChanged(true);
   bsp = bnx.MakeBinResult(args, top, initid->max_length);
  } // endif CheckMemory

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

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

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

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

/*********************************************************************************/
/*  Make a Json array from values coming from rows.                              */
/*********************************************************************************/
my_bool bbin_array_grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_array_grp_init(initid, args, message);
} // end of bbin_array_grp_init

void bbin_array_grp_clear(UDF_INIT *initid, uchar *a, uchar *b)
{
 bson_array_grp_clear(initid, a, b);
} // end of bbin_array_grp_clear

void bbin_array_grp_add(UDF_INIT *initid, UDF_ARGS *args, uchar *a, uchar *b)
{
 bson_array_grp_add(initid, args, a, b);
} // end of bbin_array_grp_add

char *bbin_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result, 
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PBSON   bsp = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBVAL   arp = (PBVAL)g->Activityp;

 if (g->N < 0)
  PUSH_WARNING("Result truncated to json_grp_size values");

 if (arp)
  if ((bsp = BbinAlloc(g, initid->max_length, arp))) {
                        safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");
        }

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

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

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

/*********************************************************************************/
/*  Make a Json object from values coming from rows.                             */
/*********************************************************************************/
my_bool bbin_object_grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_object_grp_init(initid, args, message);
} // end of bbin_object_grp_init

void bbin_object_grp_clear(UDF_INIT *initid, uchar *a, uchar *b)
{
 bson_object_grp_clear(initid, a, b);
} // end of bbin_object_grp_clear

void bbin_object_grp_add(UDF_INIT *initid, UDF_ARGS *args, uchar *a, uchar *b)
{
 bson_object_grp_add(initid, args, a, b);
} // end of bbin_object_grp_add

char *bbin_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result, 
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 PBSON   bsp = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBVAL   bop = (PBVAL)g->Activityp;

 if (g->N < 0)
  PUSH_WARNING("Result truncated to json_grp_size values");

 if (bop)
  if ((bsp = BbinAlloc(g, initid->max_length, bop))) {
   safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
  }

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

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

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

/*********************************************************************************/
/*  Make a Json Object containing all the parameters.                            */
/*********************************************************************************/
my_bool bbin_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 bbin_make_object_init

char *bbin_make_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) {
  if (!CheckMemory(g, initid, args, args->arg_count, true)) {
   BJNX  bnx(g);
   PBVAL objp;

   if ((objp = bnx.NewVal(TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     bnx.SetKeyValue(objp, bnx.MakeValue(args, i), bnx.MakeKey(args, i));

    if ((bsp = BbinAlloc(bnx.G, initid->max_length, objp))) {
     safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");

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

   } // endif objp  

  } // endif CheckMemory

 } // endif Xchk

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

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

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

/*********************************************************************************/
/*  Make a Json Object containing all not null parameters.                       */
/*********************************************************************************/
my_bool bbin_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 bbin_object_nonull_init

char *bbin_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) {
  if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
   BJNX  bnx(g);
   PBVAL jvp, objp;

   if ((objp = bnx.NewVal(TYPE_JOB))) {
    for (uint i = 0; i < args->arg_count; i++)
     if (!bnx.IsValueNull(jvp = bnx.MakeValue(args, i)))
      bnx.SetKeyValue(objp, jvp, bnx.MakeKey(args, i));

    if ((bsp = BbinAlloc(bnx.G, initid->max_length, objp))) {
     safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");

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

   } // endif objp

  } // endif CheckMemory

 } // endif Xchk

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

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

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

/*********************************************************************************/
/*  Make a Json Object containing all the key/value parameters.                  */
/*********************************************************************************/
my_bool bbin_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 bbin_object_key_init

char *bbin_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) {
  if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
   BJNX  bnx(g);
   PBVAL objp;

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

    if ((bsp = BbinAlloc(bnx.G, initid->max_length, objp))) {
     safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");

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

   } // endif objp

  } // endif CheckMemory

 } // endif Xchk

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

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

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

/*********************************************************************************/
/*  Add or replace a value in a Json Object.                                     */
/*********************************************************************************/
my_bool bbin_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 (!IsArgJson(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 bbin_object_add_init

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

 if (g->Xchk) {
  // This constant function was recalled
  bsp = (PBSON)g->Xchk;
  *res_length = sizeof(BSON);
  return (char*)bsp;
 } else if (!CheckMemory(g, initid, args, 2, false, true, true)) {
  PSZ   key;
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL top;
  PBVAL jobp = bnx.MakeValue(args, 0, true, &top);
  PBVAL jvp = jobp;

  if (bnx.CheckPath(g, args, jvp, jobp, 2))
   PUSH_WARNING(g->Message);
  else if (jobp && jobp->Type == TYPE_JOB) {
   jvp = bnx.MakeValue(args, 1);
   key = bnx.MakeKey(args, 1);
   bnx.SetKeyValue(jobp, jvp, key);
   bnx.SetChanged(true);
  } else {
   PUSH_WARNING("First argument target is not an object");
   // if (g->Mrr) *error = 1;  (only if no path)
  } // endif jobp

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

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

 } // endif CheckMemory

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

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

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

/*********************************************************************************/
/*  Delete a value from a Json array.                                            */
/*********************************************************************************/
my_bool bbin_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_array_delete_init(initid, args, message);
} // end of bbin_array_delete_init

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

 if (g->Xchk) {
  // This constant function was recalled
  bsp = (PBSON)g->Xchk;
 } else if (!CheckMemory(g, initid, args, 1, false, false, true)) {
  int* x;
  uint n = 1;
  BJNX  bnx(g);
  PBVAL arp, top;
  PBVAL jvp = bnx.MakeValue(args, 0, true, &top);

  if (!(x = GetIntArgPtr(g, args, n)))
   PUSH_WARNING("Missing or null array index");
  else if (bnx.CheckPath(g, args, jvp, arp, 1))
   PUSH_WARNING(g->Message);
  else if (arp && arp->Type == TYPE_JAR) {
   bnx.SetChanged(bnx.DeleteValue(arp, *x));
   bsp = bnx.MakeBinResult(args, top, initid->max_length);
  } else {
   PUSH_WARNING("First argument target is not an array");
   // if (g->Mrr) *error = 1;
  } // endif jvp

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

 } // endif CheckMemory

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

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

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

/*********************************************************************************/
/*  Delete a value from a Json object.                                           */
/*********************************************************************************/
my_bool bbin_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 (!IsArgJson(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 bbin_object_delete_init

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

 if (g->Xchk) {
  // This constant function was recalled
  bsp = (PBSON)g->Xchk;
  *res_length = sizeof(BSON);
  return (char*)bsp;
 } else if (!CheckMemory(g, initid, args, 1, false, true, true)) {
  PCSZ  key;
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL top;
  PBVAL jobp = bnx.MakeValue(args, 0, true, &top);

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

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

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

 } // endif CheckMemory

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

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

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

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

char *bbin_object_list(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) {
  if (!CheckMemory(g, initid, args, 1, true, true)) {
   BJNX  bnx(g);
   PBVAL top, jarp = NULL;
   PBVAL jsp = bnx.MakeValue(args, 0, true, &top);

   if (jsp->Type == TYPE_JOB) {
    jarp = bnx.GetKeyList(jsp);
   } else {
    PUSH_WARNING("First argument is not an object");
    if (g->Mrr) *error = 1;
   } // endif jsp type

   // In case of error unchanged argument will be returned
   bsp = bnx.MakeBinResult(args, top, initid->max_length);
   bsp->Jsp = (PJSON)jarp;

  } // endif CheckMemory

  // 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 bbin_object_list

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

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

char *bbin_object_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) {
  if (!CheckMemory(g, initid, args, 1, true, true)) {
   BJNX  bnx(g);
   PBVAL top, jarp = NULL;
   PBVAL jvp = bnx.MakeValue(args, 0, true, &top);

   if (jvp->Type == TYPE_JOB) {
    jarp = bnx.GetObjectValList(jvp);
   } else {
    PUSH_WARNING("First argument is not an object");
    if (g->Mrr) *error = 1;
   } // endif jvp

   // In case of error unchanged argument will be returned
   bsp = bnx.MakeBinResult(args, top, initid->max_length);
   bsp->Jsp = (PJSON)jarp;

  } // endif CheckMemory

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

 } // endif bsp

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

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

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

/*********************************************************************************/
/*  Get a Json item from a Json document.                                        */
/*********************************************************************************/
my_bool bbin_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_get_item_init(initid, args, message);
} // end of bbin_get_item_init            

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

 if (g->Xchk) {
  bsp = (PBSON)g->Xchk;
 } else if (!CheckMemory(g, initid, args, 1, true, true)) {
  BJNX  bnx(g, NULL, TYPE_STRING, initid->max_length);
  PBVAL top, jvp = NULL;
  PBVAL jsp = bnx.MakeValue(args, 0, true, &top);

  if (bnx.CheckPath(g, args, jsp, jvp, 1))
   PUSH_WARNING(g->Message);
  else if (jvp) {
   bsp = bnx.MakeBinResult(args, top, initid->max_length);
   bsp->Jsp = (PJSON)jvp;

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

  } // endif jvp

 } else 
  PUSH_WARNING("CheckMemory error");

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

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

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

/*********************************************************************************/
/*  Merge two arrays or objects.                                                 */
/*********************************************************************************/
my_bool bbin_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_item_merge_init(initid, args, message);
} // end of bbin_item_merge_init

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

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

 if (!CheckMemory(g, initid, args, 2, false, false, true)) {
  JTYP  type = TYPE_JAR;
  BJNX  bnx(g);
  PBVAL jvp = NULL, top = NULL;
  PBVAL jsp[2] = {NULL, NULL};

  for (int i = 0; i < 2; i++) {
   if (i) {
    jvp = bnx.MakeValue(args, i, true);

    if (jvp && (jvp->Type != type)) {
     PUSH_WARNING("Argument types mismatch");
     goto fin;
    } // endif type

   } else {
    jvp = bnx.MakeValue(args, i, true, &top);
    type = (JTYP)jvp->Type;

    if (type != TYPE_JAR && type != TYPE_JOB) {
     PUSH_WARNING("First argument is not an array or object");
     goto fin;
    } // endif type

   } // endif i

   jsp[i] = jvp;
  } // endfor i

  if (type == TYPE_JAR)
   bnx.MergeArray(jsp[0], jsp[1]);
  else
   bnx.MergeObject(jsp[0], jsp[1]);

  bnx.SetChanged(true);
  bsp = bnx.MakeBinResult(args, top, initid->max_length);
 } // endif CheckMemory

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

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

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

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

/*********************************************************************************/
/*  This function is used by the jbin_set/insert/update_item functions.          */
/*********************************************************************************/
static char *bbin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 char   *path;
 int     w;
 my_bool b = true;
 PBJNX   bxp;
 PBVAL   jsp, jvp, top;
 PBSON   bsp = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;

 if (g->Alchecked) {
  bsp = (PBSON)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)) {
    throw 1;
   } else {
    BJNX bnx(g);

    jsp = bnx.MakeValue(args, 0, true, &top);

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

   } // endif CheckMemory

  } else {
   jsp = (PBVAL)g->Xchk;
   top = (PBVAL)g->More;
  } // endif Xchk

  bxp = new(g)BJNX(g, jsp, TYPE_STRING, initid->max_length, 0, true);

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

   if (bxp->SetJpath(g, path, false))
    throw 2;

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

   if (b && bxp->WriteValue(g, jvp))
    throw 3;

   bxp->SetChanged(true);
  } // endfor i

  if (!(bsp = bxp->MakeBinResult(args, top, initid->max_length)))
   throw 4;

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

 } 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);
  PUSH_WARNING(g->Message);
 } // end catch

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

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

/*********************************************************************************/
/*  Set Json items of a Json document according to path.                         */
/*********************************************************************************/
my_bool bbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_set_item_init(initid, args, message);
} // end of bbin_set_item_init

char *bbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *p)
{
 strcpy(result, "$set");
 return bbin_handle_item(initid, args, result, res_length, is_null, p);
} // end of bbin_set_item

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

/*********************************************************************************/
/*  Insert Json items of a Json document according to path.                      */
/*********************************************************************************/
my_bool bbin_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_set_item_init(initid, args, message);
} // end of bbin_insert_item_init

char *bbin_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *p)
{
 strcpy(result, "$insert");
 return bbin_handle_item(initid, args, result, res_length, is_null, p);
} // end of bbin_insert_item

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

/*********************************************************************************/
/*  Update Json items of a Json document according to path.                      */
/*********************************************************************************/
my_bool bbin_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_set_item_init(initid, args, message);
} // end of bbin_update_item_init

char *bbin_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *p)
{
 strcpy(result, "$update");
 return bbin_handle_item(initid, args, result, res_length, is_null, p);
} // end of bbin_update_item

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

/*********************************************************************************/
/*  Delete items from a Json document.                                           */
/*********************************************************************************/
my_bool bbin_delete_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_delete_item_init(initid, args, message);
} // end of bbin_delete_item_init

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

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

 if (!CheckMemory(g, initid, args, 1, false, false, true)) {
  BJNX  bnx(g, NULL, TYPE_STRING);
  PBVAL top, jar = NULL;
  PBVAL jvp = bnx.MakeValue(args, 0, true, &top);

  if (args->arg_count == 1) {
   // This should be coming from bbin_locate_all
   jar = jvp;   // This is the array of paths
   jvp = top;   // And this is the document
  } else if(!bnx.IsJson(jvp)) {
   PUSH_WARNING("First argument is not a JSON document");
   goto fin;
  } else if (args->arg_count == 2) {
   // Check whether this is an array of paths 
   jar = bnx.MakeValue(args, 1, true);

   if (jar && jar->Type != TYPE_JAR)
    jar = NULL;

  } // endif arg_count

  if (jar) {
   // Do the deletion in reverse order
   for(int i = bnx.GetArraySize(jar) - 1; i >= 0; i--) {
    path = bnx.GetString(bnx.GetArrayValue(jar, i));

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

    bnx.SetChanged(bnx.DeleteItem(g, jvp));
   } // endfor i

  } else for (uint i = 1; i < args->arg_count; i++) {
   path = MakePSZ(g, args, i);

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

   bnx.SetChanged(bnx.DeleteItem(g, jvp));
  } // endfor i

  bsp = bnx.MakeBinResult(args, top, initid->max_length);

  if (args->arg_count == 1)
   // Here Jsp was not a sub-item of top
   bsp->Jsp = (PJSON)top;

 } // endif CheckMemory

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

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

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

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

/*********************************************************************************/
/*  Returns a json file as a json binary tree.                                   */
/*********************************************************************************/
my_bool bbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
 return bson_file_init(initid, args, message);
} // end of bbin_file_init

char *bbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
 unsigned long *res_length, uchar *is_null, uchar *error)
{
 char   *fn;
 int     pretty = 3;
 size_t  len = 0;
 PBVAL   jsp, jvp = NULL;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 BJNX    bnx(g);
 PBSON   bsp = (PBSON)g->Xchk;

 if (bsp)
  goto fin;

 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 = bnx.ParseJsonFile(g, fn, pretty, len))) {
  PUSH_WARNING(g->Message);
  *error = 1;
  goto fin;
 } // endif jsp

// if (pretty == 3)
// PUSH_WARNING("File pretty format cannot be determined");
// else if (pretty == 3)
// pretty = pty;

 if ((bsp = BbinAlloc(bnx.G, 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 (bnx.CheckPath(g, args, jsp, jvp, 1)) {
  PUSH_WARNING(g->Message);
  bsp = NULL;
  goto fin;
 } else if (jvp)
  bsp->Jsp = (PJSON)jvp;

 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 bbin_file

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

/*********************************************************************************/
/*  Locate all occurences of a value in a Json tree.                             */
/*********************************************************************************/
my_bool bbin_locate_all_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
 return bson_locate_all_init(initid, args, message);
} // end of bbin_locate_all_init

char* bbin_locate_all(UDF_INIT* initid, UDF_ARGS* args, char* result,
 unsigned long* res_length, uchar * is_null, uchar * error) {
 char   *path = NULL;
 int     mx = 10;
 PBVAL   bvp, bvp2;
 PGLOBAL g = (PGLOBAL)initid->ptr;
 PBSON   bsp = NULL;

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

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

 try {
  PBVAL top = NULL;
  BJNX  bnx(g);

  if (!g->Xchk) {
   if (CheckMemory(g, initid, args, 1, true)) {
    PUSH_WARNING("CheckMemory error");
    *error = 1;
    goto err;
   } else
    bnx.Reset();

   bvp = bnx.MakeValue(args, 0, true, &top);

   if (bvp->Type == TYPE_NULL) {
    PUSH_WARNING("First argument is not a valid JSON item");
    goto err;
   } // endif bvp

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

  } else {
   bvp = (PBVAL)g->Xchk;
   top = (PBVAL)g->More;
  } // endif Xchk

  // The item to locate
  bvp2 = bnx.MakeValue(args, 1, true);

  if (bvp2->Type == TYPE_NULL) {
   PUSH_WARNING("Invalid second argument");
   goto err;
  } // endif bvp2

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

  if ((path = bnx.LocateAll(g, bvp, bvp2, mx))) {
   bsp = bnx.MakeBinResult(args, top, initid->max_length);
   bsp->Jsp = (PJSON)bnx.ParseJson(g, path, strlen(path));
  } // endif path

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

 } catch (int n) {
  xtrc(1, "Exception %d: %s\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 (!bsp) {
  *res_length = 0;
  *is_null = 1;
 } else
  *res_length = sizeof(BSON);

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

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



Messung V0.5 in Prozent
C=83 H=93 G=87

¤ Dauer der Verarbeitung: 0.120 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.