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 | /***********************************************************************/ |
54 | USETEMP UseTemp(void); |
55 | char *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 | /***********************************************************************/ |
72 | PQRYRES 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 | /***********************************************************************/ |
160 | JSONDISC::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 | |
174 | int 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 | |
381 | err: |
382 | if (tdp->Pretty != 2) |
383 | tjnp->CloseDB(g); |
384 | |
385 | return 0; |
386 | } // end of GetColumns |
387 | |
388 | bool 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 | |
475 | void 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 | |
531 | JSONDEF::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 | /***********************************************************************/ |
552 | bool 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 | /***********************************************************************/ |
593 | PTDB 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 | /***********************************************************************/ |
715 | TDBJSN::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 | |
750 | TDBJSN::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 |
775 | PTDB 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 | /***********************************************************************/ |
795 | PCOL 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 | /***********************************************************************/ |
805 | PCOL 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 | /***********************************************************************/ |
821 | int 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 | /***********************************************************************/ |
834 | int 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 | /***********************************************************************/ |
845 | PJSON 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 | /***********************************************************************/ |
879 | bool 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 | /***********************************************************************/ |
918 | bool TDBJSN::(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 | /***********************************************************************/ |
949 | int 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 | /***********************************************************************/ |
988 | int 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 | /***********************************************************************/ |
1084 | int 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 | /***********************************************************************/ |
1098 | JSONCOL::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 | /***********************************************************************/ |
1117 | JSONCOL::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 | /***********************************************************************/ |
1134 | bool 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 | /***********************************************************************/ |
1151 | bool 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 | /***********************************************************************/ |
1168 | bool 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 | /***********************************************************************/ |
1302 | bool 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 | |
1381 | fin: |
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 | /***********************************************************************/ |
1390 | PSZ 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 | /***********************************************************************/ |
1458 | PVAL 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 | /***********************************************************************/ |
1472 | void 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 | /***********************************************************************/ |
1516 | void 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 | /***********************************************************************/ |
1533 | PVAL 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 | /***********************************************************************/ |
1597 | PVAL 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 | /***********************************************************************/ |
1637 | PVAL 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 | /***********************************************************************/ |
1738 | PJSON 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 | /***********************************************************************/ |
1816 | void 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 | /***********************************************************************/ |
1908 | TDBJSON::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 | |
1915 | TDBJSON::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 |
1924 | PTDB 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 | /***********************************************************************/ |
1943 | int 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 | /***********************************************************************/ |
1958 | int 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 | /***********************************************************************/ |
2092 | int 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 | /***********************************************************************/ |
2110 | int 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 | /***********************************************************************/ |
2121 | void 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 | /***********************************************************************/ |
2132 | int 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 | /***********************************************************************/ |
2145 | int 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 | /***********************************************************************/ |
2163 | bool 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 | /***********************************************************************/ |
2190 | bool 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 | /***********************************************************************/ |
2228 | int 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 | /***********************************************************************/ |
2257 | int 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 | /***********************************************************************/ |
2293 | int 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 | /***********************************************************************/ |
2316 | void 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 | /***********************************************************************/ |
2340 | TDBJCL::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 | /***********************************************************************/ |
2350 | PQRYRES TDBJCL::GetResult(PGLOBAL g) |
2351 | { |
2352 | return JSONColumns(g, Db, Dsn, Topt, false); |
2353 | } // end of GetResult |
2354 | |
2355 | /* --------------------------- End of json --------------------------- */ |
2356 | |