Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  tabbson.cpp   Sprache: C

 

/************* tabbson C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: tabbson     Version 1.2                               */
/*  (C) Copyright to the author Olivier BERTRAND          2020 - 2021  */
/*  This program are the BSON class DB execution routines.             */
/***********************************************************************/

/***********************************************************************/
/*  Include relevant sections of the MariaDB header file.              */
/***********************************************************************/
#include <my_global.h>

/***********************************************************************/
/*  Include application header files:                                  */
/*  global.h    is header containing all global declarations.          */
/*  plgdbsem.h  is header containing the DB application declarations.  */
/*  tdbdos.h    is header containing the TDBDOS declarations.          */
/*  json.h      is header containing the JSON classes declarations.    */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "maputil.h"
#include "filamtxt.h"
#include "tabdos.h"
#include "tabbson.h"
#include "filamap.h"
#if defined(GZ_SUPPORT)
#include "filamgz.h"
#endif   // GZ_SUPPORT
#if defined(ZIP_SUPPORT)
#include "filamzip.h"
#endif   // ZIP_SUPPORT
#if defined(JAVA_SUPPORT)
#include "jmgfam.h"
#endif   // JAVA_SUPPORT
#if defined(CMGO_SUPPORT)
#include "cmgfam.h"
#endif   // CMGO_SUPPORT
#include "tabmul.h"
#include "checklvl.h"
#include "resource.h"
#include "mycat.h"                             // for FNC_COL
#include "m_string.h"

/***********************************************************************/
/*  This should be an option.                                          */
/***********************************************************************/
#define MAXCOL          200        /* Default max column nb in result  */
//#define TYPE_UNKNOWN     12        /* Must be greater than other types */

/***********************************************************************/
/*  External functions.                                                */
/***********************************************************************/
USETEMP UseTemp(void);
bool    JsonAllPath(void);
int     GetDefaultDepth(void);
char   *GetJsonNull(void);
bool    Stringified(PCSZ, char*);

/***********************************************************************/
/* BSONColumns: construct the result blocks containing the description */
/* of all the columns of a table contained inside a JSON file.         */
/***********************************************************************/
PQRYRES BSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
{
  static int  buftyp[] = { TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
                          TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING };
  static XFLD fldtyp[] = { FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
                          FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT };
  static unsigned int length[] = { 0, 6, 8, 10, 10, 6, 6, 0 };
  int     i, n = 0;
  int     ncol = sizeof(buftyp) / sizeof(int);
  PJCL    jcp;
  BSONDISC* pjdc = NULL;
  PQRYRES qrp;
  PCOLRES crp;

  if (info) {
    length[0] = 128;
    length[7] = 256;
    goto skipit;
  } // endif info

  if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
    safe_strcpy(g->Message, sizeof(g->Message), "Cannot find column definition for multiple table");
    return NULL;
  } // endif Multiple

  pjdc = new(g) BSONDISC(g, length);

  if (!(n = pjdc->GetColumns(g, db, dsn, topt)))
    return NULL;

skipit:
  if (trace(1))
    htrc("BSONColumns: n=%d len=%d\n", n, length[0]);

  /*********************************************************************/
  /*  Allocate the structures used to refer to the result set.         */
  /*********************************************************************/
  qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
    buftyp, fldtyp, length, false, false);

  crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
  crp->Name = PlugDup(g, "Nullable");
  crp->Next->Name = PlugDup(g, "Jpath");

  if (info || !qrp)
    return qrp;

  qrp->Nblin = n;

  /*********************************************************************/
  /*  Now get the results into blocks.                                 */
  /*********************************************************************/
  for (i = 0, jcp = pjdc->fjcp; jcp; i++, jcp = jcp->Next) {
    if (jcp->Type == TYPE_UNKNOWN)
      jcp->Type = TYPE_STRG;               // Void column

    crp = qrp->Colresp;                    // Column Name
    crp->Kdata->SetValue(jcp->Name, i);
    crp = crp->Next;                       // Data Type
    crp->Kdata->SetValue(jcp->Type, i);
    crp = crp->Next;                       // Type Name
    crp->Kdata->SetValue(GetTypeName(jcp->Type), i);
    crp = crp->Next;                       // Precision
    crp->Kdata->SetValue(jcp->Len, i);
    crp = crp->Next;                       // Length
    crp->Kdata->SetValue(jcp->Len, i);
    crp = crp->Next;                       // Scale (precision)
    crp->Kdata->SetValue(jcp->Scale, i);
    crp = crp->Next;                       // Nullable
    crp->Kdata->SetValue(jcp->Cbn ? 1 : 0, i);
    crp = crp->Next;                       // Field format

    if (crp->Kdata)
      crp->Kdata->SetValue(jcp->Fmt, i);

  } // endfor i

/*********************************************************************/
/*  Return the result pointer.                                       */
/*********************************************************************/
  return qrp;
} // end of BSONColumns

/* -------------------------- Class BSONDISC ------------------------- */

/***********************************************************************/
/*  Class used to get the columns of a JSON table.                     */
/***********************************************************************/
BSONDISC::BSONDISC(PGLOBAL g, uint* lg)
{
  length = lg;
  jcp = fjcp = pjcp = NULL;
  tdp = NULL;
  tjnp = NULL;
  jpp = NULL;
  tjsp = NULL;
  jsp = NULL;
  bp = NULL;
  row = NULL;
  sep = NULL;
  strfy = NULL;
  i = n = bf = ncol = lvl = sz = limit = 0;
  all = false;
} // end of BSONDISC constructor

int BSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
{
  char  filename[_MAX_PATH];
  bool  mgo = (GetTypeID(topt->type) == TAB_MONGO);
  PBVAL bdp = NULL;

  lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
  lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
  sep = GetStringTableOption(g, topt, "Separator", ".");
  sz = GetIntegerTableOption(g, topt, "Jsize", 1024);
  limit = GetIntegerTableOption(g, topt, "Limit", 50);
  strfy = GetStringTableOption(g, topt, "Stringify", NULL);

  /*********************************************************************/
  /*  Open the input file.                                             */
  /*********************************************************************/
  tdp = new(g) BSONDEF;
  tdp->G = NULL;
#if defined(ZIP_SUPPORT)
  tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
  tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
#endif   // ZIP_SUPPORT
  tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);

  if (!tdp->Fn && topt->http)
    tdp->Fn = GetStringTableOption(g, topt, "Subtype", NULL);

  if (!(tdp->Database = SetPath(g, db)))
    return 0;

  if ((tdp->Objname = GetStringTableOption(g, topt, "Object", NULL))) {
    if (*tdp->Objname == '$') tdp->Objname++;
    if (*tdp->Objname == '.') tdp->Objname++;
  } // endif Objname

  tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0;
  tdp->Pretty = GetIntegerTableOption(g, topt, "Pretty", 2);
  tdp->Xcol = GetStringTableOption(g, topt, "Expand", NULL);
  tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false);
  tdp->Uri = (dsn && *dsn ? dsn : NULL);

  if (!tdp->Fn && !tdp->Uri) {
    safe_strcpy(g->Message, sizeof(g->Message), MSG(MISSING_FNAME));
    return 0;
  } else
    topt->subtype = NULL;

  if (tdp->Fn) {
    //  We used the file name relative to recorded datapath
    PlugSetPath(filename, tdp->Fn, tdp->GetPath());
    tdp->Fn = PlugDup(g, filename);
  } // endif Fn

  if (trace(1))
    htrc("File %s objname=%s pretty=%d lvl=%d\n",
      tdp->Fn, tdp->Objname, tdp->Pretty, lvl);

  if (tdp->Uri) {
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
    tdp->Collname = GetStringTableOption(g, topt, "Tabname", NULL);
    tdp->Schema = GetStringTableOption(g, topt, "Dbname", "test");
    tdp->Options = (PSZ)GetStringTableOption(g, topt, "Colist", "all");
    tdp->Pipe = GetBooleanTableOption(g, topt, "Pipeline", false);
    tdp->Driver = (PSZ)GetStringTableOption(g, topt, "Driver", NULL);
    tdp->Version = GetIntegerTableOption(g, topt, "Version", 3);
    tdp->Wrapname = (PSZ)GetStringTableOption(g, topt, "Wrapper",
      (tdp->Version == 2) ? "Mongo2Interface" : "Mongo3Interface");
    tdp->Pretty = 0;
#else   // !MONGO_SUPPORT
    snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
    return 0;
#endif  // !MONGO_SUPPORT
  } // endif Uri

  if (tdp->Pretty == 2) {
    tdp->G = g;

    if (tdp->Zipped) {
#if defined(ZIP_SUPPORT)
      tjsp = new(g) TDBBSON(g, tdp, new(g) UNZFAM(tdp));
#else   // !ZIP_SUPPORT
      snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
      return 0;
#endif  // !ZIP_SUPPORT
    } else
      tjsp = new(g) TDBBSON(g, tdp, new(g) MAPFAM(tdp));

    if (tjsp->MakeDocument(g))
      return 0;

    bp = tjsp->Bp;
//  bdp = tjsp->GetDoc() ? bp->GetBson(tjsp->GetDoc()) : NULL;
    bdp = tjsp->GetDoc();
    jsp = bdp ? bp->GetArrayValue(bdp, 0) : NULL;
  } else {
    if (!((tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))) {
      if (!mgo) {
        snprintf(g->Message, sizeof(g->Message), "LRECL must be specified for pretty=%d", tdp->Pretty);
        return 0;
      } else
        tdp->Lrecl = 8192;       // Should be enough

    } // endif Lrecl

    // Allocate the parse work memory
    tdp->G = PlugInit(NULL, (size_t)tdp->Lrecl * (tdp->Pretty >= 0 ? 4 : 2));
    tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF);

    if (tdp->Zipped) {
#if defined(ZIP_SUPPORT)
      tjnp = new(g)TDBBSN(g, tdp, new(g) UNZFAM(tdp));
#else   // !ZIP_SUPPORT
      snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
      return NULL;
#endif  // !ZIP_SUPPORT
    } else if (tdp->Uri) {
      if (tdp->Driver && toupper(*tdp->Driver) == 'C') {
#if defined(CMGO_SUPPORT)
        tjnp = new(g) TDBBSN(g, tdp, new(g) CMGFAM(tdp));
#else
        snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "C");
        return 0;
#endif
      } else if (tdp->Driver && toupper(*tdp->Driver) == 'J') {
#if defined(JAVA_SUPPORT)
        tjnp = new(g) TDBBSN(g, tdp, new(g) JMGFAM(tdp));
#else
        snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "Java");
        return 0;
#endif
      } else {             // Driver not specified
#if defined(CMGO_SUPPORT)
        tjnp = new(g) TDBBSN(g, tdp, new(g) CMGFAM(tdp));
#elif defined(JAVA_SUPPORT)
        tjnp = new(g) TDBBSN(g, tdp, new(g) JMGFAM(tdp));
#else
        snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
        return 0;
#endif
      } // endif Driver

    } else if (tdp->Pretty >= 0)
      tjnp = new(g) TDBBSN(g, tdp, new(g) DOSFAM(tdp));
    else
      tjnp = new(g) TDBBSN(g, tdp, new(g) BINFAM(tdp));

    tjnp->SetMode(MODE_READ);
    bp = tjnp->Bp;

    if (tjnp->OpenDB(g))
      return 0;

    switch (tjnp->ReadDB(g)) {
    case RC_EF:
      safe_strcpy(g->Message, sizeof(g->Message), "Void json table");
    case RC_FX:
      goto err;
    default:
      jsp = tjnp->Row;
    } // endswitch ReadDB

  } // endif pretty

  if (!(row = (jsp) ? bp->GetObject(jsp) : NULL)) {
    safe_strcpy(g->Message, sizeof(g->Message), "Can only retrieve columns from object rows");
    goto err;
  } // endif row

  all = GetBooleanTableOption(g, topt, "Fullarray", false);
  jcol.Name = jcol.Fmt = NULL;
  jcol.Next = NULL;
  jcol.Found = true;
  colname[0] = 0;

  if (!tdp->Uri) {
    fmt[0] = '$';
    fmt[1] = '.';
    bf = 2;
  } // endif Uri

  /*********************************************************************/
  /*  Analyse the JSON tree and define columns.                        */
  /*********************************************************************/
  for (i = 1; ; i++) {
    for (jpp = row; jpp; jpp = bp->GetNext(jpp)) {
      strncpy(colname, bp->GetKey(jpp), 64);
      fmt[bf] = 0;

      if (Find(g, bp->GetVlp(jpp), colname, MY_MIN(lvl, 0)))
        goto err;

    } // endfor jpp

    // Missing column can be null
    for (jcp = fjcp; jcp; jcp = jcp->Next) {
      jcp->Cbn |= !jcp->Found;
      jcp->Found = false;
    } // endfor jcp

    if (tdp->Pretty != 2) {
      // Read next record
      switch (tjnp->ReadDB(g)) {
      case RC_EF:
        jsp = NULL;
        break;
      case RC_FX:
        goto err;
      default:
        jsp = tjnp->Row;
      } // endswitch ReadDB

    } else
      jsp = bp->GetNext(jsp);

    if (!(row = (jsp) ? bp->GetObject(jsp) : NULL))
      break;

  } // endfor i

  if (tdp->Pretty != 2)
    tjnp->CloseDB(g);

  return n;

err:
  if (tdp->Pretty != 2)
    tjnp->CloseDB(g);

  return 0;
} // end of GetColumns

bool BSONDISC::Find(PGLOBAL g, PBVAL jvp, PCSZ key, int j)
{
  char  *p, *pc = colname + strlen(colname), buf[32];
  int    ars;
  size_t n;
  PBVAL  job;
  PBVAL  jar;

  if (jvp && !bp->IsJson(jvp)) {
    if (JsonAllPath() && !fmt[bf])
      safe_strcat(fmt, sizeof(fmt), colname);

    jcol.Type = (JTYP)jvp->Type;

    switch (jvp->Type) {
      case TYPE_STRG:
      case TYPE_DTM:
        jcol.Len = (int)strlen(bp->GetString(jvp));
        break;
      case TYPE_INTG:
      case TYPE_BINT:
        jcol.Len = (int)strlen(bp->GetString(jvp, buf));
        break;
      case TYPE_DBL:
      case TYPE_FLOAT:
        jcol.Len = (int)strlen(bp->GetString(jvp, buf));
        jcol.Scale = jvp->Nd;
        break;
      case TYPE_BOOL:
        jcol.Len = 1;
        break;
      default:
        jcol.Len = 0;
        break;
    } // endswitch Type

    jcol.Scale = jvp->Nd;
    jcol.Cbn = jvp->Type == TYPE_NULL;
  } else if (!jvp || bp->IsValueNull(jvp)) {
    jcol.Type = TYPE_UNKNOWN;
    jcol.Len = jcol.Scale = 0;
    jcol.Cbn = true;
  } else  if (j < lvl && !Stringified(strfy, colname)) {
    if (!fmt[bf])
      safe_strcat(fmt, sizeof(fmt), colname);

    p = fmt + strlen(fmt);
    jsp = jvp;

    switch (jsp->Type) {
    case TYPE_JOB:
      job = jsp;

      for (PBPR jrp = bp->GetObject(job); jrp; jrp = bp->GetNext(jrp)) {
        PCSZ k = bp->GetKey(jrp);

        if (*k != '$') {
          n = sizeof(fmt) - strlen(fmt) - 1;
          strncat(strncat(fmt, sep, n), k, n - strlen(sep));
          n = sizeof(colname) - strlen(colname) - 1;
          strncat(strncat(colname, "_", n), k, n - 1);
        } // endif Key

        if (Find(g, bp->GetVlp(jrp), k, j + 1))
          return true;

        *p = *pc = 0;
      } // endfor jrp

      return false;
    case TYPE_JAR:
      jar = jsp;

      if (all || (tdp->Xcol && !stricmp(tdp->Xcol, key)))
        ars = MY_MIN(bp->GetArraySize(jar), limit);
      else
        ars = MY_MIN(bp->GetArraySize(jar), 1);

      for (int k = 0; k < ars; k++) {
        n = sizeof(fmt) - (strlen(fmt) + 1);

        if (!tdp->Xcol || stricmp(tdp->Xcol, key)) {
          snprintf(buf, sizeof(buf), "%d", k);

          if (tdp->Uri) {
            strncat(strncat(fmt, sep, n), buf, n - strlen(sep));
          } else {
            strncat(strncat(fmt, "[", n), buf, n - 1);
            strncat(fmt, "]", n - (strlen(buf) + 1));
          } // endif uri

          if (all) {
            n = sizeof(colname) - (strlen(colname) + 1);
            strncat(strncat(colname, "_", n), buf, n - 1);
          } // endif all

        } else {
          strncat(fmt, (tdp->Uri ? sep : "[*]"), n);
        }

        if (Find(g, bp->GetArrayValue(jar, k), "", j))
          return true;

        *p = *pc = 0;
      } // endfor k

      return false;
    default:
      snprintf(g->Message, sizeof(g->Message), "Logical error after %s", fmt);
      return true;
    } // endswitch Type

  } else if (lvl >= 0) {
    if (Stringified(strfy, colname)) {
      if (!fmt[bf])
        safe_strcat(fmt, sizeof(fmt), colname);

      safe_strcat(fmt, sizeof(fmt), ".*");
    } else if (JsonAllPath() && !fmt[bf])
      safe_strcat(fmt, sizeof(fmt), colname);

    jcol.Type = TYPE_STRG;
    jcol.Len = sz;
    jcol.Scale = 0;
    jcol.Cbn = true;
  } else
    return false;

  AddColumn(g);
  return false;
} // end of Find

void BSONDISC::AddColumn(PGLOBAL g)
{
  bool b = fmt[bf] != 0;     // True if formatted

  // Check whether this column was already found
  for (jcp = fjcp; jcp; jcp = jcp->Next)
    if (!strcmp(colname, jcp->Name))
      break;

  if (jcp) {
    if (jcp->Type != jcol.Type) {
      if (jcp->Type == TYPE_UNKNOWN || jcp->Type == TYPE_NULL)
        jcp->Type = jcol.Type;
      //    else if (jcol.Type != TYPE_UNKNOWN && jcol.Type != TYPE_VOID)
      //      jcp->Type = TYPE_STRING;
      else if (jcp->Type != TYPE_STRG)
        switch (jcol.Type) {
        case TYPE_STRG:
        case TYPE_DBL:
          jcp->Type = jcol.Type;
          break;
        case TYPE_BINT:
          if (jcp->Type == TYPE_INTG || jcp->Type == TYPE_BOOL)
            jcp->Type = jcol.Type;

          break;
        case TYPE_INTG:
          if (jcp->Type == TYPE_BOOL)
            jcp->Type = jcol.Type;

          break;
        default:
          break;
        } // endswith Type

    } // endif Type

    if (b && (!jcp->Fmt || strlen(jcp->Fmt) < strlen(fmt))) {
      jcp->Fmt = PlugDup(g, fmt);
      length[7] = MY_MAX(length[7], strlen(fmt));
    } // endif fmt

    jcp->Len = MY_MAX(jcp->Len, jcol.Len);
    jcp->Scale = MY_MAX(jcp->Scale, jcol.Scale);
    jcp->Cbn |= jcol.Cbn;
    jcp->Found = true;
  } else if (jcol.Type != TYPE_UNKNOWN || tdp->Accept) {
    // New column
    jcp = (PJCL)PlugSubAlloc(g, NULL, sizeof(JCOL));
    *jcp = jcol;
    jcp->Cbn |= (i > 1);
    jcp->Name = PlugDup(g, colname);
    length[0] = MY_MAX(length[0], strlen(colname));

    if (b) {
      jcp->Fmt = PlugDup(g, fmt);
      length[7] = MY_MAX(length[7], strlen(fmt));
    } else
      jcp->Fmt = NULL;

    if (pjcp) {
      jcp->Next = pjcp->Next;
      pjcp->Next = jcp;
    } else
      fjcp = jcp;

    n++;
  } // endif jcp

  if (jcp)
    pjcp = jcp;

} // end of AddColumn

/* -------------------------- Class BTUTIL --------------------------- */

/***********************************************************************/
/*  Find the row in the tree structure.                                */
/***********************************************************************/
PBVAL BTUTIL::FindRow(PGLOBAL g)
{
  char *p, *objpath = PlugDup(g, Tp->Objname);
  char *sep = (char*)(Tp->Sep == ':' ? ":[" : ".[");
  bool  bp = false, b = false;
  PBVAL jsp = Tp->Row;
  PBVAL val = NULL;

  for (; jsp && objpath; objpath = p, bp = b) {
    if ((p = strpbrk(objpath + 1, sep))) {
      b = (*p == '[');
      *p++ = 0;
    } // endif p

    if (!bp && *objpath != '[' && !IsNum(objpath)) { // objpass is a key
      val = (jsp->Type == TYPE_JOB) ?
        GetKeyValue(jsp, objpath) : NULL;
    } else {
      if (bp || *objpath == '[') {                   // Old style
        if (objpath[strlen(objpath) - 1] != ']') {
          snprintf(g->Message, sizeof(g->Message), "Invalid Table path %s", Tp->Objname);
          return NULL;
        } else if (!bp)
          objpath++;

      } // endif bp

      val = (jsp->Type == TYPE_JAR) ?
        GetArrayValue(jsp, atoi(objpath) - Tp->B) : NULL;
    } // endif objpath

      //  jsp = (val) ? val->GetJson() : NULL;
    jsp = val;
  } // endfor objpath

  if (jsp && jsp->Type != TYPE_JOB) {
    if (jsp->Type == TYPE_JAR) {
      jsp = GetArrayValue(jsp, Tp->B);

      if (jsp->Type != TYPE_JOB)
        jsp = NULL;

    } else
      jsp = NULL;

  } // endif Type

  return jsp;
} // end of FindRow

/***********************************************************************/
/*  Parse the read line.                                               */
/***********************************************************************/
PBVAL BTUTIL::ParseLine(PGLOBAL g, int prty, bool cma)
{
  pretty = prty;
  comma = cma;
  return ParseJson(g, Tp->To_Line, strlen(Tp->To_Line));
} // end of ParseLine

/***********************************************************************/
/*  Make the top tree from the object path.                            */
/***********************************************************************/
PBVAL BTUTIL::MakeTopTree(PGLOBAL g, int type)
{
  PBVAL top = NULL, val = NULL;

  if (Tp->Objname) {
    if (!Tp->Row) {
      // Parse and allocate Objpath item(s)
      char *p, *objpath = PlugDup(g, Tp->Objname);
      char *sep = (char*)(Tp->Sep == ':' ? ":[" : ".[");
      int   i;
      bool  bp = false, b = false;
      PBVAL objp = NULL;
      PBVAL arp = NULL;

      for (; objpath; objpath = p, bp = b) {
        if ((p = strpbrk(objpath + 1, sep))) {
          b = (*p == '[');
          *p++ = 0;
        } // endif p


        if (!bp && *objpath != '[' && !IsNum(objpath)) {
          // objpass is a key
          objp = NewVal(TYPE_JOB);

          if (!top)
            top = objp;

          if (val)
            SetValueObj(val, objp);

          val = NewVal();
          SetKeyValue(objp, MOF(val), objpath);
        } else {
          if (bp || *objpath == '[') {
            // Old style
            if (objpath[strlen(objpath) - 1] != ']') {
              snprintf(g->Message, sizeof(g->Message), "Invalid Table path %s", Tp->Objname);
              return NULL;
            } else if (!bp)
              objpath++;

          } // endif bp

          if (!top)
            top = NewVal(TYPE_JAR);

          if (val)
            SetValueArr(val, arp);

          val = NewVal();
          i = atoi(objpath) - Tp->B;
          SetArrayValue(arp, val, i);
        } // endif objpath

      } // endfor p

    } // endif Val

    Tp->Row = val;
    if (Tp->Row) Tp->Row->Type = type;
  } else
    top = Tp->Row = NewVal(type);

  return top;
} // end of MakeTopTree

PSZ BTUTIL::SerialVal(PGLOBAL g, PBVAL vlp, int pretty)
{
  return Serialize(g, vlp, NULL, pretty);
} // en of SerialTop

/* -------------------------- Class BCUTIL --------------------------- */

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

    if (Jb) {
      vp->SetValue_psz(Serialize(g, jvp, NULL, 0));
      Jb = false;
    } else switch (jvp->Type) {
    case TYPE_STRG:
    case TYPE_INTG:
    case TYPE_BINT:
    case TYPE_DBL:
    case TYPE_DTM:
    case TYPE_FLOAT:
      switch (vp->GetType()) {
        case TYPE_STRING:
        case TYPE_DECIM:
          vp->SetValue_psz(GetString(jvp));
          break;
        case TYPE_INT:
        case TYPE_SHORT:
        case TYPE_TINY:
          vp->SetValue(GetInteger(jvp));
          break;
        case TYPE_BIGINT:
          vp->SetValue(GetBigint(jvp));
          break;
        case TYPE_DOUBLE:
          vp->SetValue(GetDouble(jvp));

          if (jvp->Type == TYPE_DBL || jvp->Type == TYPE_FLOAT)
            vp->SetPrec(jvp->Nd);

          break;
        case TYPE_DATE:
          if (jvp->Type == TYPE_STRG) {
            PSZ dat = GetString(jvp);

            if (!IsNum(dat)) {
              if (!((DTVAL*)vp)->IsFormatted())
                ((DTVAL*)vp)->SetFormat(g, "YYYY-MM-DDThh:mm:ssZ", 20, 0);

              vp->SetValue_psz(dat);
            } else
              vp->SetValue(atoi(dat));

          } else
            vp->SetValue(GetInteger(jvp));

          break;
        default:
          snprintf(G->Message, sizeof(G->Message), "Unsupported column type %d", vp->GetType());
          throw 888;
      } // endswitch Type

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

      break;
    case TYPE_JAR:
    case TYPE_JOB:
      //      SetJsonValue(g, vp, val->GetArray()->GetValue(0));
      vp->SetValue_psz(GetValueText(g, jvp, NULL));
      break;
    default:
      vp->Reset();
      vp->SetNull(true);
    } // endswitch Type

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

} // end of SetJsonValue

/***********************************************************************/
/*  MakeJson: Serialize the json item and set value to it.             */
/***********************************************************************/
PBVAL BCUTIL::MakeBson(PGLOBAL g, PBVAL jsp, int n)
{
  PBVAL vlp, jvp = jsp;

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

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

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

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

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

    } // endif Type

  } // endif's

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

/***********************************************************************/
/*  GetRowValue:                                                       */
/***********************************************************************/
PBVAL BCUTIL::GetRowValue(PGLOBAL g, PBVAL row, int i)
{
  int    nod = Cp->Nod;
  JNODE *nodes = Cp->Nodes;
  PBVAL  arp;
  PBVAL  bvp = NULL;

  for (; i < nod && row; i++) {
    if (nodes[i].Op == OP_NUM) {
      bvp = NewVal(TYPE_INT);
      bvp->N = (row->Type == TYPE_JAR) ? GetSize(row) : 1;
      return(bvp);
    } else if (nodes[i].Op == OP_XX) {
      return MakeBson(g, row, i);
    } else switch (row->Type) {
    case TYPE_JOB:
      if (!nodes[i].Key) {
        // Expected Array was not there, wrap the value
        if (i < nod - 1)
          continue;
        else
          bvp = row;

      } else
        bvp = GetKeyValue(row, nodes[i].Key);

      break;
    case TYPE_JAR:
      arp = row;

      if (!nodes[i].Key) {
        if (nodes[i].Op == OP_EQ)
          bvp = GetArrayValue(arp, nodes[i].Rank);
        else if (nodes[i].Op == OP_EXP)
          return NewVal(ExpandArray(g, arp, i));
        else
          return NewVal(CalculateArray(g, arp, i));

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

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

    if (i < nod - 1)
      row = bvp;

  } // endfor i

  return bvp;
} // end of GetRowValue

/***********************************************************************/
/*  GetColumnValue:                                                    */
/***********************************************************************/
PVAL BCUTIL::GetColumnValue(PGLOBAL g, PBVAL row, int i)
{
  PVAL  value = Cp->Value;
  PBVAL bvp = GetRowValue(g, row, i);

  SetJsonValue(g, value, bvp);
  return value;
} // end of GetColumnValue

/***********************************************************************/
/*  ExpandArray:                                                       */
/***********************************************************************/
PVAL BCUTIL::ExpandArray(PGLOBAL g, PBVAL arp, int n)
{
  int    nod = Cp->Nod, ars = MY_MIN(Tp->Limit, GetArraySize(arp));
  JNODE *nodes = Cp->Nodes;
  PVAL   value = Cp->Value;
  PBVAL  bvp;
  BVAL   bval;

  if (!ars) {
    value->Reset();
    value->SetNull(true);
    Tp->NextSame = 0;
    return value;
  } // endif ars

  if (!(bvp = GetArrayValue(arp, (nodes[n].Rx = nodes[n].Nx)))) {
    safe_strcpy(g->Message, sizeof(g->Message), "Logical error expanding array");
    throw 666;
  } // endif jvp

  if (n < nod - 1 && IsJson(bvp)) {
    SetValue(&bval, GetColumnValue(g, bvp, n + 1));
    bvp = &bval;
  } // endif n

  if (n >= Tp->NextSame) {
    if (++nodes[n].Nx == ars) {
      nodes[n].Nx = 0;
      Cp->Xnod = 0;
    } else
      Cp->Xnod = n;

    Tp->NextSame = Cp->Xnod;
  } // endif NextSame

  SetJsonValue(g, value, bvp);
  return value;
} // end of ExpandArray

/***********************************************************************/
/*  CalculateArray:                                                    */
/***********************************************************************/
PVAL BCUTIL::CalculateArray(PGLOBAL g, PBVAL arp, int n)
{
  int    i, ars, nv = 0, nextsame = Tp->NextSame;
  bool   err;
  int    nod = Cp->Nod;
  JNODE *nodes = Cp->Nodes;
  OPVAL  op = nodes[n].Op;
  PVAL   val[2], vp = nodes[n].Valp, mulval = Cp->MulVal;
  PBVAL  jvrp, jvp;
  BVAL   jval;

  vp->Reset();
  ars = MY_MIN(Tp->Limit, GetArraySize(arp));
  xtrc(1,"CalculateArray: size=%d op=%d nextsame=%d\n", ars, op, nextsame);

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

    if (!IsValueNull(jvrp) || (op == OP_CNC && GetJsonNull())) do {
      if (IsValueNull(jvrp)) {
        SetString(jvrp, PlugDup(G, GetJsonNull()));
        jvp = jvrp;
      } else if (n < nod - 1 && IsJson(jvrp)) {
        Tp->NextSame = nextsame;
        SetValue(&jval, GetColumnValue(g, jvrp, n + 1));
        jvp = &jval;
      } else
        jvp = jvrp;

      xtrc(1, "jvp=%s null=%d\n", GetString(jvp), IsValueNull(jvp) ? 1 : 0);

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

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

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

        if (err)
          vp->Reset();

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

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

        } // endif trace

      } // endif Null

    } while (Tp->NextSame > nextsame);

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

  Tp->NextSame = nextsame;
  return vp;
} // end of CalculateArray

/***********************************************************************/
/*  GetRow: Get the object containing this column.                     */
/***********************************************************************/
PBVAL BCUTIL::GetRow(PGLOBAL g)
{
  int    nod = Cp->Nod;
  JNODE *nodes = Cp->Nodes;
  PBVAL  val = NULL;
  PBVAL  arp;
  PBVAL  nwr, row = Tp->Row;

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

      val = GetKeyValue(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 = row;
      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++) {
        int type;

        if (nodes[i].Op == OP_XX)
          break;
        else if (!nodes[i].Key)
          // Construct intermediate array
          type = TYPE_JAR;
        else
          type = TYPE_JOB;

        if (row->Type == TYPE_JOB) {
          nwr = AddPair(row, nodes[i - 1].Key, type);
        } else if (row->Type == TYPE_JAR) {
          AddArrayValue(row, (nwr = NewVal(type)));
        } else {
          safe_strcpy(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


/* -------------------------- Class BSONDEF -------------------------- */

BSONDEF::BSONDEF(void)
{
  Jmode = MODE_OBJECT;
  Objname = NULL;
  Xcol = NULL;
  Pretty = 2;
  Limit = 1;
  Base = 0;
  Strict = false;
  Sep = '.';
  Uri = NULL;
  Collname = Options = Filter = NULL;
  Pipe = false;
  Driver = NULL;
  Version = 0;
  Wrapname = NULL;
} // end of BSONDEF constructor

/***********************************************************************/
/*  DefineAM: define specific AM block values.                         */
/***********************************************************************/
bool BSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
  G = g;
  Schema = GetStringCatInfo(g, "DBname", Schema);
  Jmode = (JMODE)GetIntCatInfo("Jmode", MODE_OBJECT);

  if ((Objname = GetStringCatInfo(g, "Object", NULL))) {
    if (*Objname == '$') Objname++;
    if (*Objname == '.') Objname++;
  } // endif Objname

  Xcol = GetStringCatInfo(g, "Expand", NULL);
  Pretty = GetIntCatInfo("Pretty", 2);
  Limit = GetIntCatInfo("Limit", 50);
  Base = GetIntCatInfo("Base", 0) ? 1 : 0;
  Sep = *GetStringCatInfo(g, "Separator", ".");
  Accept = GetBoolCatInfo("Accept", false);

  // Don't use url as MONGO uri when called from REST
  if (stricmp(am, "REST") && (Uri = GetStringCatInfo(g, "Connect", NULL))) {
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
    Collname = GetStringCatInfo(g, "Name",
      (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
    Collname = GetStringCatInfo(g, "Tabname", Collname);
    Options = GetStringCatInfo(g, "Colist", Xcol ? "all" : NULL);
    Filter = GetStringCatInfo(g, "Filter", NULL);
    Pipe = GetBoolCatInfo("Pipeline", false);
    Driver = GetStringCatInfo(g, "Driver", NULL);
    Version = GetIntCatInfo("Version", 3);
    Pretty = 0;
#if defined(JAVA_SUPPORT)
    if (Version == 2)
      Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo2Interface");
    else
      Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo3Interface");
#endif   // JAVA_SUPPORT
#else   // !MONGO_SUPPORT
    snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
    return true;
#endif  // !MONGO_SUPPORT
  } // endif Uri

  return DOSDEF::DefineAM(g, (Uri ? "XMGO" : "DOS"), poff);
} // end of DefineAM

/***********************************************************************/
/*  GetTable: makes a new Table Description Block.                     */
/***********************************************************************/
PTDB BSONDEF::GetTable(PGLOBAL g, MODE m)
{
  if (trace(1))
    htrc("BSON GetTable Pretty=%d Uri=%s\n", Pretty, SVP(Uri));

  if (Catfunc == FNC_COL)
    return new(g)TDBBCL(this);

  PTDBASE tdbp;
  PTXF    txfp = NULL;

  // JSN not used for pretty=1 for insert or delete
  if (Pretty <= 0 || (Pretty == 1 && (m == MODE_READ || m == MODE_UPDATE))) {
    USETEMP tmp = UseTemp();
    bool    map = Mapped && Pretty >= 0 && m != MODE_INSERT &&
      !(tmp != TMP_NO && m == MODE_UPDATE) &&
      !(tmp == TMP_FORCE && (m == MODE_UPDATE || m == MODE_DELETE));

    if (Lrecl) {
      // Allocate the parse work memory
      G = PlugInit(NULL, (size_t)Lrecl * (Pretty < 0 ? 3 : 5));
    } else {
      safe_strcpy(g->Message, sizeof(g->Message), "LRECL is not defined");
      return NULL;
    } // endif Lrecl

    if (Pretty < 0) {  // BJsonfile
      txfp = new(g) BINFAM(this);
    } else if (Uri) {
      if (Driver && toupper(*Driver) == 'C') {
#if defined(CMGO_SUPPORT)
        txfp = new(g) CMGFAM(this);
#else
        snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "C");
        return NULL;
#endif
      } else if (Driver && toupper(*Driver) == 'J') {
#if defined(JAVA_SUPPORT)
        txfp = new(g) JMGFAM(this);
#else
        snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "Java");
        return NULL;
#endif
      } else {             // Driver not specified
#if defined(CMGO_SUPPORT)
        txfp = new(g) CMGFAM(this);
#elif defined(JAVA_SUPPORT)
        txfp = new(g) JMGFAM(this);
#else   // !MONGO_SUPPORT
        snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
        return NULL;
#endif  // !MONGO_SUPPORT
      } // endif Driver

      Pretty = 4;   // Not a file
    } else if (Zipped) {
#if defined(ZIP_SUPPORT)
      if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
        txfp = new(g) UNZFAM(this);
      } else if (m == MODE_INSERT) {
        txfp = new(g) ZIPFAM(this);
      } else {
        safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
        return NULL;
      } // endif's m
#else   // !ZIP_SUPPORT
      snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
      return NULL;
#endif  // !ZIP_SUPPORT
    } else if (Compressed) {
#if defined(GZ_SUPPORT)
      if (Compressed == 1)
        txfp = new(g) GZFAM(this);
      else
        txfp = new(g) ZLBFAM(this);
#else   // !GZ_SUPPORT
      snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "GZ");
      return NULL;
#endif  // !GZ_SUPPORT
    } else if (map) {
      txfp = new(g) MAPFAM(this);
    } else
      txfp = new(g) DOSFAM(this);

    // Txfp must be set for TDBBSN
    tdbp = new(g) TDBBSN(g, this, txfp);
  } else {
    if (Zipped) {
#if defined(ZIP_SUPPORT)
      if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
        txfp = new(g) UNZFAM(this);
      } else if (m == MODE_INSERT) {
        safe_strcpy(g->Message, sizeof(g->Message), "INSERT supported only for zipped JSON when pretty=0");
        return NULL;
      } else {
        safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
        return NULL;
      } // endif's m
#else   // !ZIP_SUPPORT
      snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
      return NULL;
#endif  // !ZIP_SUPPORT
    } else
      txfp = new(g) MAPFAM(this);

    tdbp = new(g) TDBBSON(g, this, txfp);
  } // endif Pretty

  if (Multiple)
    tdbp = new(g) TDBMUL(tdbp);

  return tdbp;
} // end of GetTable

/* --------------------------- Class TDBBSN -------------------------- */

/***********************************************************************/
/*  Implementation of the TDBBSN class (Pretty < 2)                    */
/***********************************************************************/
TDBBSN::TDBBSN(PGLOBAL g, PBDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
{
  Bp = new(g) BTUTIL(tdp->G, this);
  Top = NULL;
  Row = NULL;
  Colp = NULL;

  if (tdp) {
    Jmode = tdp->Jmode;
    Objname = tdp->Objname;
    Xcol = tdp->Xcol;
    Limit = tdp->Limit;
    Pretty = tdp->Pretty;
    B = tdp->Base ? 1 : 0;
    Sep = tdp->Sep;
    Strict = tdp->Strict;
  } else {
    Jmode = MODE_OBJECT;
    Objname = NULL;
    Xcol = NULL;
    Limit = 1;
    Pretty = 0;
    B = 0;
    Sep = '.';
    Strict = false;
  } // endif tdp

  Fpos = -1;
  N = M = 0;
  NextSame = 0;
  SameRow = 0;
  Xval = -1;
  Comma = false;
  Bp->SetPretty(Pretty);
} // end of TDBBSN standard constructor

TDBBSN::TDBBSN(TDBBSN* tdbp) : TDBDOS(NULL, tdbp)
{
  Bp = tdbp->Bp;
  Top = tdbp->Top;
  Row = tdbp->Row;
  Colp = tdbp->Colp;
  Jmode = tdbp->Jmode;
  Objname = tdbp->Objname;
  Xcol = tdbp->Xcol;
  Fpos = tdbp->Fpos;
  N = tdbp->N;
  M = tdbp->M;
  Limit = tdbp->Limit;
  NextSame = tdbp->NextSame;
  SameRow = tdbp->SameRow;
  Xval = tdbp->Xval;
  B = tdbp->B;
  Sep = tdbp->Sep;
  Pretty = tdbp->Pretty;
  Strict = tdbp->Strict;
  Comma = tdbp->Comma;
}  // end of TDBBSN copy constructor

// Used for update
PTDB TDBBSN::Clone(PTABS t)
{
  PTDB    tp;
  PBSCOL  cp1, cp2;
  PGLOBAL g = t->G;

  tp = new(g) TDBBSN(this);

  for (cp1 = (PBSCOL)Columns; cp1; cp1 = (PBSCOL)cp1->GetNext()) {
    cp2 = new(g) BSONCOL(cp1, tp);  // Make a copy
    NewPointer(t, cp1, cp2);
  } // endfor cp1

  return tp;
} // end of Clone

/***********************************************************************/
/*  Allocate JSN column description block.                             */
/***********************************************************************/
PCOL TDBBSN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
{
  PBSCOL colp = new(g) BSONCOL(g, cdp, this, cprec, n);

  return (colp->ParseJpath(g)) ? NULL : colp;
} // end of MakeCol

/***********************************************************************/
/*  InsertSpecialColumn: Put a special column ahead of the column list.*/
/***********************************************************************/
PCOL TDBBSN::InsertSpecialColumn(PCOL colp)
{
  if (!colp->IsSpecial())
    return NULL;

  //if (Xcol && ((SPCBLK*)colp)->GetRnm())
  //  colp->SetKey(0);               // Rownum is no more a key

  colp->SetNext(Columns);
  Columns = colp;
  return colp;
} // end of InsertSpecialColumn

/***********************************************************************/
/*  JSON Cardinality: returns table size in number of rows.            */
/***********************************************************************/
int TDBBSN::Cardinality(PGLOBAL g)
{
  if (!g)
    return 0;
  else if (Cardinal < 0) {
    Cardinal = TDBDOS::Cardinality(g);

  } // endif Cardinal

  return Cardinal;
} // end of Cardinality

/***********************************************************************/
/*  JSON GetMaxSize: returns file size estimate in number of lines.    */
/***********************************************************************/
int TDBBSN::GetMaxSize(PGLOBAL g)
{
  if (MaxSize < 0)
    MaxSize = TDBDOS::GetMaxSize(g) * ((Xcol) ? Limit : 1);

  return MaxSize;
} // end of GetMaxSize

/***********************************************************************/
/*  JSON EstimatedLength. Returns an estimated minimum line length.    */
/***********************************************************************/
int TDBBSN::EstimatedLength(void)
{
  if (AvgLen <= 0)
    return (Lrecl ? Lrecl : 1024) / 8;  // TODO: make it better
  else
    return AvgLen;

} // end of Estimated Length

/***********************************************************************/
/*  OpenDB: Data Base open routine for BSN access method.              */
/***********************************************************************/
bool TDBBSN::OpenDB(PGLOBAL g)
{
  TUSE use = Use;

  if (Use == USE_OPEN) {
    /*******************************************************************/
    /*  Table already open replace it at its beginning.    ???         */
    /*******************************************************************/
    Fpos = -1;
    NextSame = 0;
    SameRow = 0;
  } // endif Use

    /*********************************************************************/
    /*  Open according to logical input/output mode required.            */
    /*********************************************************************/
  if (TDBDOS::OpenDB(g))
    return true;

  if (use == USE_OPEN)
    return false;

  if (Pretty < 0) {
    /*********************************************************************/
    /*  Binary BJSON table.                                              */
    /*********************************************************************/
    xtrc(1, "JSN OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
      this, Tdb_No, Use, Mode);

    // Lrecl is Ok
    size_t linelen = Lrecl;
    MODE   mode = Mode;

    // Buffer must be allocated in G->Sarea
    Mode = MODE_ANY;
    Txfp->AllocateBuffer(Bp->G);
    Mode = mode;

    if (Mode == MODE_INSERT)
      Bp->SubSet(true);
    else
      Bp->MemSave();

    To_Line = Txfp->GetBuf();
    memset(To_Line, 0, linelen);
    xtrc(1, "OpenJSN: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
  } // endif Pretty

  /***********************************************************************/
  /*  First opening.                                                     */
  /***********************************************************************/
  if (Mode == MODE_INSERT) {
    int type;

    switch (Jmode) {
      case MODE_OBJECT: type = TYPE_JOB;  break;
      case MODE_ARRAY:  type = TYPE_JAR;  break;
      case MODE_VALUE:  type = TYPE_JVAL; break;
      default:
        snprintf(g->Message, sizeof(g->Message), "Invalid Jmode %d", Jmode);
        return true;
    } // endswitch Jmode

    Top = Bp->MakeTopTree(g, type);
    Bp->MemSave();
  } // endif Mode

  if (Xcol)
    To_Filter = NULL;              // Not compatible

  return false;
} // end of OpenDB

/***********************************************************************/
/*  SkipHeader: Physically skip first header line if applicable.       */
/*  This is called from TDBDOS::OpenDB and must be executed before     */
/*  Kindex construction if the file is accessed using an index.        */
/***********************************************************************/
bool TDBBSN::SkipHeader(PGLOBAL g)
{
  int  len = GetFileLength(g);
  bool rc = false;

#if defined(_DEBUG)
  if (len < 0)
    return true;
#endif   // _DEBUG

  if (Pretty == 1) {
    if (Mode == MODE_INSERT || Mode == MODE_DELETE) {
      // Mode Insert and delete are no more handled here
      DBUG_ASSERT(false);
    } else if (len > 0) // !Insert && !Delete
      rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));

  } // endif Pretty

  return rc;
} // end of SkipHeader

/***********************************************************************/
/*  ReadDB: Data Base read routine for JSN access method.              */
/***********************************************************************/
int TDBBSN::ReadDB(PGLOBAL g)
{
  int   rc;

  N++;

  if (NextSame) {
    SameRow = NextSame;
    NextSame = 0;
    M++;
    return RC_OK;
  } else if ((rc = TDBDOS::ReadDB(g)) == RC_OK) {
    if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK))
      return rc; // Deferred reading failed

    if (Pretty >= 0) {
      // Recover the memory used for parsing
      Bp->SubSet();

      if ((Row = Bp->ParseLine(g, Pretty, Comma))) {
        Top = Row;
        Row = Bp->FindRow(g);
        SameRow = 0;
        Fpos++;
        M = 1;
        rc = RC_OK;
      } else if (Pretty != 1 || strcmp(To_Line, "]")) {
        Bp->GetMsg(g);
        rc = RC_FX;
      } else
        rc = RC_EF;

    } else { // Here we get a movable Json binary tree
      Bp->MemSet(((BINFAM*)Txfp)->Recsize);  // Useful when updating
      Row = Top = (PBVAL)To_Line;
      Row = Bp->FindRow(g);
      SameRow = 0;
      Fpos++;
      M = 1;
      rc = RC_OK;
    } // endif Pretty

  } // endif ReadDB

  return rc;
} // end of ReadDB

/***********************************************************************/
/*  PrepareWriting: Prepare the line for WriteDB.                      */
/***********************************************************************/
bool TDBBSN::PrepareWriting(PGLOBAL g)
{
  if (Pretty >= 0) {
    PSZ s;

//  if (!(Top = Bp->MakeTopTree(g, Row->Type)))
//    return true;

    if ((s = Bp->SerialVal(g, Top, Pretty))) {
      if (Comma)
        strcat(s, ",");

      if ((signed)strlen(s) > Lrecl) {
        safe_strcpy(To_Line, Lrecl, s);
        snprintf(g->Message, sizeof(g->Message), "Line truncated (lrecl=%d)", Lrecl);
        return PushWarning(g, this);
      } else
        strcpy(To_Line, s);

      return false;
    } else
      return true;
  } else
    ((BINFAM*)Txfp)->Recsize = ((size_t)PlugSubAlloc(Bp->G, NULL, 0)
                              - (size_t)To_Line);
  return false;
} // end of PrepareWriting

/***********************************************************************/
/*  WriteDB: Data Base write routine for JSON access method.           */
/***********************************************************************/
int TDBBSN::WriteDB(PGLOBAL g) {
  int rc = TDBDOS::WriteDB(g);

  Bp->SubSet();
  Bp->Clear(Row);
  return rc;
} // end of WriteDB

/***********************************************************************/
/*  Data Base close routine for JSON access method.                    */
/***********************************************************************/
void TDBBSN::CloseDB(PGLOBAL g)
{
  TDBDOS::CloseDB(g);
  Bp->G = PlugExit(Bp->G);                  
} // end of CloseDB

/* ---------------------------- BSONCOL ------------------------------ */

/***********************************************************************/
/*  BSONCOL public constructor.                                        */
/***********************************************************************/
BSONCOL::BSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
        : DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
{
  Tbp = (TDBBSN*)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
  Cp = new(g) BCUTIL(((PBDEF)Tbp->To_Def)->G, this, Tbp);
  Jpath = cdp->GetFmt();
  MulVal = NULL;
  Nodes = NULL;
  Nod = 0;
  Sep = Tbp->Sep;
  Xnod = -1;
  Xpd = false;
  Parsed = false;
  Warned = false;
  Sgfy = false;
} // end of BSONCOL constructor

/***********************************************************************/
/*  BSONCOL constructor used for copying columns.                      */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
BSONCOL::BSONCOL(BSONCOL* col1, PTDB tdbp) : DOSCOL(col1, tdbp)
{
  Tbp = col1->Tbp;
  Cp = col1->Cp;
  Jpath = col1->Jpath;
  MulVal = col1->MulVal;
  Nodes = col1->Nodes;
  Nod = col1->Nod;
  Sep = col1->Sep;
  Xnod = col1->Xnod;
  Xpd = col1->Xpd;
  Parsed = col1->Parsed;
  Warned = col1->Warned;
  Sgfy = col1->Sgfy;
} // end of BSONCOL copy constructor

/***********************************************************************/
/*  SetBuffer: prepare a column block for write operation.             */
/***********************************************************************/
bool BSONCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
{
  if (DOSCOL::SetBuffer(g, value, ok, check))
    return true;

  // Parse the json path
  if (ParseJpath(g))
    return true;

  Tbp = (TDBBSN*)To_Tdb;
  return false;
} // end of SetBuffer

/***********************************************************************/
/*  Check whether this object is expanded.                             */
/***********************************************************************/
bool BSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
{
  if ((Tbp->Xcol && nm && !strcmp(nm, Tbp->Xcol) &&
    (Tbp->Xval < 0 || Tbp->Xval == i)) || Xpd) {
    Xpd = true;              // Expandable object
    Nodes[i].Op = OP_EXP;
  } else if (b) {
    safe_strcpy(g->Message, sizeof(g->Message), "Cannot expand more than one branch");
    return true;
  } // endif Xcol

  return false;
} // end of CheckExpand

/***********************************************************************/
/*  Analyse array processing options.                                  */
/***********************************************************************/
bool BSONCOL::SetArrayOptions(PGLOBAL g, char* p, int i, PSZ nm)
{
  int    n;
  bool   dg = true, b = false;
  PJNODE jnp = &Nodes[i];

  //if (*p == '[') p++;    // Old syntax .[ or :[
  n = (int)strlen(p);

  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 for %s", p, Name);
      return true;
    } // endif p

  } else
    b = true;

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

  if (!n) {
    // Default specifications
    if (CheckExpand(g, i, nm, false))
      return true;
    else if (jnp->Op != OP_EXP) {
      if (b) {
        // Return 1st value (B is the index base)
        jnp->Rank = Tbp->B;
        jnp->Op = OP_EQ;
      } else if (!Value->IsTypeNum()) {
        jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING);
        jnp->Op = OP_CNC;
      } else
        jnp->Op = OP_ADD;

    } // endif OP

  } else if (dg) {
    // Return nth value
    jnp->Rank = atoi(p) - Tbp->B;
    jnp->Op = OP_EQ;
  } else if (n == 1) {
    // Set the Op value;
    if (Sep == ':')
      switch (*p) {
      case '*': *p = 'x'; break;
      case 'x':
      case 'X': *p = '*'; break; // Expand this array
      default: break;
      } // endswitch p

    switch (*p) {
    case '+': jnp->Op = OP_ADD;  break;
    case 'x': jnp->Op = OP_MULT; break;
    case '>': jnp->Op = OP_MAX;  break;
    case '<': jnp->Op = OP_MIN;  break;
    case '!': jnp->Op = OP_SEP;  break; // Average
    case '#': jnp->Op = OP_NUM;  break;
    case '*': // Expand this array
      if (!Tbp->Xcol && nm) {
        Xpd = true;
        jnp->Op = OP_EXP;
        Tbp->Xval = i;
        Tbp->Xcol = nm;
      } else if (CheckExpand(g, i, nm, true))
        return true;

      break;
    default:
      snprintf(g->Message, sizeof(g->Message),
        "Invalid function specification %c for %s", *p, Name);
      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;
      jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
    } // endif n

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

  // For calculated arrays, a local Value must be used
  switch (jnp->Op) {
  case OP_NUM:
    jnp->Valp = AllocateValue(g, TYPE_INT);
    break;
  case OP_ADD:
  case OP_MULT:
  case OP_SEP:
    if (!IsTypeChar(Buf_Type))
      jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
    else
      jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);

    break;
  case OP_MIN:
  case OP_MAX:
    jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
    break;
  case OP_CNC:
    if (IsTypeChar(Buf_Type))
      jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
    else
      jnp->Valp = AllocateValue(g, TYPE_STRING, 512);

    break;
  default:
    break;
  } // endswitch Op

  if (jnp->Valp)
    MulVal = AllocateValue(g, jnp->Valp);

  return false;
} // end of SetArrayOptions

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

  if (Parsed)
    return false;                       // Already done
  else if (InitValue(g))
    return true;
  else if (!Jpath)
    Jpath = Name;

  if (To_Tdb->GetOrig()) {
    // This is an updated column, get nodes from origin
    for (PBSCOL colp = (PBSCOL)Tbp->GetColumns(); colp;
      colp = (PBSCOL)colp->GetNext())
      if (!stricmp(Name, colp->GetName())) {
        Nod = colp->Nod;
        Nodes = colp->Nodes;
        Xpd = colp->Xpd;
        goto fin;
      } // endif Name

    snprintf(g->Message, sizeof(g->Message), "Cannot parse updated column %s", Name);
    return true;
  } // endif To_Orig

  pbuf = PlugDup(g, Jpath);
  if (*pbuf == '$') pbuf++;
  if (*pbuf == Sep) pbuf++;
  if (*pbuf == '[') p1 = pbuf++;

  // Estimate the required number of nodes
  for (i = 0, p = pbuf; (p = NextChr(p, Sep)); i++, p++)
    Nod++;                         // One path node found

  Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE));
  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, Sep);

    if (!p2)
      p2 = p1;
    else if (p1) {
      if (p1 < p2)
        p2 = p1;
      else if (p1 == p2 + 1)
        *p2++ = 0;     // Old syntax .[ or :[
      else
        p1 = NULL;

    } // endif p1

    if (p2)
      *p2++ = 0;

    // Jpath must be explicit
    if (a || *p == 0 || *p == '[' || IsNum(p)) {
      // Analyse intermediate array processing
      if (SetArrayOptions(g, p, i, Nodes[i - 1].Key))
        return true;
      else if (Xpd && Tbp->Mode == MODE_DELETE) {
        safe_strcpy(g->Message, sizeof(g->Message), "Cannot delete expanded columns");
        return true;
      } // endif Xpd

    } else if (*p == '*') {
      // Return JSON
      Nodes[i].Op = OP_XX;
    } else {
      Nodes[i].Key = p;
      Nodes[i].Op = OP_EXIST;
    } // endif's

  } // endfor i, p

  Nod = i;

fin:
  MulVal = AllocateValue(g, Value);
  Parsed = true;
  return false;
} // end of ParseJpath

/***********************************************************************/
/*  Get Jpath converted to Mongo path.                                 */
/***********************************************************************/
PSZ BSONCOL::GetJpath(PGLOBAL g, bool proj)
{
  if (Jpath) {
    char* p1, * p2, * mgopath;
    int   i = 0;

    if (strcmp(Jpath, "*")) {
      p1 = Jpath;
      if (*p1 == '$') p1++;
      if (*p1 == '.') p1++;
      mgopath = PlugDup(g, p1);
    } else {
      Sgfy = true;
      return NULL;
    } // endif

    for (p1 = p2 = mgopath; *p1; p1++)
    {
      if (i) {                 // Inside []
        if (isdigit(*p1)) {
          if (!proj)
            *p2++ = *p1;

        } else if (*p1 == ']' && i == 1) {
          if (proj && p1[1] == '.')
            p1++;

          i = 0;
        } else if (*p1 == '.' && i == 2) {
          if (!proj)
            *p2++ = '.';

          i = 0;
        } else if (!proj)
          return NULL;

      } else switch (*p1) {
      case ':':
      case '.':
        if (isdigit(p1[1]))
          i = 2;

        *p2++ = '.';
        break;
      case '[':
        if (*(p2 - 1) != '.')
          *p2++ = '.';

        i = 1;
        break;
      case '*':
        if (*(p2 - 1) == '.' && !*(p1 + 1)) {
          p2--;              // Suppress last :*
          Sgfy = true;
          break;
        } // endif p2
        /* fall through */
      default:
        *p2++ = *p1;
        break;
      } // endswitch p1;
    }

    if (*(p2 - 1) == '.')
      p2--;

    *p2 = 0;
    return mgopath;
  } else
    return NULL;

} // end of GetJpath

/***********************************************************************/
/*  ReadColumn:                                                        */
/***********************************************************************/
void BSONCOL::ReadColumn(PGLOBAL g)
{
  if (!Tbp->SameRow || Xnod >= Tbp->SameRow)
    Value->SetValue_pval(Cp->GetColumnValue(g, Tbp->Row, 0));

#if defined(DEVELOPMENT)
  if (Xpd && Value->IsNull() && !((PBDEF)Tbp->To_Def)->Accept)
    htrc("Null expandable JSON value for column %s\n", Name);
#endif   // DEVELOPMENT

  // Set null when applicable
  if (!Nullable)
    Value->SetNull(false);

} // end of ReadColumn

/***********************************************************************/
/*  WriteColumn:                                                       */
/***********************************************************************/
void BSONCOL::WriteColumn(PGLOBAL g)
{
  if (Xpd && Tbp->Pretty < 2) {
    safe_strcpy(g->Message, sizeof(g->Message), "Cannot write expanded column when Pretty is not 2");
    throw 666;
  } // endif Xpd

  /*********************************************************************/
  /*  Check whether this node must be written.                         */
  /*********************************************************************/
  if (Value != To_Val)
    Value->SetValue_pval(To_Val, FALSE);    // Convert the updated value

  /*********************************************************************/
  /*  On INSERT Null values are represented by no node.                */
  /*********************************************************************/
  if (Value->IsNull() && Tbp->Mode == MODE_INSERT)
    return;

  PBVAL jsp, row = Cp->GetRow(g);

  if (row) switch (Buf_Type) {
  case TYPE_STRING:
  case TYPE_DATE:
  case TYPE_INT:
  case TYPE_TINY:
  case TYPE_SHORT:
  case TYPE_BIGINT:
  case TYPE_DOUBLE:
    if (Buf_Type == TYPE_STRING && Nodes[Nod - 1].Op == OP_XX) {
      char *s = Value->GetCharValue();

      if (!(jsp = Cp->ParseJson(g, s, strlen(s)))) {
        safe_strcpy(g->Message, sizeof(g->Message), s);
        throw 666;
      } // endif jsp

      switch (row->Type) {
        case TYPE_JAR:
          if (Nod > 1 && Nodes[Nod - 2].Op == OP_EQ)
            Cp->SetArrayValue(row, jsp, Nodes[Nod - 2].Rank);
          else
            Cp->AddArrayValue(row, jsp);

          break;
        case TYPE_JOB:  
          if (Nod > 1 && Nodes[Nod - 2].Key)
            Cp->SetKeyValue(row, jsp, Nodes[Nod - 2].Key);

          break;
        case TYPE_JVAL:
        default: 
          Cp->SetValueVal(row, jsp);
      } // endswitch Type

      break;
    } else
      jsp = Cp->NewVal(Value);

    switch (row->Type) {
      case TYPE_JAR:
        if (Nodes[Nod - 1].Op == OP_EQ)
          Cp->SetArrayValue(row, jsp, Nodes[Nod - 1].Rank);
        else
          Cp->AddArrayValue(row, jsp);

        break;
      case TYPE_JOB:
        if (Nodes[Nod - 1].Key)
          Cp->SetKeyValue(row, jsp, Nodes[Nod - 1].Key);

        break;
      case TYPE_JVAL:
      default:
        Cp->SetValueVal(row, jsp);
    } // endswitch Type

    break;
  default:                  // ??????????
    snprintf(g->Message, sizeof(g->Message), "Invalid column type %d", Buf_Type);
  } // endswitch Type

} // end of WriteColumn

/* -------------------------- Class TDBBSON -------------------------- */

/***********************************************************************/
/*  Implementation of the TDBBSON class.                               */
/***********************************************************************/
TDBBSON::TDBBSON(PGLOBAL g, PBDEF tdp, PTXF txfp) : TDBBSN(g, tdp, txfp)
{
  Docp = NULL;
  Docrow = NULL;
  Multiple = tdp->Multiple;
  Docsize = 0;
  Done = Changed = false;
  Bp->SetPretty(2);
} // end of TDBBSON standard constructor

TDBBSON::TDBBSON(PBTDB tdbp) : TDBBSN(tdbp)
{
  Docp = tdbp->Docp;
  Docrow = tdbp->Docrow;
  Multiple = tdbp->Multiple;
  Docsize = tdbp->Docsize;
  Done = tdbp->Done;
  Changed = tdbp->Changed;
} // end of TDBBSON copy constructor

// Used for update
PTDB TDBBSON::Clone(PTABS t)
{
  PTDB    tp;
  PBSCOL   cp1, cp2;
  PGLOBAL g = t->G;

  tp = new(g) TDBBSON(this);

  for (cp1 = (PBSCOL)Columns; cp1; cp1 = (PBSCOL)cp1->GetNext()) {
    cp2 = new(g) BSONCOL(cp1, tp);  // Make a copy
    NewPointer(t, cp1, cp2);
  } // endfor cp1

  return tp;
} // end of Clone

/***********************************************************************/
/*  Make the document tree from the object path.                       */
/***********************************************************************/
int TDBBSON::MakeNewDoc(PGLOBAL g)
{
  // Create a void table that will be populated
  Docp = Bp->NewVal(TYPE_JAR);

  if (!(Top = Bp->MakeTopTree(g, TYPE_JAR)))
    return RC_FX;

  Docp = Row;
  Done = true;
  return RC_OK;
} // end of MakeNewDoc

/***********************************************************************/
/*  Make the document tree from a file.                                */
/***********************************************************************/
int TDBBSON::MakeDocument(PGLOBAL g)
{
  char   *p, *p1, *p2, *memory, *objpath, *key = NULL;
  int     i = 0;
  size_t  len;
  my_bool a;
  MODE    mode = Mode;
  PBVAL   jsp;
  PBVAL   objp = NULL;
  PBVAL   arp = NULL;
  PBVAL   val = NULL;

  if (Done)
    return RC_OK;

  /*********************************************************************/
  /*  Create the mapping file object in mode read.                     */
  /*********************************************************************/
  Mode = MODE_READ;

  if (!Txfp->OpenTableFile(g)) {
    PFBLOCK fp = Txfp->GetTo_Fb();

    if (fp) {
      len = fp->Length;
      memory = fp->Memory;
    } else {
      Mode = mode;         // Restore saved Mode
      return MakeNewDoc(g);
    } // endif fp

  } else
    return RC_FX;

  /*********************************************************************/
  /*  Parse the json file and allocate its tree structure.             */
  /*********************************************************************/
  g->Message[0] = 0;
  jsp = Top = Bp->ParseJson(g, memory, len);
  Txfp->CloseTableFile(g, false);
  Mode = mode;             // Restore saved Mode

  if (!jsp && g->Message[0])
    return RC_FX;

  if ((objpath = PlugDup(g, Objname))) {
    p1 = (*objpath == '[') ? objpath++ : NULL;

    /*********************************************************************/
    /*  Find the table in the tree structure.                            */
    /*********************************************************************/
    for (p = objpath; jsp && p; 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;

      if (!a && *p && *p != '[' && !IsNum(p)) {
        // obj is a key
        if (jsp->Type != TYPE_JOB) {
          safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
          return RC_FX;
        } // endif Type

        key = p;
        objp = jsp;
        arp = NULL;
        val = Bp->GetKeyValue(objp, key);

        if (!val || !(jsp = Bp->GetBson(val))) {
          snprintf(g->Message, sizeof(g->Message), "Cannot find object key %s", key);
          return RC_FX;
        } // endif val

      } else {
        if (*p == '[') {
          // Old style
          if (p[strlen(p) - 1] != ']') {
            snprintf(g->Message, sizeof(g->Message), "Invalid Table path near %s", p);
            return RC_FX;
          } else
            p++;

        } // endif p

        if (jsp->Type != TYPE_JAR) {
          safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
          return RC_FX;
        } // endif Type

        arp = jsp;
        objp = NULL;
        i = atoi(p) - B;
        val = Bp->GetArrayValue(arp, i);

        if (!val) {
          snprintf(g->Message, sizeof(g->Message), "Cannot find array value %d", i);
          return RC_FX;
        } // endif val

      } // endif

      jsp = val;
    } // endfor p

  } // endif objpath

  if (jsp && jsp->Type == TYPE_JAR)
    Docp = jsp;
  else {
    // The table is void or is just one object or one value
    if (objp) {
      Docp = Bp->GetKeyValue(objp, key);
      Docp->To_Val = Bp->MOF(Bp->DupVal(Docp));
      Docp->Type = TYPE_JAR;
    } else if (arp) {
      Docp = Bp->NewVal(TYPE_JAR);
      Bp->AddArrayValue(Docp, jsp);
      Bp->SetArrayValue(arp, Docp, i);
    } else {
      Top = Docp = Bp->NewVal(TYPE_JAR);
      Bp->AddArrayValue(Docp, jsp);
    } // endif's

  } // endif jsp

  Docsize = Bp->GetSize(Docp);
  Done = true;
  return RC_OK;
} // end of MakeDocument

/***********************************************************************/
/*  JSON Cardinality: returns table size in number of rows.            */
/***********************************************************************/
int TDBBSON::Cardinality(PGLOBAL g)
{
  if (!g)
    return (Xcol || Multiple) ? 0 : 1;
  else if (Cardinal < 0) {
    if (!Multiple) {
      if (MakeDocument(g) == RC_OK)
        Cardinal = Docsize;

    } else
      return 10;

  } // endif Cardinal

  return Cardinal;
} // end of Cardinality

/***********************************************************************/
/*  JSON GetMaxSize: returns table size estimate in number of rows.    */
/***********************************************************************/
int TDBBSON::GetMaxSize(PGLOBAL g)
{
  if (MaxSize < 0)
    MaxSize = Cardinality(g) * ((Xcol) ? Limit : 1);

  return MaxSize;
} // end of GetMaxSize

/***********************************************************************/
/*  ResetSize: call by TDBMUL when calculating size estimate.          */
/***********************************************************************/
void TDBBSON::ResetSize(void)
{
  MaxSize = Cardinal = -1;
  Fpos = -1;
  N = 0;
  Docrow = NULL;
  Done = false;
} // end of ResetSize

/***********************************************************************/
/*  TDBBSON is not indexable.                                          */
/***********************************************************************/
int TDBBSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool)
{
  if (pxdf) {
    safe_strcpy(g->Message, sizeof(g->Message), "JSON not indexable when pretty = 2");
    return RC_FX;
  } else
    return RC_OK;

} // end of MakeIndex

/***********************************************************************/
/*  Return the position in the table.                                  */
/***********************************************************************/
int TDBBSON::GetRecpos(void)
{
#if 0
  union {
    uint Rpos;
    BYTE Spos[4];
  };

  Rpos = htonl(Fpos);
  Spos[0] = (BYTE)NextSame;
  return Rpos;
#endif // 0
  return Fpos;
} // end of GetRecpos

/***********************************************************************/
/*  Set the position in the table.                                  */
/***********************************************************************/
bool TDBBSON::SetRecpos(PGLOBAL, int recpos)
{
#if 0
  union {
    uint Rpos;
    BYTE Spos[4];
  };

  Rpos = recpos;
  NextSame = Spos[0];
  Spos[0] = 0;
  Fpos = (signed)ntohl(Rpos);

  //if (Fpos != (signed)ntohl(Rpos)) {
  //  Fpos = ntohl(Rpos);
  //  same = false;
  //} else
  //  same = true;
#endif // 0

  Fpos = recpos - 1;
  Docrow = NULL;
  return false;
} // end of SetRecpos

/***********************************************************************/
/*  JSON Access Method opening routine.                                */
/***********************************************************************/
bool TDBBSON::OpenDB(PGLOBAL g)
{
  if (Use == USE_OPEN) {
    /*******************************************************************/
    /*  Table already open replace it at its beginning.                */
    /*******************************************************************/
    Fpos = -1;
    NextSame = false;
    SameRow = 0;
    Docrow = NULL;
    return false;
  } // endif use

/*********************************************************************/
/*  OpenDB: initialize the JSON file processing.                     */
/*********************************************************************/
  if (MakeDocument(g) != RC_OK)
    return true;

  if (Mode == MODE_INSERT)
    switch (Jmode) {
    case MODE_OBJECT: Row = Bp->NewVal(TYPE_JOB);  break;
    case MODE_ARRAY:  Row = Bp->NewVal(TYPE_JAR);  break;
    case MODE_VALUE:  Row = Bp->NewVal(TYPE_JVAL); break;
    default:
      snprintf(g->Message, sizeof(g->Message), "Invalid Jmode %d", Jmode);
      return true;
    } // endswitch Jmode

  if (Xcol)
    To_Filter = NULL;              // Imcompatible

  Use = USE_OPEN;
  return false;
} // end of OpenDB

/***********************************************************************/
/*  ReadDB: Data Base read routine for JSON access method.             */
/***********************************************************************/
int TDBBSON::ReadDB(PGLOBAL)
{
  int rc;

  N++;

  if (NextSame) {
    SameRow = NextSame;
    NextSame = false;
    M++;
    rc = RC_OK;
  } else if (++Fpos < Docsize) {
    Docrow = (Docrow) ? Bp->GetNext(Docrow) : Bp->GetArrayValue(Docp, Fpos);
    Row = (Docrow->Type == TYPE_JVAL) ? Bp->GetBson(Docrow) : Docrow;
    SameRow = 0;
    M = 1;
    rc = RC_OK;
  } else
    rc = RC_EF;

  return rc;
} // end of ReadDB

/***********************************************************************/
/*  WriteDB: Data Base write routine for JSON access method.           */
/***********************************************************************/
int TDBBSON::WriteDB(PGLOBAL g)
{
  if (Mode == MODE_INSERT) {
    Bp->AddArrayValue(Docp, Row);

    switch(Jmode) {
    case MODE_OBJECT: Row = Bp->NewVal(TYPE_JOB); break;
    case MODE_ARRAY:  Row = Bp->NewVal(TYPE_JAR); break;
    default:          Row = Bp->NewVal();         break;
    } // endswitch Jmode

  } else
    Bp->SetArrayValue(Docp, Row, Fpos);

  Changed = true;
  return RC_OK;
} // end of WriteDB

/***********************************************************************/
/*  Data Base delete line routine for JSON access method.              */
/***********************************************************************/
int TDBBSON::DeleteDB(PGLOBAL g, int irc)
{
  if (irc == RC_OK)
    // Deleted current row
    Bp->DeleteValue(Docp, Fpos);
  else if (irc == RC_FX)
    // Delete all
    Docp->To_Val = 0;

  Changed = true;
  return RC_OK;
} // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for JSON access methods.                   */
/***********************************************************************/
void TDBBSON::CloseDB(PGLOBAL g)
{
  if (!Changed)
    return;

  // Save the modified document
  char filename[_MAX_PATH];

//Docp->InitArray(g);

  // We used the file name relative to recorded datapath
  PlugSetPath(filename, ((PBDEF)To_Def)->Fn, GetPath());

  // Serialize the modified table
  if (!Bp->Serialize(g, Top, filename, Pretty))
    puts(g->Message);

} // end of CloseDB

/* ---------------------------TDBBCL class --------------------------- */

/***********************************************************************/
/*  TDBBCL class constructor.                                          */
/***********************************************************************/
TDBBCL::TDBBCL(PBDEF tdp) : TDBCAT(tdp) {
  Topt = tdp->GetTopt();
  Db = tdp->Schema;
  Dsn = tdp->Uri;
} // end of TDBBCL constructor

/***********************************************************************/
/*  GetResult: Get the list the JSON file columns.                     */
/***********************************************************************/
PQRYRES TDBBCL::GetResult(PGLOBAL g) {
  return BSONColumns(g, Db, Dsn, Topt, false);
} // end of GetResult

/* --------------------------- End of json --------------------------- */

Messung V0.5 in Prozent
C=79 H=95 G=87

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






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1867298