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