| 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 | |