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