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