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