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 /***********************************************************************/ 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 /***********************************************************************/ 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 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 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 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 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 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 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 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 2626 PQRYRES TDBBCL::GetResult(PGLOBAL g) { 2627 return BSONColumns(g, Db, Dsn, Topt, false); 2628 } // end of GetResult 2629 2630 /* --------------------------- End of json --------------------------- */ 2631