1 /************* tabbson C++ Program Source Code File (.CPP) *************/
2 /* PROGRAM NAME: tabbson     Version 1.2                               */
3 /*  (C) Copyright to the author Olivier BERTRAND          2020 - 2021  */
4 /*  This program are the BSON class DB execution routines.             */
5 /***********************************************************************/
6 
7 /***********************************************************************/
8 /*  Include relevant sections of the MariaDB header file.              */
9 /***********************************************************************/
10 #include <my_global.h>
11 
12 /***********************************************************************/
13 /*  Include application header files:                                  */
14 /*  global.h    is header containing all global declarations.          */
15 /*  plgdbsem.h  is header containing the DB application declarations.  */
16 /*  tdbdos.h    is header containing the TDBDOS declarations.          */
17 /*  json.h      is header containing the JSON classes declarations.    */
18 /***********************************************************************/
19 #include "global.h"
20 #include "plgdbsem.h"
21 #include "maputil.h"
22 #include "filamtxt.h"
23 #include "tabdos.h"
24 #include "tabbson.h"
25 #include "filamap.h"
26 #if defined(GZ_SUPPORT)
27 #include "filamgz.h"
28 #endif   // GZ_SUPPORT
29 #if defined(ZIP_SUPPORT)
30 #include "filamzip.h"
31 #endif   // ZIP_SUPPORT
32 #if defined(JAVA_SUPPORT)
33 #include "jmgfam.h"
34 #endif   // JAVA_SUPPORT
35 #if defined(CMGO_SUPPORT)
36 #include "cmgfam.h"
37 #endif   // CMGO_SUPPORT
38 #include "tabmul.h"
39 #include "checklvl.h"
40 #include "resource.h"
41 #include "mycat.h"                             // for FNC_COL
42 
43 /***********************************************************************/
44 /*  This should be an option.                                          */
45 /***********************************************************************/
46 #define MAXCOL          200        /* Default max column nb in result  */
47 //#define TYPE_UNKNOWN     12        /* Must be greater than other types */
48 
49 /***********************************************************************/
50 /*  External functions.                                                */
51 /***********************************************************************/
52 USETEMP UseTemp(void);
53 bool    JsonAllPath(void);
54 int     GetDefaultDepth(void);
55 char   *GetJsonNull(void);
56 bool    Stringified(PCSZ, char*);
57 
58 /***********************************************************************/
59 /* BSONColumns: construct the result blocks containing the description */
60 /* of all the columns of a table contained inside a JSON file.         */
61 /***********************************************************************/
62 PQRYRES BSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
63 {
64   static int  buftyp[] = { TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
65                           TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING };
66   static XFLD fldtyp[] = { FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
67                           FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT };
68   static unsigned int length[] = { 0, 6, 8, 10, 10, 6, 6, 0 };
69   int     i, n = 0;
70   int     ncol = sizeof(buftyp) / sizeof(int);
71   PJCL    jcp;
72   BSONDISC* pjdc = NULL;
73   PQRYRES qrp;
74   PCOLRES crp;
75 
76   if (info) {
77     length[0] = 128;
78     length[7] = 256;
79     goto skipit;
80   } // endif info
81 
82   if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
83     strcpy(g->Message, "Cannot find column definition for multiple table");
84     return NULL;
85   } // endif Multiple
86 
87   pjdc = new(g) BSONDISC(g, length);
88 
89   if (!(n = pjdc->GetColumns(g, db, dsn, topt)))
90     return NULL;
91 
92 skipit:
93   if (trace(1))
94     htrc("BSONColumns: n=%d len=%d\n", n, length[0]);
95 
96   /*********************************************************************/
97   /*  Allocate the structures used to refer to the result set.         */
98   /*********************************************************************/
99   qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
100     buftyp, fldtyp, length, false, false);
101 
102   crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
103   crp->Name = PlugDup(g, "Nullable");
104   crp->Next->Name = PlugDup(g, "Jpath");
105 
106   if (info || !qrp)
107     return qrp;
108 
109   qrp->Nblin = n;
110 
111   /*********************************************************************/
112   /*  Now get the results into blocks.                                 */
113   /*********************************************************************/
114   for (i = 0, jcp = pjdc->fjcp; jcp; i++, jcp = jcp->Next) {
115     if (jcp->Type == TYPE_UNKNOWN)
116       jcp->Type = TYPE_STRG;               // Void column
117 
118     crp = qrp->Colresp;                    // Column Name
119     crp->Kdata->SetValue(jcp->Name, i);
120     crp = crp->Next;                       // Data Type
121     crp->Kdata->SetValue(jcp->Type, i);
122     crp = crp->Next;                       // Type Name
123     crp->Kdata->SetValue(GetTypeName(jcp->Type), i);
124     crp = crp->Next;                       // Precision
125     crp->Kdata->SetValue(jcp->Len, i);
126     crp = crp->Next;                       // Length
127     crp->Kdata->SetValue(jcp->Len, i);
128     crp = crp->Next;                       // Scale (precision)
129     crp->Kdata->SetValue(jcp->Scale, i);
130     crp = crp->Next;                       // Nullable
131     crp->Kdata->SetValue(jcp->Cbn ? 1 : 0, i);
132     crp = crp->Next;                       // Field format
133 
134     if (crp->Kdata)
135       crp->Kdata->SetValue(jcp->Fmt, i);
136 
137   } // endfor i
138 
139 /*********************************************************************/
140 /*  Return the result pointer.                                       */
141 /*********************************************************************/
142   return qrp;
143 } // end of BSONColumns
144 
145 /* -------------------------- Class BSONDISC ------------------------- */
146 
147 /***********************************************************************/
148 /*  Class used to get the columns of a JSON table.                     */
149 /***********************************************************************/
150 BSONDISC::BSONDISC(PGLOBAL g, uint* lg)
151 {
152   length = lg;
153   jcp = fjcp = pjcp = NULL;
154   tdp = NULL;
155   tjnp = NULL;
156   jpp = NULL;
157   tjsp = NULL;
158   jsp = NULL;
159   bp = NULL;
160   row = NULL;
161   sep = NULL;
162   strfy = NULL;
163   i = n = bf = ncol = lvl = sz = limit = 0;
164   all = false;
165 } // end of BSONDISC constructor
166 
167 int BSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
168 {
169   char  filename[_MAX_PATH];
170   bool  mgo = (GetTypeID(topt->type) == TAB_MONGO);
171   PBVAL bdp = NULL;
172 
173   lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
174   lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
175   sep = GetStringTableOption(g, topt, "Separator", ".");
176   sz = GetIntegerTableOption(g, topt, "Jsize", 1024);
177   limit = GetIntegerTableOption(g, topt, "Limit", 50);
178   strfy = GetStringTableOption(g, topt, "Stringify", NULL);
179 
180   /*********************************************************************/
181   /*  Open the input file.                                             */
182   /*********************************************************************/
183   tdp = new(g) BSONDEF;
184   tdp->G = NULL;
185 #if defined(ZIP_SUPPORT)
186   tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
187   tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
188 #endif   // ZIP_SUPPORT
189   tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);
190 
191   if (!tdp->Fn && topt->http)
192     tdp->Fn = GetStringTableOption(g, topt, "Subtype", NULL);
193 
194   if (!(tdp->Database = SetPath(g, db)))
195     return 0;
196 
197   if ((tdp->Objname = GetStringTableOption(g, topt, "Object", NULL))) {
198     if (*tdp->Objname == '$') tdp->Objname++;
199     if (*tdp->Objname == '.') tdp->Objname++;
200   } // endif Objname
201 
202   tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0;
203   tdp->Pretty = GetIntegerTableOption(g, topt, "Pretty", 2);
204   tdp->Xcol = GetStringTableOption(g, topt, "Expand", NULL);
205   tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false);
206   tdp->Uri = (dsn && *dsn ? dsn : NULL);
207 
208   if (!tdp->Fn && !tdp->Uri) {
209     strcpy(g->Message, MSG(MISSING_FNAME));
210     return 0;
211   } else
212     topt->subtype = NULL;
213 
214   if (tdp->Fn) {
215     //  We used the file name relative to recorded datapath
216     PlugSetPath(filename, tdp->Fn, tdp->GetPath());
217     tdp->Fn = PlugDup(g, filename);
218   } // endif Fn
219 
220   if (trace(1))
221     htrc("File %s objname=%s pretty=%d lvl=%d\n",
222       tdp->Fn, tdp->Objname, tdp->Pretty, lvl);
223 
224   if (tdp->Uri) {
225 #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
226     tdp->Collname = GetStringTableOption(g, topt, "Tabname", NULL);
227     tdp->Schema = GetStringTableOption(g, topt, "Dbname", "test");
228     tdp->Options = (PSZ)GetStringTableOption(g, topt, "Colist", "all");
229     tdp->Pipe = GetBooleanTableOption(g, topt, "Pipeline", false);
230     tdp->Driver = (PSZ)GetStringTableOption(g, topt, "Driver", NULL);
231     tdp->Version = GetIntegerTableOption(g, topt, "Version", 3);
232     tdp->Wrapname = (PSZ)GetStringTableOption(g, topt, "Wrapper",
233       (tdp->Version == 2) ? "Mongo2Interface" : "Mongo3Interface");
234     tdp->Pretty = 0;
235 #else   // !MONGO_SUPPORT
236     sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
237     return 0;
238 #endif  // !MONGO_SUPPORT
239   } // endif Uri
240 
241   if (tdp->Pretty == 2) {
242     tdp->G = g;
243 
244     if (tdp->Zipped) {
245 #if defined(ZIP_SUPPORT)
246       tjsp = new(g) TDBBSON(g, tdp, new(g) UNZFAM(tdp));
247 #else   // !ZIP_SUPPORT
248       sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
249       return 0;
250 #endif  // !ZIP_SUPPORT
251     } else
252       tjsp = new(g) TDBBSON(g, tdp, new(g) MAPFAM(tdp));
253 
254     if (tjsp->MakeDocument(g))
255       return 0;
256 
257     bp = tjsp->Bp;
258 //  bdp = tjsp->GetDoc() ? bp->GetBson(tjsp->GetDoc()) : NULL;
259     bdp = tjsp->GetDoc();
260     jsp = bdp ? bp->GetArrayValue(bdp, 0) : NULL;
261   } else {
262     if (!((tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))) {
263       if (!mgo) {
264         sprintf(g->Message, "LRECL must be specified for pretty=%d", tdp->Pretty);
265         return 0;
266       } else
267         tdp->Lrecl = 8192;       // Should be enough
268 
269     } // endif Lrecl
270 
271     // Allocate the parse work memory
272     tdp->G = PlugInit(NULL, (size_t)tdp->Lrecl * (tdp->Pretty >= 0 ? 4 : 2));
273     tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF);
274 
275     if (tdp->Zipped) {
276 #if defined(ZIP_SUPPORT)
277       tjnp = new(g)TDBBSN(g, tdp, new(g) UNZFAM(tdp));
278 #else   // !ZIP_SUPPORT
279       sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
280       return NULL;
281 #endif  // !ZIP_SUPPORT
282     } else if (tdp->Uri) {
283       if (tdp->Driver && toupper(*tdp->Driver) == 'C') {
284 #if defined(CMGO_SUPPORT)
285         tjnp = new(g) TDBBSN(g, tdp, new(g) CMGFAM(tdp));
286 #else
287         sprintf(g->Message, "Mongo %s Driver not available", "C");
288         return 0;
289 #endif
290       } else if (tdp->Driver && toupper(*tdp->Driver) == 'J') {
291 #if defined(JAVA_SUPPORT)
292         tjnp = new(g) TDBBSN(g, tdp, new(g) JMGFAM(tdp));
293 #else
294         sprintf(g->Message, "Mongo %s Driver not available", "Java");
295         return 0;
296 #endif
297       } else {             // Driver not specified
298 #if defined(CMGO_SUPPORT)
299         tjnp = new(g) TDBBSN(g, tdp, new(g) CMGFAM(tdp));
300 #elif defined(JAVA_SUPPORT)
301         tjnp = new(g) TDBBSN(g, tdp, new(g) JMGFAM(tdp));
302 #else
303         sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
304         return 0;
305 #endif
306       } // endif Driver
307 
308     } else if (tdp->Pretty >= 0)
309       tjnp = new(g) TDBBSN(g, tdp, new(g) DOSFAM(tdp));
310     else
311       tjnp = new(g) TDBBSN(g, tdp, new(g) BINFAM(tdp));
312 
313     tjnp->SetMode(MODE_READ);
314     bp = tjnp->Bp;
315 
316     if (tjnp->OpenDB(g))
317       return 0;
318 
319     switch (tjnp->ReadDB(g)) {
320     case RC_EF:
321       strcpy(g->Message, "Void json table");
322     case RC_FX:
323       goto err;
324     default:
325       jsp = tjnp->Row;
326     } // endswitch ReadDB
327 
328   } // endif pretty
329 
330   if (!(row = (jsp) ? bp->GetObject(jsp) : NULL)) {
331     strcpy(g->Message, "Can only retrieve columns from object rows");
332     goto err;
333   } // endif row
334 
335   all = GetBooleanTableOption(g, topt, "Fullarray", false);
336   jcol.Name = jcol.Fmt = NULL;
337   jcol.Next = NULL;
338   jcol.Found = true;
339   colname[0] = 0;
340 
341   if (!tdp->Uri) {
342     fmt[0] = '$';
343     fmt[1] = '.';
344     bf = 2;
345   } // endif Uri
346 
347   /*********************************************************************/
348   /*  Analyse the JSON tree and define columns.                        */
349   /*********************************************************************/
350   for (i = 1; ; i++) {
351     for (jpp = row; jpp; jpp = bp->GetNext(jpp)) {
352       strncpy(colname, bp->GetKey(jpp), 64);
353       fmt[bf] = 0;
354 
355       if (Find(g, bp->GetVlp(jpp), colname, MY_MIN(lvl, 0)))
356         goto err;
357 
358     } // endfor jpp
359 
360     // Missing column can be null
361     for (jcp = fjcp; jcp; jcp = jcp->Next) {
362       jcp->Cbn |= !jcp->Found;
363       jcp->Found = false;
364     } // endfor jcp
365 
366     if (tdp->Pretty != 2) {
367       // Read next record
368       switch (tjnp->ReadDB(g)) {
369       case RC_EF:
370         jsp = NULL;
371         break;
372       case RC_FX:
373         goto err;
374       default:
375         jsp = tjnp->Row;
376       } // endswitch ReadDB
377 
378     } else
379       jsp = bp->GetNext(jsp);
380 
381     if (!(row = (jsp) ? bp->GetObject(jsp) : NULL))
382       break;
383 
384   } // endfor i
385 
386   if (tdp->Pretty != 2)
387     tjnp->CloseDB(g);
388 
389   return n;
390 
391 err:
392   if (tdp->Pretty != 2)
393     tjnp->CloseDB(g);
394 
395   return 0;
396 } // end of GetColumns
397 
398 bool BSONDISC::Find(PGLOBAL g, PBVAL jvp, PCSZ key, int j)
399 {
400   char  *p, *pc = colname + strlen(colname), buf[32];
401   int    ars;
402   size_t n;
403   PBVAL  job;
404   PBVAL  jar;
405 
406   if (jvp && !bp->IsJson(jvp)) {
407     if (JsonAllPath() && !fmt[bf])
408       strcat(fmt, colname);
409 
410     jcol.Type = (JTYP)jvp->Type;
411 
412     switch (jvp->Type) {
413       case TYPE_STRG:
414       case TYPE_DTM:
415         jcol.Len = (int)strlen(bp->GetString(jvp));
416         break;
417       case TYPE_INTG:
418       case TYPE_BINT:
419         jcol.Len = (int)strlen(bp->GetString(jvp, buf));
420         break;
421       case TYPE_DBL:
422       case TYPE_FLOAT:
423         jcol.Len = (int)strlen(bp->GetString(jvp, buf));
424         jcol.Scale = jvp->Nd;
425         break;
426       case TYPE_BOOL:
427         jcol.Len = 1;
428         break;
429       default:
430         jcol.Len = 0;
431         break;
432     } // endswitch Type
433 
434     jcol.Scale = jvp->Nd;
435     jcol.Cbn = jvp->Type == TYPE_NULL;
436   } else if (!jvp || bp->IsValueNull(jvp)) {
437     jcol.Type = TYPE_UNKNOWN;
438     jcol.Len = jcol.Scale = 0;
439     jcol.Cbn = true;
440   } else  if (j < lvl && !Stringified(strfy, colname)) {
441     if (!fmt[bf])
442       strcat(fmt, colname);
443 
444     p = fmt + strlen(fmt);
445     jsp = jvp;
446 
447     switch (jsp->Type) {
448     case TYPE_JOB:
449       job = jsp;
450 
451       for (PBPR jrp = bp->GetObject(job); jrp; jrp = bp->GetNext(jrp)) {
452         PCSZ k = bp->GetKey(jrp);
453 
454         if (*k != '$') {
455           n = sizeof(fmt) - strlen(fmt) - 1;
456           strncat(strncat(fmt, sep, n), k, n - strlen(sep));
457           n = sizeof(colname) - strlen(colname) - 1;
458           strncat(strncat(colname, "_", n), k, n - 1);
459         } // endif Key
460 
461         if (Find(g, bp->GetVlp(jrp), k, j + 1))
462           return true;
463 
464         *p = *pc = 0;
465       } // endfor jrp
466 
467       return false;
468     case TYPE_JAR:
469       jar = jsp;
470 
471       if (all || (tdp->Xcol && !stricmp(tdp->Xcol, key)))
472         ars = MY_MIN(bp->GetArraySize(jar), limit);
473       else
474         ars = MY_MIN(bp->GetArraySize(jar), 1);
475 
476       for (int k = 0; k < ars; k++) {
477         n = sizeof(fmt) - (strlen(fmt) + 1);
478 
479         if (!tdp->Xcol || stricmp(tdp->Xcol, key)) {
480           sprintf(buf, "%d", k);
481 
482           if (tdp->Uri) {
483             strncat(strncat(fmt, sep, n), buf, n - strlen(sep));
484           } else {
485             strncat(strncat(fmt, "[", n), buf, n - 1);
486             strncat(fmt, "]", n - (strlen(buf) + 1));
487           } // endif uri
488 
489           if (all) {
490             n = sizeof(colname) - (strlen(colname) + 1);
491             strncat(strncat(colname, "_", n), buf, n - 1);
492           } // endif all
493 
494         } else {
495           strncat(fmt, (tdp->Uri ? sep : "[*]"), n);
496         }
497 
498         if (Find(g, bp->GetArrayValue(jar, k), "", j))
499           return true;
500 
501         *p = *pc = 0;
502       } // endfor k
503 
504       return false;
505     default:
506       sprintf(g->Message, "Logical error after %s", fmt);
507       return true;
508     } // endswitch Type
509 
510   } else if (lvl >= 0) {
511     if (Stringified(strfy, colname)) {
512       if (!fmt[bf])
513         strcat(fmt, colname);
514 
515       strcat(fmt, ".*");
516     } else if (JsonAllPath() && !fmt[bf])
517       strcat(fmt, colname);
518 
519     jcol.Type = TYPE_STRG;
520     jcol.Len = sz;
521     jcol.Scale = 0;
522     jcol.Cbn = true;
523   } else
524     return false;
525 
526   AddColumn(g);
527   return false;
528 } // end of Find
529 
530 void BSONDISC::AddColumn(PGLOBAL g)
531 {
532   bool b = fmt[bf] != 0;     // True if formatted
533 
534   // Check whether this column was already found
535   for (jcp = fjcp; jcp; jcp = jcp->Next)
536     if (!strcmp(colname, jcp->Name))
537       break;
538 
539   if (jcp) {
540     if (jcp->Type != jcol.Type) {
541       if (jcp->Type == TYPE_UNKNOWN || jcp->Type == TYPE_NULL)
542         jcp->Type = jcol.Type;
543       //    else if (jcol.Type != TYPE_UNKNOWN && jcol.Type != TYPE_VOID)
544       //      jcp->Type = TYPE_STRING;
545       else if (jcp->Type != TYPE_STRG)
546         switch (jcol.Type) {
547         case TYPE_STRG:
548         case TYPE_DBL:
549           jcp->Type = jcol.Type;
550           break;
551         case TYPE_BINT:
552           if (jcp->Type == TYPE_INTG || jcp->Type == TYPE_BOOL)
553             jcp->Type = jcol.Type;
554 
555           break;
556         case TYPE_INTG:
557           if (jcp->Type == TYPE_BOOL)
558             jcp->Type = jcol.Type;
559 
560           break;
561         default:
562           break;
563         } // endswith Type
564 
565     } // endif Type
566 
567     if (b && (!jcp->Fmt || strlen(jcp->Fmt) < strlen(fmt))) {
568       jcp->Fmt = PlugDup(g, fmt);
569       length[7] = MY_MAX(length[7], strlen(fmt));
570     } // endif fmt
571 
572     jcp->Len = MY_MAX(jcp->Len, jcol.Len);
573     jcp->Scale = MY_MAX(jcp->Scale, jcol.Scale);
574     jcp->Cbn |= jcol.Cbn;
575     jcp->Found = true;
576   } else if (jcol.Type != TYPE_UNKNOWN || tdp->Accept) {
577     // New column
578     jcp = (PJCL)PlugSubAlloc(g, NULL, sizeof(JCOL));
579     *jcp = jcol;
580     jcp->Cbn |= (i > 1);
581     jcp->Name = PlugDup(g, colname);
582     length[0] = MY_MAX(length[0], strlen(colname));
583 
584     if (b) {
585       jcp->Fmt = PlugDup(g, fmt);
586       length[7] = MY_MAX(length[7], strlen(fmt));
587     } else
588       jcp->Fmt = NULL;
589 
590     if (pjcp) {
591       jcp->Next = pjcp->Next;
592       pjcp->Next = jcp;
593     } else
594       fjcp = jcp;
595 
596     n++;
597   } // endif jcp
598 
599   if (jcp)
600     pjcp = jcp;
601 
602 } // end of AddColumn
603 
604 /* -------------------------- Class BTUTIL --------------------------- */
605 
606 /***********************************************************************/
607 /*  Find the row in the tree structure.                                */
608 /***********************************************************************/
609 PBVAL BTUTIL::FindRow(PGLOBAL g)
610 {
611   char *p, *objpath = PlugDup(g, Tp->Objname);
612   char *sep = (char*)(Tp->Sep == ':' ? ":[" : ".[");
613   bool  bp = false, b = false;
614   PBVAL jsp = Tp->Row;
615   PBVAL val = NULL;
616 
617   for (; jsp && objpath; objpath = p, bp = b) {
618     if ((p = strpbrk(objpath + 1, sep))) {
619       b = (*p == '[');
620       *p++ = 0;
621     } // endif p
622 
623     if (!bp && *objpath != '[' && !IsNum(objpath)) { // objpass is a key
624       val = (jsp->Type == TYPE_JOB) ?
625         GetKeyValue(jsp, objpath) : NULL;
626     } else {
627       if (bp || *objpath == '[') {                   // Old style
628         if (objpath[strlen(objpath) - 1] != ']') {
629           sprintf(g->Message, "Invalid Table path %s", Tp->Objname);
630           return NULL;
631         } else if (!bp)
632           objpath++;
633 
634       } // endif bp
635 
636       val = (jsp->Type == TYPE_JAR) ?
637         GetArrayValue(jsp, atoi(objpath) - Tp->B) : NULL;
638     } // endif objpath
639 
640       //  jsp = (val) ? val->GetJson() : NULL;
641     jsp = val;
642   } // endfor objpath
643 
644   if (jsp && jsp->Type != TYPE_JOB) {
645     if (jsp->Type == TYPE_JAR) {
646       jsp = GetArrayValue(jsp, Tp->B);
647 
648       if (jsp->Type != TYPE_JOB)
649         jsp = NULL;
650 
651     } else
652       jsp = NULL;
653 
654   } // endif Type
655 
656   return jsp;
657 } // end of FindRow
658 
659 /***********************************************************************/
660 /*  Parse the read line.                                               */
661 /***********************************************************************/
662 PBVAL BTUTIL::ParseLine(PGLOBAL g, int prty, bool cma)
663 {
664   pretty = prty;
665   comma = cma;
666   return ParseJson(g, Tp->To_Line, strlen(Tp->To_Line));
667 } // end of ParseLine
668 
669 /***********************************************************************/
670 /*  Make the top tree from the object path.                            */
671 /***********************************************************************/
672 PBVAL BTUTIL::MakeTopTree(PGLOBAL g, int type)
673 {
674   PBVAL top = NULL, val = NULL;
675 
676   if (Tp->Objname) {
677     if (!Tp->Row) {
678       // Parse and allocate Objpath item(s)
679       char *p, *objpath = PlugDup(g, Tp->Objname);
680       char *sep = (char*)(Tp->Sep == ':' ? ":[" : ".[");
681       int   i;
682       bool  bp = false, b = false;
683       PBVAL objp = NULL;
684       PBVAL arp = NULL;
685 
686       for (; objpath; objpath = p, bp = b) {
687         if ((p = strpbrk(objpath + 1, sep))) {
688           b = (*p == '[');
689           *p++ = 0;
690         } // endif p
691 
692 
693         if (!bp && *objpath != '[' && !IsNum(objpath)) {
694           // objpass is a key
695           objp = NewVal(TYPE_JOB);
696 
697           if (!top)
698             top = objp;
699 
700           if (val)
701             SetValueObj(val, objp);
702 
703           val = NewVal();
704           SetKeyValue(objp, MOF(val), objpath);
705         } else {
706           if (bp || *objpath == '[') {
707             // Old style
708             if (objpath[strlen(objpath) - 1] != ']') {
709               sprintf(g->Message, "Invalid Table path %s", Tp->Objname);
710               return NULL;
711             } else if (!bp)
712               objpath++;
713 
714           } // endif bp
715 
716           if (!top)
717             top = NewVal(TYPE_JAR);
718 
719           if (val)
720             SetValueArr(val, arp);
721 
722           val = NewVal();
723           i = atoi(objpath) - Tp->B;
724           SetArrayValue(arp, val, i);
725         } // endif objpath
726 
727       } // endfor p
728 
729     } // endif Val
730 
731     Tp->Row = val;
732     if (Tp->Row) Tp->Row->Type = type;
733   } else
734     top = Tp->Row = NewVal(type);
735 
736   return top;
737 } // end of MakeTopTree
738 
739 PSZ BTUTIL::SerialVal(PGLOBAL g, PBVAL vlp, int pretty)
740 {
741   return Serialize(g, vlp, NULL, pretty);
742 } // en of SerialTop
743 
744 /* -------------------------- Class BCUTIL --------------------------- */
745 
746 /***********************************************************************/
747 /*  SetValue: Set a value from a BVALUE contains.                      */
748 /***********************************************************************/
749 void BCUTIL::SetJsonValue(PGLOBAL g, PVAL vp, PBVAL jvp)
750 {
751   if (jvp) {
752     vp->SetNull(false);
753 
754     if (Jb) {
755       vp->SetValue_psz(Serialize(g, jvp, NULL, 0));
756       Jb = false;
757     } else switch (jvp->Type) {
758     case TYPE_STRG:
759     case TYPE_INTG:
760     case TYPE_BINT:
761     case TYPE_DBL:
762     case TYPE_DTM:
763     case TYPE_FLOAT:
764       switch (vp->GetType()) {
765         case TYPE_STRING:
766         case TYPE_DECIM:
767           vp->SetValue_psz(GetString(jvp));
768           break;
769         case TYPE_INT:
770         case TYPE_SHORT:
771         case TYPE_TINY:
772           vp->SetValue(GetInteger(jvp));
773           break;
774         case TYPE_BIGINT:
775           vp->SetValue(GetBigint(jvp));
776           break;
777         case TYPE_DOUBLE:
778           vp->SetValue(GetDouble(jvp));
779 
780           if (jvp->Type == TYPE_DBL || jvp->Type == TYPE_FLOAT)
781             vp->SetPrec(jvp->Nd);
782 
783           break;
784         case TYPE_DATE:
785           if (jvp->Type == TYPE_STRG) {
786             PSZ dat = GetString(jvp);
787 
788             if (!IsNum(dat)) {
789               if (!((DTVAL*)vp)->IsFormatted())
790                 ((DTVAL*)vp)->SetFormat(g, "YYYY-MM-DDThh:mm:ssZ", 20, 0);
791 
792               vp->SetValue_psz(dat);
793             } else
794               vp->SetValue(atoi(dat));
795 
796           } else
797             vp->SetValue(GetInteger(jvp));
798 
799           break;
800         default:
801           sprintf(G->Message, "Unsupported column type %d", vp->GetType());
802           throw 888;
803       } // endswitch Type
804 
805       break;
806     case TYPE_BOOL:
807       if (vp->IsTypeNum())
808         vp->SetValue(GetInteger(jvp) ? 1 : 0);
809       else
810         vp->SetValue_psz((PSZ)(GetInteger(jvp) ? "true" : "false"));
811 
812       break;
813     case TYPE_JAR:
814     case TYPE_JOB:
815       //      SetJsonValue(g, vp, val->GetArray()->GetValue(0));
816       vp->SetValue_psz(GetValueText(g, jvp, NULL));
817       break;
818     default:
819       vp->Reset();
820       vp->SetNull(true);
821     } // endswitch Type
822 
823   } else {
824     vp->Reset();
825     vp->SetNull(true);
826   } // endif val
827 
828 } // end of SetJsonValue
829 
830 /***********************************************************************/
831 /*  MakeJson: Serialize the json item and set value to it.             */
832 /***********************************************************************/
833 PBVAL BCUTIL::MakeBson(PGLOBAL g, PBVAL jsp, int n)
834 {
835   PBVAL vlp, jvp = jsp;
836 
837   if (n < Cp->Nod - 1) {
838     if (jsp->Type == TYPE_JAR) {
839       int    ars = GetArraySize(jsp);
840       PJNODE jnp = &Cp->Nodes[n];
841 
842       jvp = NewVal(TYPE_JAR);
843       jnp->Op = OP_EQ;
844 
845       for (int i = 0; i < ars; i++) {
846         jnp->Rank = i;
847         vlp = GetRowValue(g, jsp, n);
848         AddArrayValue(jvp,DupVal(vlp));
849       } // endfor i
850 
851       jnp->Op = OP_XX;
852       jnp->Rank = 0;
853     } else if (jsp->Type == TYPE_JOB) {
854       jvp = NewVal(TYPE_JOB);
855 
856       for (PBPR prp = GetObject(jsp); prp; prp = GetNext(prp)) {
857         vlp = GetRowValue(g, GetVlp(prp), n + 1);
858         SetKeyValue(jvp, vlp, MZP(prp->Key));
859       }	// endfor prp
860 
861     } // endif Type
862 
863   } // endif's
864 
865   Jb = true;
866   return jvp;
867 } // end of MakeBson
868 
869 /***********************************************************************/
870 /*  GetRowValue:                                                       */
871 /***********************************************************************/
872 PBVAL BCUTIL::GetRowValue(PGLOBAL g, PBVAL row, int i)
873 {
874   int    nod = Cp->Nod;
875   JNODE *nodes = Cp->Nodes;
876   PBVAL  arp;
877   PBVAL  bvp = NULL;
878 
879   for (; i < nod && row; i++) {
880     if (nodes[i].Op == OP_NUM) {
881       bvp = NewVal(TYPE_INT);
882       bvp->N = (row->Type == TYPE_JAR) ? GetSize(row) : 1;
883       return(bvp);
884     } else if (nodes[i].Op == OP_XX) {
885       return MakeBson(g, row, i);
886     } else switch (row->Type) {
887     case TYPE_JOB:
888       if (!nodes[i].Key) {
889         // Expected Array was not there, wrap the value
890         if (i < nod - 1)
891           continue;
892         else
893           bvp = row;
894 
895       } else
896         bvp = GetKeyValue(row, nodes[i].Key);
897 
898       break;
899     case TYPE_JAR:
900       arp = row;
901 
902       if (!nodes[i].Key) {
903         if (nodes[i].Op == OP_EQ)
904           bvp = GetArrayValue(arp, nodes[i].Rank);
905         else if (nodes[i].Op == OP_EXP)
906           return NewVal(ExpandArray(g, arp, i));
907         else
908           return NewVal(CalculateArray(g, arp, i));
909 
910       } else {
911         // Unexpected array, unwrap it as [0]
912         bvp = GetArrayValue(arp, 0);
913         i--;
914       } // endif's
915 
916       break;
917     case TYPE_JVAL:
918       bvp = row;
919       break;
920     default:
921       sprintf(g->Message, "Invalid row JSON type %d", row->Type);
922       bvp = NULL;
923     } // endswitch Type
924 
925     if (i < nod - 1)
926       row = bvp;
927 
928   } // endfor i
929 
930   return bvp;
931 } // end of GetRowValue
932 
933 /***********************************************************************/
934 /*  GetColumnValue:                                                    */
935 /***********************************************************************/
936 PVAL BCUTIL::GetColumnValue(PGLOBAL g, PBVAL row, int i)
937 {
938   PVAL  value = Cp->Value;
939   PBVAL bvp = GetRowValue(g, row, i);
940 
941   SetJsonValue(g, value, bvp);
942   return value;
943 } // end of GetColumnValue
944 
945 /***********************************************************************/
946 /*  ExpandArray:                                                       */
947 /***********************************************************************/
948 PVAL BCUTIL::ExpandArray(PGLOBAL g, PBVAL arp, int n)
949 {
950   int    nod = Cp->Nod, ars = MY_MIN(Tp->Limit, GetArraySize(arp));
951   JNODE *nodes = Cp->Nodes;
952   PVAL   value = Cp->Value;
953   PBVAL  bvp;
954   BVAL   bval;
955 
956   if (!ars) {
957     value->Reset();
958     value->SetNull(true);
959     Tp->NextSame = 0;
960     return value;
961   } // endif ars
962 
963   if (!(bvp = GetArrayValue(arp, (nodes[n].Rx = nodes[n].Nx)))) {
964     strcpy(g->Message, "Logical error expanding array");
965     throw 666;
966   } // endif jvp
967 
968   if (n < nod - 1 && IsJson(bvp)) {
969     SetValue(&bval, GetColumnValue(g, bvp, n + 1));
970     bvp = &bval;
971   } // endif n
972 
973   if (n >= Tp->NextSame) {
974     if (++nodes[n].Nx == ars) {
975       nodes[n].Nx = 0;
976       Cp->Xnod = 0;
977     } else
978       Cp->Xnod = n;
979 
980     Tp->NextSame = Cp->Xnod;
981   } // endif NextSame
982 
983   SetJsonValue(g, value, bvp);
984   return value;
985 } // end of ExpandArray
986 
987 /***********************************************************************/
988 /*  CalculateArray:                                                    */
989 /***********************************************************************/
990 PVAL BCUTIL::CalculateArray(PGLOBAL g, PBVAL arp, int n)
991 {
992   int    i, ars, nv = 0, nextsame = Tp->NextSame;
993   bool   err;
994   int    nod = Cp->Nod;
995   JNODE *nodes = Cp->Nodes;
996   OPVAL  op = nodes[n].Op;
997   PVAL   val[2], vp = nodes[n].Valp, mulval = Cp->MulVal;
998   PBVAL  jvrp, jvp;
999   BVAL   jval;
1000 
1001   vp->Reset();
1002   ars = MY_MIN(Tp->Limit, GetArraySize(arp));
1003   xtrc(1,"CalculateArray: size=%d op=%d nextsame=%d\n", ars, op, nextsame);
1004 
1005   for (i = 0; i < ars; i++) {
1006     jvrp = GetArrayValue(arp, i);
1007     xtrc(1, "i=%d nv=%d\n", i, nv);
1008 
1009     if (!IsValueNull(jvrp) || (op == OP_CNC && GetJsonNull())) do {
1010       if (IsValueNull(jvrp)) {
1011         SetString(jvrp, PlugDup(G, GetJsonNull()));
1012         jvp = jvrp;
1013       } else if (n < nod - 1 && IsJson(jvrp)) {
1014         Tp->NextSame = nextsame;
1015         SetValue(&jval, GetColumnValue(g, jvrp, n + 1));
1016         jvp = &jval;
1017       } else
1018         jvp = jvrp;
1019 
1020       xtrc(1, "jvp=%s null=%d\n", GetString(jvp), IsValueNull(jvp) ? 1 : 0);
1021 
1022       if (!nv++) {
1023         SetJsonValue(g, vp, jvp);
1024         continue;
1025       } else
1026         SetJsonValue(g, mulval, jvp);
1027 
1028       if (!mulval->IsNull()) {
1029         switch (op) {
1030         case OP_CNC:
1031           if (nodes[n].CncVal) {
1032             val[0] = nodes[n].CncVal;
1033             err = vp->Compute(g, val, 1, op);
1034           } // endif CncVal
1035 
1036           val[0] = mulval;
1037           err = vp->Compute(g, val, 1, op);
1038           break;
1039         // case OP_NUM:
1040         case OP_SEP:
1041           val[0] = nodes[n].Valp;
1042           val[1] = mulval;
1043           err = vp->Compute(g, val, 2, OP_ADD);
1044           break;
1045         default:
1046           val[0] = nodes[n].Valp;
1047           val[1] = mulval;
1048           err = vp->Compute(g, val, 2, op);
1049         } // endswitch Op
1050 
1051         if (err)
1052           vp->Reset();
1053 
1054         if (trace(1)) {
1055           char buf(32);
1056 
1057           htrc("vp='%s' err=%d\n",
1058             vp->GetCharString(&buf), err ? 1 : 0);
1059 
1060         } // endif trace
1061 
1062       } // endif Null
1063 
1064     } while (Tp->NextSame > nextsame);
1065 
1066   } // endfor i
1067 
1068   if (op == OP_SEP) {
1069     // Calculate average
1070     mulval->SetValue(nv);
1071     val[0] = vp;
1072     val[1] = mulval;
1073 
1074     if (vp->Compute(g, val, 2, OP_DIV))
1075       vp->Reset();
1076 
1077   } // endif Op
1078 
1079   Tp->NextSame = nextsame;
1080   return vp;
1081 } // end of CalculateArray
1082 
1083 /***********************************************************************/
1084 /*  GetRow: Get the object containing this column.                     */
1085 /***********************************************************************/
1086 PBVAL BCUTIL::GetRow(PGLOBAL g)
1087 {
1088   int    nod = Cp->Nod;
1089   JNODE *nodes = Cp->Nodes;
1090   PBVAL  val = NULL;
1091   PBVAL  arp;
1092   PBVAL  nwr, row = Tp->Row;
1093 
1094   for (int i = 0; i < nod && row; i++) {
1095     if (i < nod-1 && nodes[i+1].Op == OP_XX)
1096       break;
1097     else switch (row->Type) {
1098     case TYPE_JOB:
1099       if (!nodes[i].Key)
1100         // Expected Array was not there, wrap the value
1101         continue;
1102 
1103       val = GetKeyValue(row, nodes[i].Key);
1104       break;
1105     case TYPE_JAR:
1106       arp = row;
1107 
1108       if (!nodes[i].Key) {
1109         if (nodes[i].Op == OP_EQ)
1110           val = GetArrayValue(arp, nodes[i].Rank);
1111         else
1112           val = GetArrayValue(arp, nodes[i].Rx);
1113 
1114       } else {
1115         // Unexpected array, unwrap it as [0]
1116         val = GetArrayValue(arp, 0);
1117         i--;
1118       } // endif Nodes
1119 
1120       break;
1121     case TYPE_JVAL:
1122       val = row;
1123       break;
1124     default:
1125       sprintf(g->Message, "Invalid row JSON type %d", row->Type);
1126       val = NULL;
1127     } // endswitch Type
1128 
1129     if (val) {
1130       row = val;
1131     } else {
1132       // Construct missing objects
1133       for (i++; row && i < nod; i++) {
1134         int type;
1135 
1136         if (nodes[i].Op == OP_XX)
1137           break;
1138         else if (!nodes[i].Key)
1139           // Construct intermediate array
1140           type = TYPE_JAR;
1141         else
1142           type = TYPE_JOB;
1143 
1144         if (row->Type == TYPE_JOB) {
1145           nwr = AddPair(row, nodes[i - 1].Key, type);
1146         } else if (row->Type == TYPE_JAR) {
1147           AddArrayValue(row, (nwr = NewVal(type)));
1148         } else {
1149           strcpy(g->Message, "Wrong type when writing new row");
1150           nwr = NULL;
1151         } // endif's
1152 
1153         row = nwr;
1154       } // endfor i
1155 
1156       break;
1157     } // endelse
1158 
1159   } // endfor i
1160 
1161   return row;
1162 } // end of GetRow
1163 
1164 
1165 /* -------------------------- Class BSONDEF -------------------------- */
1166 
1167 BSONDEF::BSONDEF(void)
1168 {
1169   Jmode = MODE_OBJECT;
1170   Objname = NULL;
1171   Xcol = NULL;
1172   Pretty = 2;
1173   Limit = 1;
1174   Base = 0;
1175   Strict = false;
1176   Sep = '.';
1177   Uri = NULL;
1178   Collname = Options = Filter = NULL;
1179   Pipe = false;
1180   Driver = NULL;
1181   Version = 0;
1182   Wrapname = NULL;
1183 } // end of BSONDEF constructor
1184 
1185 /***********************************************************************/
1186 /*  DefineAM: define specific AM block values.                         */
1187 /***********************************************************************/
1188 bool BSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
1189 {
1190   G = g;
1191   Schema = GetStringCatInfo(g, "DBname", Schema);
1192   Jmode = (JMODE)GetIntCatInfo("Jmode", MODE_OBJECT);
1193 
1194   if ((Objname = GetStringCatInfo(g, "Object", NULL))) {
1195     if (*Objname == '$') Objname++;
1196     if (*Objname == '.') Objname++;
1197   } // endif Objname
1198 
1199   Xcol = GetStringCatInfo(g, "Expand", NULL);
1200   Pretty = GetIntCatInfo("Pretty", 2);
1201   Limit = GetIntCatInfo("Limit", 50);
1202   Base = GetIntCatInfo("Base", 0) ? 1 : 0;
1203   Sep = *GetStringCatInfo(g, "Separator", ".");
1204   Accept = GetBoolCatInfo("Accept", false);
1205 
1206   // Don't use url as MONGO uri when called from REST
1207   if (stricmp(am, "REST") && (Uri = GetStringCatInfo(g, "Connect", NULL))) {
1208 #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
1209     Collname = GetStringCatInfo(g, "Name",
1210       (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
1211     Collname = GetStringCatInfo(g, "Tabname", Collname);
1212     Options = GetStringCatInfo(g, "Colist", Xcol ? "all" : NULL);
1213     Filter = GetStringCatInfo(g, "Filter", NULL);
1214     Pipe = GetBoolCatInfo("Pipeline", false);
1215     Driver = GetStringCatInfo(g, "Driver", NULL);
1216     Version = GetIntCatInfo("Version", 3);
1217     Pretty = 0;
1218 #if defined(JAVA_SUPPORT)
1219     if (Version == 2)
1220       Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo2Interface");
1221     else
1222       Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo3Interface");
1223 #endif   // JAVA_SUPPORT
1224 #else   // !MONGO_SUPPORT
1225     sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
1226     return true;
1227 #endif  // !MONGO_SUPPORT
1228   } // endif Uri
1229 
1230   return DOSDEF::DefineAM(g, (Uri ? "XMGO" : "DOS"), poff);
1231 } // end of DefineAM
1232 
1233 /***********************************************************************/
1234 /*  GetTable: makes a new Table Description Block.                     */
1235 /***********************************************************************/
1236 PTDB BSONDEF::GetTable(PGLOBAL g, MODE m)
1237 {
1238   if (trace(1))
1239     htrc("BSON GetTable Pretty=%d Uri=%s\n", Pretty, SVP(Uri));
1240 
1241   if (Catfunc == FNC_COL)
1242     return new(g)TDBBCL(this);
1243 
1244   PTDBASE tdbp;
1245   PTXF    txfp = NULL;
1246 
1247   // JSN not used for pretty=1 for insert or delete
1248   if (Pretty <= 0 || (Pretty == 1 && (m == MODE_READ || m == MODE_UPDATE))) {
1249     USETEMP tmp = UseTemp();
1250     bool    map = Mapped && Pretty >= 0 && m != MODE_INSERT &&
1251       !(tmp != TMP_NO && m == MODE_UPDATE) &&
1252       !(tmp == TMP_FORCE && (m == MODE_UPDATE || m == MODE_DELETE));
1253 
1254     if (Lrecl) {
1255       // Allocate the parse work memory
1256       G = PlugInit(NULL, (size_t)Lrecl * (Pretty < 0 ? 3 : 5));
1257     } else {
1258       strcpy(g->Message, "LRECL is not defined");
1259       return NULL;
1260     } // endif Lrecl
1261 
1262     if (Pretty < 0) {	 // BJsonfile
1263       txfp = new(g) BINFAM(this);
1264     } else if (Uri) {
1265       if (Driver && toupper(*Driver) == 'C') {
1266 #if defined(CMGO_SUPPORT)
1267         txfp = new(g) CMGFAM(this);
1268 #else
1269         sprintf(g->Message, "Mongo %s Driver not available", "C");
1270         return NULL;
1271 #endif
1272       } else if (Driver && toupper(*Driver) == 'J') {
1273 #if defined(JAVA_SUPPORT)
1274         txfp = new(g) JMGFAM(this);
1275 #else
1276         sprintf(g->Message, "Mongo %s Driver not available", "Java");
1277         return NULL;
1278 #endif
1279       } else {             // Driver not specified
1280 #if defined(CMGO_SUPPORT)
1281         txfp = new(g) CMGFAM(this);
1282 #elif defined(JAVA_SUPPORT)
1283         txfp = new(g) JMGFAM(this);
1284 #else   // !MONGO_SUPPORT
1285         sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
1286         return NULL;
1287 #endif  // !MONGO_SUPPORT
1288       } // endif Driver
1289 
1290       Pretty = 4;   // Not a file
1291     } else if (Zipped) {
1292 #if defined(ZIP_SUPPORT)
1293       if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
1294         txfp = new(g) UNZFAM(this);
1295       } else if (m == MODE_INSERT) {
1296         txfp = new(g) ZIPFAM(this);
1297       } else {
1298         strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
1299         return NULL;
1300       } // endif's m
1301 #else   // !ZIP_SUPPORT
1302       sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
1303       return NULL;
1304 #endif  // !ZIP_SUPPORT
1305     } else if (Compressed) {
1306 #if defined(GZ_SUPPORT)
1307       if (Compressed == 1)
1308         txfp = new(g) GZFAM(this);
1309       else
1310         txfp = new(g) ZLBFAM(this);
1311 #else   // !GZ_SUPPORT
1312       sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
1313       return NULL;
1314 #endif  // !GZ_SUPPORT
1315     } else if (map) {
1316       txfp = new(g) MAPFAM(this);
1317     } else
1318       txfp = new(g) DOSFAM(this);
1319 
1320     // Txfp must be set for TDBBSN
1321     tdbp = new(g) TDBBSN(g, this, txfp);
1322   } else {
1323     if (Zipped) {
1324 #if defined(ZIP_SUPPORT)
1325       if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
1326         txfp = new(g) UNZFAM(this);
1327       } else if (m == MODE_INSERT) {
1328         strcpy(g->Message, "INSERT supported only for zipped JSON when pretty=0");
1329         return NULL;
1330       } else {
1331         strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
1332         return NULL;
1333       } // endif's m
1334 #else   // !ZIP_SUPPORT
1335       sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
1336       return NULL;
1337 #endif  // !ZIP_SUPPORT
1338     } else
1339       txfp = new(g) MAPFAM(this);
1340 
1341     tdbp = new(g) TDBBSON(g, this, txfp);
1342   } // endif Pretty
1343 
1344   if (Multiple)
1345     tdbp = new(g) TDBMUL(tdbp);
1346 
1347   return tdbp;
1348 } // end of GetTable
1349 
1350 /* --------------------------- Class TDBBSN -------------------------- */
1351 
1352 /***********************************************************************/
1353 /*  Implementation of the TDBBSN class (Pretty < 2)                    */
1354 /***********************************************************************/
1355 TDBBSN::TDBBSN(PGLOBAL g, PBDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
1356 {
1357   Bp = new(g) BTUTIL(tdp->G, this);
1358   Top = NULL;
1359   Row = NULL;
1360   Colp = NULL;
1361 
1362   if (tdp) {
1363     Jmode = tdp->Jmode;
1364     Objname = tdp->Objname;
1365     Xcol = tdp->Xcol;
1366     Limit = tdp->Limit;
1367     Pretty = tdp->Pretty;
1368     B = tdp->Base ? 1 : 0;
1369     Sep = tdp->Sep;
1370     Strict = tdp->Strict;
1371   } else {
1372     Jmode = MODE_OBJECT;
1373     Objname = NULL;
1374     Xcol = NULL;
1375     Limit = 1;
1376     Pretty = 0;
1377     B = 0;
1378     Sep = '.';
1379     Strict = false;
1380   } // endif tdp
1381 
1382   Fpos = -1;
1383   N = M = 0;
1384   NextSame = 0;
1385   SameRow = 0;
1386   Xval = -1;
1387   Comma = false;
1388   Bp->SetPretty(Pretty);
1389 } // end of TDBBSN standard constructor
1390 
1391 TDBBSN::TDBBSN(TDBBSN* tdbp) : TDBDOS(NULL, tdbp)
1392 {
1393   Bp = tdbp->Bp;
1394   Top = tdbp->Top;
1395   Row = tdbp->Row;
1396   Colp = tdbp->Colp;
1397   Jmode = tdbp->Jmode;
1398   Objname = tdbp->Objname;
1399   Xcol = tdbp->Xcol;
1400   Fpos = tdbp->Fpos;
1401   N = tdbp->N;
1402   M = tdbp->M;
1403   Limit = tdbp->Limit;
1404   NextSame = tdbp->NextSame;
1405   SameRow = tdbp->SameRow;
1406   Xval = tdbp->Xval;
1407   B = tdbp->B;
1408   Sep = tdbp->Sep;
1409   Pretty = tdbp->Pretty;
1410   Strict = tdbp->Strict;
1411   Comma = tdbp->Comma;
1412 }  // end of TDBBSN copy constructor
1413 
1414 // Used for update
1415 PTDB TDBBSN::Clone(PTABS t)
1416 {
1417   PTDB    tp;
1418   PBSCOL  cp1, cp2;
1419   PGLOBAL g = t->G;
1420 
1421   tp = new(g) TDBBSN(this);
1422 
1423   for (cp1 = (PBSCOL)Columns; cp1; cp1 = (PBSCOL)cp1->GetNext()) {
1424     cp2 = new(g) BSONCOL(cp1, tp);  // Make a copy
1425     NewPointer(t, cp1, cp2);
1426   } // endfor cp1
1427 
1428   return tp;
1429 } // end of Clone
1430 
1431 /***********************************************************************/
1432 /*  Allocate JSN column description block.                             */
1433 /***********************************************************************/
1434 PCOL TDBBSN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1435 {
1436   PBSCOL colp = new(g) BSONCOL(g, cdp, this, cprec, n);
1437 
1438   return (colp->ParseJpath(g)) ? NULL : colp;
1439 } // end of MakeCol
1440 
1441 /***********************************************************************/
1442 /*  InsertSpecialColumn: Put a special column ahead of the column list.*/
1443 /***********************************************************************/
1444 PCOL TDBBSN::InsertSpecialColumn(PCOL colp)
1445 {
1446   if (!colp->IsSpecial())
1447     return NULL;
1448 
1449   //if (Xcol && ((SPCBLK*)colp)->GetRnm())
1450   //  colp->SetKey(0);               // Rownum is no more a key
1451 
1452   colp->SetNext(Columns);
1453   Columns = colp;
1454   return colp;
1455 } // end of InsertSpecialColumn
1456 
1457 /***********************************************************************/
1458 /*  JSON Cardinality: returns table size in number of rows.            */
1459 /***********************************************************************/
1460 int TDBBSN::Cardinality(PGLOBAL g)
1461 {
1462   if (!g)
1463     return 0;
1464   else if (Cardinal < 0) {
1465     Cardinal = TDBDOS::Cardinality(g);
1466 
1467   }	// endif Cardinal
1468 
1469   return Cardinal;
1470 } // end of Cardinality
1471 
1472 /***********************************************************************/
1473 /*  JSON GetMaxSize: returns file size estimate in number of lines.    */
1474 /***********************************************************************/
1475 int TDBBSN::GetMaxSize(PGLOBAL g)
1476 {
1477   if (MaxSize < 0)
1478     MaxSize = TDBDOS::GetMaxSize(g) * ((Xcol) ? Limit : 1);
1479 
1480   return MaxSize;
1481 } // end of GetMaxSize
1482 
1483 /***********************************************************************/
1484 /*  JSON EstimatedLength. Returns an estimated minimum line length.    */
1485 /***********************************************************************/
1486 int TDBBSN::EstimatedLength(void)
1487 {
1488   if (AvgLen <= 0)
1489     return (Lrecl ? Lrecl : 1024) / 8;		// TODO: make it better
1490   else
1491     return AvgLen;
1492 
1493 } // end of Estimated Length
1494 
1495 /***********************************************************************/
1496 /*  OpenDB: Data Base open routine for BSN access method.              */
1497 /***********************************************************************/
1498 bool TDBBSN::OpenDB(PGLOBAL g)
1499 {
1500   TUSE use = Use;
1501 
1502   if (Use == USE_OPEN) {
1503     /*******************************************************************/
1504     /*  Table already open replace it at its beginning.    ???         */
1505     /*******************************************************************/
1506     Fpos = -1;
1507     NextSame = 0;
1508     SameRow = 0;
1509   } // endif Use
1510 
1511     /*********************************************************************/
1512     /*  Open according to logical input/output mode required.            */
1513     /*********************************************************************/
1514   if (TDBDOS::OpenDB(g))
1515     return true;
1516 
1517   if (use == USE_OPEN)
1518     return false;
1519 
1520   if (Pretty < 0) {
1521     /*********************************************************************/
1522     /*  Binary BJSON table.                                              */
1523     /*********************************************************************/
1524     xtrc(1, "JSN OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
1525       this, Tdb_No, Use, Mode);
1526 
1527     // Lrecl is Ok
1528     size_t linelen = Lrecl;
1529     MODE   mode = Mode;
1530 
1531     // Buffer must be allocated in G->Sarea
1532     Mode = MODE_ANY;
1533     Txfp->AllocateBuffer(Bp->G);
1534     Mode = mode;
1535 
1536     if (Mode == MODE_INSERT)
1537       Bp->SubSet(true);
1538     else
1539       Bp->MemSave();
1540 
1541     To_Line = Txfp->GetBuf();
1542     memset(To_Line, 0, linelen);
1543     xtrc(1, "OpenJSN: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
1544   } // endif Pretty
1545 
1546   /***********************************************************************/
1547   /*  First opening.                                                     */
1548   /***********************************************************************/
1549   if (Mode == MODE_INSERT) {
1550     int type;
1551 
1552     switch (Jmode) {
1553       case MODE_OBJECT: type = TYPE_JOB;  break;
1554       case MODE_ARRAY:  type = TYPE_JAR;  break;
1555       case MODE_VALUE:  type = TYPE_JVAL; break;
1556       default:
1557         sprintf(g->Message, "Invalid Jmode %d", Jmode);
1558         return true;
1559     } // endswitch Jmode
1560 
1561     Top = Bp->MakeTopTree(g, type);
1562     Bp->MemSave();
1563   } // endif Mode
1564 
1565   if (Xcol)
1566     To_Filter = NULL;              // Not compatible
1567 
1568   return false;
1569 } // end of OpenDB
1570 
1571 /***********************************************************************/
1572 /*  SkipHeader: Physically skip first header line if applicable.       */
1573 /*  This is called from TDBDOS::OpenDB and must be executed before     */
1574 /*  Kindex construction if the file is accessed using an index.        */
1575 /***********************************************************************/
1576 bool TDBBSN::SkipHeader(PGLOBAL g)
1577 {
1578   int  len = GetFileLength(g);
1579   bool rc = false;
1580 
1581 #if defined(_DEBUG)
1582   if (len < 0)
1583     return true;
1584 #endif   // _DEBUG
1585 
1586   if (Pretty == 1) {
1587     if (Mode == MODE_INSERT || Mode == MODE_DELETE) {
1588       // Mode Insert and delete are no more handled here
1589       DBUG_ASSERT(false);
1590     } else if (len > 0) // !Insert && !Delete
1591       rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
1592 
1593   } // endif Pretty
1594 
1595   return rc;
1596 } // end of SkipHeader
1597 
1598 /***********************************************************************/
1599 /*  ReadDB: Data Base read routine for JSN access method.              */
1600 /***********************************************************************/
1601 int TDBBSN::ReadDB(PGLOBAL g)
1602 {
1603   int   rc;
1604 
1605   N++;
1606 
1607   if (NextSame) {
1608     SameRow = NextSame;
1609     NextSame = 0;
1610     M++;
1611     return RC_OK;
1612   } else if ((rc = TDBDOS::ReadDB(g)) == RC_OK) {
1613     if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK))
1614       return rc;	// Deferred reading failed
1615 
1616     if (Pretty >= 0) {
1617       // Recover the memory used for parsing
1618       Bp->SubSet();
1619 
1620       if ((Row = Bp->ParseLine(g, Pretty, Comma))) {
1621         Top = Row;
1622         Row = Bp->FindRow(g);
1623         SameRow = 0;
1624         Fpos++;
1625         M = 1;
1626         rc = RC_OK;
1627       } else if (Pretty != 1 || strcmp(To_Line, "]")) {
1628         Bp->GetMsg(g);
1629         rc = RC_FX;
1630       } else
1631         rc = RC_EF;
1632 
1633     } else { // Here we get a movable Json binary tree
1634       Bp->MemSet(((BINFAM*)Txfp)->Recsize);  // Useful when updating
1635       Row = Top = (PBVAL)To_Line;
1636       Row = Bp->FindRow(g);
1637       SameRow = 0;
1638       Fpos++;
1639       M = 1;
1640       rc = RC_OK;
1641     }	// endif Pretty
1642 
1643   } // endif ReadDB
1644 
1645   return rc;
1646 } // end of ReadDB
1647 
1648 /***********************************************************************/
1649 /*  PrepareWriting: Prepare the line for WriteDB.                      */
1650 /***********************************************************************/
1651 bool TDBBSN::PrepareWriting(PGLOBAL g)
1652 {
1653   if (Pretty >= 0) {
1654     PSZ s;
1655 
1656 //  if (!(Top = Bp->MakeTopTree(g, Row->Type)))
1657 //    return true;
1658 
1659     if ((s = Bp->SerialVal(g, Top, Pretty))) {
1660       if (Comma)
1661         strcat(s, ",");
1662 
1663       if ((signed)strlen(s) > Lrecl) {
1664         strncpy(To_Line, s, Lrecl);
1665         sprintf(g->Message, "Line truncated (lrecl=%d)", Lrecl);
1666         return PushWarning(g, this);
1667       } else
1668         strcpy(To_Line, s);
1669 
1670       return false;
1671     } else
1672       return true;
1673   } else
1674     ((BINFAM*)Txfp)->Recsize = ((size_t)PlugSubAlloc(Bp->G, NULL, 0)
1675                               - (size_t)To_Line);
1676   return false;
1677 } // end of PrepareWriting
1678 
1679 /***********************************************************************/
1680 /*  WriteDB: Data Base write routine for JSON access method.           */
1681 /***********************************************************************/
1682 int TDBBSN::WriteDB(PGLOBAL g) {
1683   int rc = TDBDOS::WriteDB(g);
1684 
1685   Bp->SubSet();
1686   Bp->Clear(Row);
1687   return rc;
1688 } // end of WriteDB
1689 
1690 /***********************************************************************/
1691 /*  Data Base close routine for JSON access method.                    */
1692 /***********************************************************************/
1693 void TDBBSN::CloseDB(PGLOBAL g)
1694 {
1695   TDBDOS::CloseDB(g);
1696   Bp->G = PlugExit(Bp->G);
1697 } // end of CloseDB
1698 
1699 /* ---------------------------- BSONCOL ------------------------------ */
1700 
1701 /***********************************************************************/
1702 /*  BSONCOL public constructor.                                        */
1703 /***********************************************************************/
1704 BSONCOL::BSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
1705         : DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
1706 {
1707   Tbp = (TDBBSN*)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
1708   Cp = new(g) BCUTIL(((PBDEF)Tbp->To_Def)->G, this, Tbp);
1709   Jpath = cdp->GetFmt();
1710   MulVal = NULL;
1711   Nodes = NULL;
1712   Nod = 0;
1713   Sep = Tbp->Sep;
1714   Xnod = -1;
1715   Xpd = false;
1716   Parsed = false;
1717   Warned = false;
1718   Sgfy = false;
1719 } // end of BSONCOL constructor
1720 
1721 /***********************************************************************/
1722 /*  BSONCOL constructor used for copying columns.                      */
1723 /*  tdbp is the pointer to the new table descriptor.                   */
1724 /***********************************************************************/
1725 BSONCOL::BSONCOL(BSONCOL* col1, PTDB tdbp) : DOSCOL(col1, tdbp)
1726 {
1727   Tbp = col1->Tbp;
1728   Cp = col1->Cp;
1729   Jpath = col1->Jpath;
1730   MulVal = col1->MulVal;
1731   Nodes = col1->Nodes;
1732   Nod = col1->Nod;
1733   Sep = col1->Sep;
1734   Xnod = col1->Xnod;
1735   Xpd = col1->Xpd;
1736   Parsed = col1->Parsed;
1737   Warned = col1->Warned;
1738   Sgfy = col1->Sgfy;
1739 } // end of BSONCOL copy constructor
1740 
1741 /***********************************************************************/
1742 /*  SetBuffer: prepare a column block for write operation.             */
1743 /***********************************************************************/
1744 bool BSONCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
1745 {
1746   if (DOSCOL::SetBuffer(g, value, ok, check))
1747     return true;
1748 
1749   // Parse the json path
1750   if (ParseJpath(g))
1751     return true;
1752 
1753   Tbp = (TDBBSN*)To_Tdb;
1754   return false;
1755 } // end of SetBuffer
1756 
1757 /***********************************************************************/
1758 /*  Check whether this object is expanded.                             */
1759 /***********************************************************************/
1760 bool BSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
1761 {
1762   if ((Tbp->Xcol && nm && !strcmp(nm, Tbp->Xcol) &&
1763     (Tbp->Xval < 0 || Tbp->Xval == i)) || Xpd) {
1764     Xpd = true;              // Expandable object
1765     Nodes[i].Op = OP_EXP;
1766   } else if (b) {
1767     strcpy(g->Message, "Cannot expand more than one branch");
1768     return true;
1769   } // endif Xcol
1770 
1771   return false;
1772 } // end of CheckExpand
1773 
1774 /***********************************************************************/
1775 /*  Analyse array processing options.                                  */
1776 /***********************************************************************/
1777 bool BSONCOL::SetArrayOptions(PGLOBAL g, char* p, int i, PSZ nm)
1778 {
1779   int    n;
1780   bool   dg = true, b = false;
1781   PJNODE jnp = &Nodes[i];
1782 
1783   //if (*p == '[') p++;    // Old syntax .[ or :[
1784   n = (int)strlen(p);
1785 
1786   if (*p) {
1787     if (p[n - 1] == ']') {
1788       p[--n] = 0;
1789     } else if (!IsNum(p)) {
1790       // Wrong array specification
1791       snprintf(g->Message, sizeof(g->Message), "Invalid array specification %s for %s", p, Name);
1792       return true;
1793     } // endif p
1794 
1795   } else
1796     b = true;
1797 
1798   // To check whether a numeric Rank was specified
1799   dg = IsNum(p);
1800 
1801   if (!n) {
1802     // Default specifications
1803     if (CheckExpand(g, i, nm, false))
1804       return true;
1805     else if (jnp->Op != OP_EXP) {
1806       if (b) {
1807         // Return 1st value (B is the index base)
1808         jnp->Rank = Tbp->B;
1809         jnp->Op = OP_EQ;
1810       } else if (!Value->IsTypeNum()) {
1811         jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING);
1812         jnp->Op = OP_CNC;
1813       } else
1814         jnp->Op = OP_ADD;
1815 
1816     } // endif OP
1817 
1818   } else if (dg) {
1819     // Return nth value
1820     jnp->Rank = atoi(p) - Tbp->B;
1821     jnp->Op = OP_EQ;
1822   } else if (n == 1) {
1823     // Set the Op value;
1824     if (Sep == ':')
1825       switch (*p) {
1826       case '*': *p = 'x'; break;
1827       case 'x':
1828       case 'X': *p = '*'; break; // Expand this array
1829       default: break;
1830       } // endswitch p
1831 
1832     switch (*p) {
1833     case '+': jnp->Op = OP_ADD;  break;
1834     case 'x': jnp->Op = OP_MULT; break;
1835     case '>': jnp->Op = OP_MAX;  break;
1836     case '<': jnp->Op = OP_MIN;  break;
1837     case '!': jnp->Op = OP_SEP;  break; // Average
1838     case '#': jnp->Op = OP_NUM;  break;
1839     case '*': // Expand this array
1840       if (!Tbp->Xcol && nm) {
1841         Xpd = true;
1842         jnp->Op = OP_EXP;
1843         Tbp->Xval = i;
1844         Tbp->Xcol = nm;
1845       } else if (CheckExpand(g, i, nm, true))
1846         return true;
1847 
1848       break;
1849     default:
1850       sprintf(g->Message,
1851         "Invalid function specification %c for %s", *p, Name);
1852       return true;
1853     } // endswitch *p
1854 
1855   } else if (*p == '"' && p[n - 1] == '"') {
1856     // This is a concat specification
1857     jnp->Op = OP_CNC;
1858 
1859     if (n > 2) {
1860       // Set concat intermediate string
1861       p[n - 1] = 0;
1862       jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
1863     } // endif n
1864 
1865   } else {
1866     sprintf(g->Message, "Wrong array specification for %s", Name);
1867     return true;
1868   } // endif's
1869 
1870   // For calculated arrays, a local Value must be used
1871   switch (jnp->Op) {
1872   case OP_NUM:
1873     jnp->Valp = AllocateValue(g, TYPE_INT);
1874     break;
1875   case OP_ADD:
1876   case OP_MULT:
1877   case OP_SEP:
1878     if (!IsTypeChar(Buf_Type))
1879       jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
1880     else
1881       jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);
1882 
1883     break;
1884   case OP_MIN:
1885   case OP_MAX:
1886     jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
1887     break;
1888   case OP_CNC:
1889     if (IsTypeChar(Buf_Type))
1890       jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
1891     else
1892       jnp->Valp = AllocateValue(g, TYPE_STRING, 512);
1893 
1894     break;
1895   default:
1896     break;
1897   } // endswitch Op
1898 
1899   if (jnp->Valp)
1900     MulVal = AllocateValue(g, jnp->Valp);
1901 
1902   return false;
1903 } // end of SetArrayOptions
1904 
1905 /***********************************************************************/
1906 /*  Parse the eventual passed Jpath information.                       */
1907 /*  This information can be specified in the Fieldfmt column option    */
1908 /*  when creating the table. It permits to indicate the position of    */
1909 /*  the node corresponding to that column.                             */
1910 /***********************************************************************/
1911 bool BSONCOL::ParseJpath(PGLOBAL g)
1912 {
1913   char* p, * p1 = NULL, * p2 = NULL, * pbuf = NULL;
1914   int   i;
1915   bool  a;
1916 
1917   if (Parsed)
1918     return false;                       // Already done
1919   else if (InitValue(g))
1920     return true;
1921   else if (!Jpath)
1922     Jpath = Name;
1923 
1924   if (To_Tdb->GetOrig()) {
1925     // This is an updated column, get nodes from origin
1926     for (PBSCOL colp = (PBSCOL)Tbp->GetColumns(); colp;
1927       colp = (PBSCOL)colp->GetNext())
1928       if (!stricmp(Name, colp->GetName())) {
1929         Nod = colp->Nod;
1930         Nodes = colp->Nodes;
1931         Xpd = colp->Xpd;
1932         goto fin;
1933       } // endif Name
1934 
1935     sprintf(g->Message, "Cannot parse updated column %s", Name);
1936     return true;
1937   } // endif To_Orig
1938 
1939   pbuf = PlugDup(g, Jpath);
1940   if (*pbuf == '$') pbuf++;
1941   if (*pbuf == Sep) pbuf++;
1942   if (*pbuf == '[') p1 = pbuf++;
1943 
1944   // Estimate the required number of nodes
1945   for (i = 0, p = pbuf; (p = NextChr(p, Sep)); i++, p++)
1946     Nod++;                         // One path node found
1947 
1948   Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE));
1949   memset(Nodes, 0, (Nod) * sizeof(JNODE));
1950 
1951   // Analyze the Jpath for this column
1952   for (i = 0, p = pbuf; p && i < Nod; i++, p = (p2 ? p2 : NULL)) {
1953     a = (p1 != NULL);
1954     p1 = strchr(p, '[');
1955     p2 = strchr(p, Sep);
1956 
1957     if (!p2)
1958       p2 = p1;
1959     else if (p1) {
1960       if (p1 < p2)
1961         p2 = p1;
1962       else if (p1 == p2 + 1)
1963         *p2++ = 0;     // Old syntax .[ or :[
1964       else
1965         p1 = NULL;
1966 
1967     } // endif p1
1968 
1969     if (p2)
1970       *p2++ = 0;
1971 
1972     // Jpath must be explicit
1973     if (a || *p == 0 || *p == '[' || IsNum(p)) {
1974       // Analyse intermediate array processing
1975       if (SetArrayOptions(g, p, i, Nodes[i - 1].Key))
1976         return true;
1977       else if (Xpd && Tbp->Mode == MODE_DELETE) {
1978         strcpy(g->Message, "Cannot delete expanded columns");
1979         return true;
1980       } // endif Xpd
1981 
1982     } else if (*p == '*') {
1983       // Return JSON
1984       Nodes[i].Op = OP_XX;
1985     } else {
1986       Nodes[i].Key = p;
1987       Nodes[i].Op = OP_EXIST;
1988     } // endif's
1989 
1990   } // endfor i, p
1991 
1992   Nod = i;
1993 
1994 fin:
1995   MulVal = AllocateValue(g, Value);
1996   Parsed = true;
1997   return false;
1998 } // end of ParseJpath
1999 
2000 /***********************************************************************/
2001 /*  Get Jpath converted to Mongo path.                                 */
2002 /***********************************************************************/
2003 PSZ BSONCOL::GetJpath(PGLOBAL g, bool proj)
2004 {
2005   if (Jpath) {
2006     char* p1, * p2, * mgopath;
2007     int   i = 0;
2008 
2009     if (strcmp(Jpath, "*")) {
2010       p1 = Jpath;
2011       if (*p1 == '$') p1++;
2012       if (*p1 == '.') p1++;
2013       mgopath = PlugDup(g, p1);
2014     } else {
2015       Sgfy = true;
2016       return NULL;
2017     } // endif
2018 
2019     for (p1 = p2 = mgopath; *p1; p1++)
2020       if (i) {                 // Inside []
2021         if (isdigit(*p1)) {
2022           if (!proj)
2023             *p2++ = *p1;
2024 
2025         } else if (*p1 == ']' && i == 1) {
2026           if (proj && p1[1] == '.')
2027             p1++;
2028 
2029           i = 0;
2030         } else if (*p1 == '.' && i == 2) {
2031           if (!proj)
2032             *p2++ = '.';
2033 
2034           i = 0;
2035         } else if (!proj)
2036           return NULL;
2037 
2038       } else switch (*p1) {
2039       case ':':
2040       case '.':
2041         if (isdigit(p1[1]))
2042           i = 2;
2043 
2044         *p2++ = '.';
2045         break;
2046       case '[':
2047         if (*(p2 - 1) != '.')
2048           *p2++ = '.';
2049 
2050         i = 1;
2051         break;
2052       case '*':
2053         if (*(p2 - 1) == '.' && !*(p1 + 1)) {
2054           p2--;              // Suppress last :*
2055           Sgfy = true;
2056           break;
2057         } // endif p2
2058 
2059       default:
2060         *p2++ = *p1;
2061         break;
2062       } // endswitch p1;
2063 
2064       if (*(p2 - 1) == '.')
2065         p2--;
2066 
2067       *p2 = 0;
2068       return mgopath;
2069   } else
2070     return NULL;
2071 
2072 } // end of GetJpath
2073 
2074 /***********************************************************************/
2075 /*  ReadColumn:                                                        */
2076 /***********************************************************************/
2077 void BSONCOL::ReadColumn(PGLOBAL g)
2078 {
2079   if (!Tbp->SameRow || Xnod >= Tbp->SameRow)
2080     Value->SetValue_pval(Cp->GetColumnValue(g, Tbp->Row, 0));
2081 
2082 #if defined(DEVELOPMENT)
2083   if (Xpd && Value->IsNull() && !((PBDEF)Tbp->To_Def)->Accept)
2084     htrc("Null expandable JSON value for column %s\n", Name);
2085 #endif   // DEVELOPMENT
2086 
2087   // Set null when applicable
2088   if (!Nullable)
2089     Value->SetNull(false);
2090 
2091 } // end of ReadColumn
2092 
2093 /***********************************************************************/
2094 /*  WriteColumn:                                                       */
2095 /***********************************************************************/
2096 void BSONCOL::WriteColumn(PGLOBAL g)
2097 {
2098   if (Xpd && Tbp->Pretty < 2) {
2099     strcpy(g->Message, "Cannot write expanded column when Pretty is not 2");
2100     throw 666;
2101   } // endif Xpd
2102 
2103   /*********************************************************************/
2104   /*  Check whether this node must be written.                         */
2105   /*********************************************************************/
2106   if (Value != To_Val)
2107     Value->SetValue_pval(To_Val, FALSE);    // Convert the updated value
2108 
2109   /*********************************************************************/
2110   /*  On INSERT Null values are represented by no node.                */
2111   /*********************************************************************/
2112   if (Value->IsNull() && Tbp->Mode == MODE_INSERT)
2113     return;
2114 
2115   PBVAL jsp, row = Cp->GetRow(g);
2116 
2117   if (row) switch (Buf_Type) {
2118   case TYPE_STRING:
2119   case TYPE_DATE:
2120   case TYPE_INT:
2121   case TYPE_TINY:
2122   case TYPE_SHORT:
2123   case TYPE_BIGINT:
2124   case TYPE_DOUBLE:
2125     if (Buf_Type == TYPE_STRING && Nodes[Nod - 1].Op == OP_XX) {
2126       char *s = Value->GetCharValue();
2127 
2128       if (!(jsp = Cp->ParseJson(g, s, strlen(s)))) {
2129         strcpy(g->Message, s);
2130         throw 666;
2131       } // endif jsp
2132 
2133       switch (row->Type) {
2134         case TYPE_JAR:
2135           if (Nod > 1 && Nodes[Nod - 2].Op == OP_EQ)
2136             Cp->SetArrayValue(row, jsp, Nodes[Nod - 2].Rank);
2137           else
2138             Cp->AddArrayValue(row, jsp);
2139 
2140           break;
2141         case TYPE_JOB:
2142           if (Nod > 1 && Nodes[Nod - 2].Key)
2143             Cp->SetKeyValue(row, jsp, Nodes[Nod - 2].Key);
2144 
2145           break;
2146         case TYPE_JVAL:
2147         default:
2148           Cp->SetValueVal(row, jsp);
2149       } // endswitch Type
2150 
2151       break;
2152     } else
2153       jsp = Cp->NewVal(Value);
2154 
2155     switch (row->Type) {
2156       case TYPE_JAR:
2157         if (Nodes[Nod - 1].Op == OP_EQ)
2158           Cp->SetArrayValue(row, jsp, Nodes[Nod - 1].Rank);
2159         else
2160           Cp->AddArrayValue(row, jsp);
2161 
2162         break;
2163       case TYPE_JOB:
2164         if (Nodes[Nod - 1].Key)
2165           Cp->SetKeyValue(row, jsp, Nodes[Nod - 1].Key);
2166 
2167         break;
2168       case TYPE_JVAL:
2169       default:
2170         Cp->SetValueVal(row, jsp);
2171     } // endswitch Type
2172 
2173     break;
2174   default:                  // ??????????
2175     sprintf(g->Message, "Invalid column type %d", Buf_Type);
2176   } // endswitch Type
2177 
2178 } // end of WriteColumn
2179 
2180 /* -------------------------- Class TDBBSON -------------------------- */
2181 
2182 /***********************************************************************/
2183 /*  Implementation of the TDBBSON class.                               */
2184 /***********************************************************************/
2185 TDBBSON::TDBBSON(PGLOBAL g, PBDEF tdp, PTXF txfp) : TDBBSN(g, tdp, txfp)
2186 {
2187   Docp = NULL;
2188   Docrow = NULL;
2189   Multiple = tdp->Multiple;
2190   Docsize = 0;
2191   Done = Changed = false;
2192   Bp->SetPretty(2);
2193 } // end of TDBBSON standard constructor
2194 
2195 TDBBSON::TDBBSON(PBTDB tdbp) : TDBBSN(tdbp)
2196 {
2197   Docp = tdbp->Docp;
2198   Docrow = tdbp->Docrow;
2199   Multiple = tdbp->Multiple;
2200   Docsize = tdbp->Docsize;
2201   Done = tdbp->Done;
2202   Changed = tdbp->Changed;
2203 } // end of TDBBSON copy constructor
2204 
2205 // Used for update
2206 PTDB TDBBSON::Clone(PTABS t)
2207 {
2208   PTDB    tp;
2209   PBSCOL   cp1, cp2;
2210   PGLOBAL g = t->G;
2211 
2212   tp = new(g) TDBBSON(this);
2213 
2214   for (cp1 = (PBSCOL)Columns; cp1; cp1 = (PBSCOL)cp1->GetNext()) {
2215     cp2 = new(g) BSONCOL(cp1, tp);  // Make a copy
2216     NewPointer(t, cp1, cp2);
2217   } // endfor cp1
2218 
2219   return tp;
2220 } // end of Clone
2221 
2222 /***********************************************************************/
2223 /*  Make the document tree from the object path.                       */
2224 /***********************************************************************/
2225 int TDBBSON::MakeNewDoc(PGLOBAL g)
2226 {
2227   // Create a void table that will be populated
2228   Docp = Bp->NewVal(TYPE_JAR);
2229 
2230   if (!(Top = Bp->MakeTopTree(g, TYPE_JAR)))
2231     return RC_FX;
2232 
2233   Docp = Row;
2234   Done = true;
2235   return RC_OK;
2236 } // end of MakeNewDoc
2237 
2238 /***********************************************************************/
2239 /*  Make the document tree from a file.                                */
2240 /***********************************************************************/
2241 int TDBBSON::MakeDocument(PGLOBAL g)
2242 {
2243   char   *p, *p1, *p2, *memory, *objpath, *key = NULL;
2244   int     i = 0;
2245   size_t  len;
2246   my_bool a;
2247   MODE    mode = Mode;
2248   PBVAL   jsp;
2249   PBVAL   objp = NULL;
2250   PBVAL   arp = NULL;
2251   PBVAL   val = NULL;
2252 
2253   if (Done)
2254     return RC_OK;
2255 
2256   /*********************************************************************/
2257   /*  Create the mapping file object in mode read.                     */
2258   /*********************************************************************/
2259   Mode = MODE_READ;
2260 
2261   if (!Txfp->OpenTableFile(g)) {
2262     PFBLOCK fp = Txfp->GetTo_Fb();
2263 
2264     if (fp) {
2265       len = fp->Length;
2266       memory = fp->Memory;
2267     } else {
2268       Mode = mode;         // Restore saved Mode
2269       return MakeNewDoc(g);
2270     } // endif fp
2271 
2272   } else
2273     return RC_FX;
2274 
2275   /*********************************************************************/
2276   /*  Parse the json file and allocate its tree structure.             */
2277   /*********************************************************************/
2278   g->Message[0] = 0;
2279   jsp = Top = Bp->ParseJson(g, memory, len);
2280   Txfp->CloseTableFile(g, false);
2281   Mode = mode;             // Restore saved Mode
2282 
2283   if (!jsp && g->Message[0])
2284     return RC_FX;
2285 
2286   if ((objpath = PlugDup(g, Objname))) {
2287     p1 = (*objpath == '[') ? objpath++ : NULL;
2288 
2289     /*********************************************************************/
2290     /*  Find the table in the tree structure.                            */
2291     /*********************************************************************/
2292     for (p = objpath; jsp && p; p = (p2 ? p2 : NULL)) {
2293       a = (p1 != NULL);
2294       p1 = strchr(p, '[');
2295       p2 = strchr(p, '.');
2296 
2297       if (!p2)
2298         p2 = p1;
2299       else if (p1) {
2300         if (p1 < p2)
2301           p2 = p1;
2302         else if (p1 == p2 + 1)
2303           *p2++ = 0;		 // Old syntax .[
2304         else
2305           p1 = NULL;
2306 
2307       }	// endif p1
2308 
2309       if (p2)
2310         *p2++ = 0;
2311 
2312       if (!a && *p && *p != '[' && !IsNum(p)) {
2313         // obj is a key
2314         if (jsp->Type != TYPE_JOB) {
2315           strcpy(g->Message, "Table path does not match the json file");
2316           return RC_FX;
2317         } // endif Type
2318 
2319         key = p;
2320         objp = jsp;
2321         arp = NULL;
2322         val = Bp->GetKeyValue(objp, key);
2323 
2324         if (!val || !(jsp = Bp->GetBson(val))) {
2325           sprintf(g->Message, "Cannot find object key %s", key);
2326           return RC_FX;
2327         } // endif val
2328 
2329       } else {
2330         if (*p == '[') {
2331           // Old style
2332           if (p[strlen(p) - 1] != ']') {
2333             sprintf(g->Message, "Invalid Table path near %s", p);
2334             return RC_FX;
2335           } else
2336             p++;
2337 
2338         } // endif p
2339 
2340         if (jsp->Type != TYPE_JAR) {
2341           strcpy(g->Message, "Table path does not match the json file");
2342           return RC_FX;
2343         } // endif Type
2344 
2345         arp = jsp;
2346         objp = NULL;
2347         i = atoi(p) - B;
2348         val = Bp->GetArrayValue(arp, i);
2349 
2350         if (!val) {
2351           sprintf(g->Message, "Cannot find array value %d", i);
2352           return RC_FX;
2353         } // endif val
2354 
2355       } // endif
2356 
2357       jsp = val;
2358     } // endfor p
2359 
2360   } // endif objpath
2361 
2362   if (jsp && jsp->Type == TYPE_JAR)
2363     Docp = jsp;
2364   else {
2365     // The table is void or is just one object or one value
2366     if (objp) {
2367       Docp = Bp->GetKeyValue(objp, key);
2368       Docp->To_Val = Bp->MOF(Bp->DupVal(Docp));
2369       Docp->Type = TYPE_JAR;
2370     } else if (arp) {
2371       Docp = Bp->NewVal(TYPE_JAR);
2372       Bp->AddArrayValue(Docp, jsp);
2373       Bp->SetArrayValue(arp, Docp, i);
2374     } else {
2375       Top = Docp = Bp->NewVal(TYPE_JAR);
2376       Bp->AddArrayValue(Docp, jsp);
2377     } // endif's
2378 
2379   } // endif jsp
2380 
2381   Docsize = Bp->GetSize(Docp);
2382   Done = true;
2383   return RC_OK;
2384 } // end of MakeDocument
2385 
2386 /***********************************************************************/
2387 /*  JSON Cardinality: returns table size in number of rows.            */
2388 /***********************************************************************/
2389 int TDBBSON::Cardinality(PGLOBAL g)
2390 {
2391   if (!g)
2392     return (Xcol || Multiple) ? 0 : 1;
2393   else if (Cardinal < 0) {
2394     if (!Multiple) {
2395       if (MakeDocument(g) == RC_OK)
2396         Cardinal = Docsize;
2397 
2398     } else
2399       return 10;
2400 
2401   } // endif Cardinal
2402 
2403   return Cardinal;
2404 } // end of Cardinality
2405 
2406 /***********************************************************************/
2407 /*  JSON GetMaxSize: returns table size estimate in number of rows.    */
2408 /***********************************************************************/
2409 int TDBBSON::GetMaxSize(PGLOBAL g)
2410 {
2411   if (MaxSize < 0)
2412     MaxSize = Cardinality(g) * ((Xcol) ? Limit : 1);
2413 
2414   return MaxSize;
2415 } // end of GetMaxSize
2416 
2417 /***********************************************************************/
2418 /*  ResetSize: call by TDBMUL when calculating size estimate.          */
2419 /***********************************************************************/
2420 void TDBBSON::ResetSize(void)
2421 {
2422   MaxSize = Cardinal = -1;
2423   Fpos = -1;
2424   N = 0;
2425   Docrow = NULL;
2426   Done = false;
2427 } // end of ResetSize
2428 
2429 /***********************************************************************/
2430 /*  TDBBSON is not indexable.                                          */
2431 /***********************************************************************/
2432 int TDBBSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool)
2433 {
2434   if (pxdf) {
2435     strcpy(g->Message, "JSON not indexable when pretty = 2");
2436     return RC_FX;
2437   } else
2438     return RC_OK;
2439 
2440 } // end of MakeIndex
2441 
2442 /***********************************************************************/
2443 /*  Return the position in the table.                                  */
2444 /***********************************************************************/
2445 int TDBBSON::GetRecpos(void)
2446 {
2447 #if 0
2448   union {
2449     uint Rpos;
2450     BYTE Spos[4];
2451   };
2452 
2453   Rpos = htonl(Fpos);
2454   Spos[0] = (BYTE)NextSame;
2455   return Rpos;
2456 #endif // 0
2457   return Fpos;
2458 } // end of GetRecpos
2459 
2460 /***********************************************************************/
2461 /*  Set the position in the table.                                  */
2462 /***********************************************************************/
2463 bool TDBBSON::SetRecpos(PGLOBAL, int recpos)
2464 {
2465 #if 0
2466   union {
2467     uint Rpos;
2468     BYTE Spos[4];
2469   };
2470 
2471   Rpos = recpos;
2472   NextSame = Spos[0];
2473   Spos[0] = 0;
2474   Fpos = (signed)ntohl(Rpos);
2475 
2476   //if (Fpos != (signed)ntohl(Rpos)) {
2477   //  Fpos = ntohl(Rpos);
2478   //  same = false;
2479   //} else
2480   //  same = true;
2481 #endif // 0
2482 
2483   Fpos = recpos - 1;
2484   Docrow = NULL;
2485   return false;
2486 } // end of SetRecpos
2487 
2488 /***********************************************************************/
2489 /*  JSON Access Method opening routine.                                */
2490 /***********************************************************************/
2491 bool TDBBSON::OpenDB(PGLOBAL g)
2492 {
2493   if (Use == USE_OPEN) {
2494     /*******************************************************************/
2495     /*  Table already open replace it at its beginning.                */
2496     /*******************************************************************/
2497     Fpos = -1;
2498     NextSame = false;
2499     SameRow = 0;
2500     Docrow = NULL;
2501     return false;
2502   } // endif use
2503 
2504 /*********************************************************************/
2505 /*  OpenDB: initialize the JSON file processing.                     */
2506 /*********************************************************************/
2507   if (MakeDocument(g) != RC_OK)
2508     return true;
2509 
2510   if (Mode == MODE_INSERT)
2511     switch (Jmode) {
2512     case MODE_OBJECT: Row = Bp->NewVal(TYPE_JOB);  break;
2513     case MODE_ARRAY:  Row = Bp->NewVal(TYPE_JAR);  break;
2514     case MODE_VALUE:  Row = Bp->NewVal(TYPE_JVAL); break;
2515     default:
2516       sprintf(g->Message, "Invalid Jmode %d", Jmode);
2517       return true;
2518     } // endswitch Jmode
2519 
2520   if (Xcol)
2521     To_Filter = NULL;              // Imcompatible
2522 
2523   Use = USE_OPEN;
2524   return false;
2525 } // end of OpenDB
2526 
2527 /***********************************************************************/
2528 /*  ReadDB: Data Base read routine for JSON access method.             */
2529 /***********************************************************************/
2530 int TDBBSON::ReadDB(PGLOBAL)
2531 {
2532   int rc;
2533 
2534   N++;
2535 
2536   if (NextSame) {
2537     SameRow = NextSame;
2538     NextSame = false;
2539     M++;
2540     rc = RC_OK;
2541   } else if (++Fpos < Docsize) {
2542     Docrow = (Docrow) ? Bp->GetNext(Docrow) : Bp->GetArrayValue(Docp, Fpos);
2543     Row = (Docrow->Type == TYPE_JVAL) ? Bp->GetBson(Docrow) : Docrow;
2544     SameRow = 0;
2545     M = 1;
2546     rc = RC_OK;
2547   } else
2548     rc = RC_EF;
2549 
2550   return rc;
2551 } // end of ReadDB
2552 
2553 /***********************************************************************/
2554 /*  WriteDB: Data Base write routine for JSON access method.           */
2555 /***********************************************************************/
2556 int TDBBSON::WriteDB(PGLOBAL g)
2557 {
2558   if (Mode == MODE_INSERT) {
2559     Bp->AddArrayValue(Docp, Row);
2560 
2561     switch(Jmode) {
2562     case MODE_OBJECT: Row = Bp->NewVal(TYPE_JOB); break;
2563     case MODE_ARRAY:  Row = Bp->NewVal(TYPE_JAR); break;
2564     default:          Row = Bp->NewVal();         break;
2565     } // endswitch Jmode
2566 
2567   } else
2568     Bp->SetArrayValue(Docp, Row, Fpos);
2569 
2570   Changed = true;
2571   return RC_OK;
2572 } // end of WriteDB
2573 
2574 /***********************************************************************/
2575 /*  Data Base delete line routine for JSON access method.              */
2576 /***********************************************************************/
2577 int TDBBSON::DeleteDB(PGLOBAL g, int irc)
2578 {
2579   if (irc == RC_OK)
2580     // Deleted current row
2581     Bp->DeleteValue(Docp, Fpos);
2582   else if (irc == RC_FX)
2583     // Delete all
2584     Docp->To_Val = 0;
2585 
2586   Changed = true;
2587   return RC_OK;
2588 } // end of DeleteDB
2589 
2590 /***********************************************************************/
2591 /*  Data Base close routine for JSON access methods.                   */
2592 /***********************************************************************/
2593 void TDBBSON::CloseDB(PGLOBAL g)
2594 {
2595   if (!Changed)
2596     return;
2597 
2598   // Save the modified document
2599   char filename[_MAX_PATH];
2600 
2601 //Docp->InitArray(g);
2602 
2603   // We used the file name relative to recorded datapath
2604   PlugSetPath(filename, ((PBDEF)To_Def)->Fn, GetPath());
2605 
2606   // Serialize the modified table
2607   if (!Bp->Serialize(g, Top, filename, Pretty))
2608     puts(g->Message);
2609 
2610 } // end of CloseDB
2611 
2612 /* ---------------------------TDBBCL class --------------------------- */
2613 
2614 /***********************************************************************/
2615 /*  TDBBCL class constructor.                                          */
2616 /***********************************************************************/
2617 TDBBCL::TDBBCL(PBDEF tdp) : TDBCAT(tdp) {
2618   Topt = tdp->GetTopt();
2619   Db = tdp->Schema;
2620   Dsn = tdp->Uri;
2621 } // end of TDBBCL constructor
2622 
2623 /***********************************************************************/
2624 /*  GetResult: Get the list the JSON file columns.                     */
2625 /***********************************************************************/
2626 PQRYRES TDBBCL::GetResult(PGLOBAL g) {
2627   return BSONColumns(g, Db, Dsn, Topt, false);
2628 } // end of GetResult
2629 
2630 /* --------------------------- End of json --------------------------- */
2631