summaryrefslogtreecommitdiff
path: root/storage/connect/tabjson.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/tabjson.cpp')
-rw-r--r--storage/connect/tabjson.cpp1327
1 files changed, 1327 insertions, 0 deletions
diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp
new file mode 100644
index 00000000000..69b05ee067d
--- /dev/null
+++ b/storage/connect/tabjson.cpp
@@ -0,0 +1,1327 @@
+/************* tabjson C++ Program Source Code File (.CPP) *************/
+/* PROGRAM NAME: tabxjson Version 1.0 */
+/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
+/* This program are the JSON class DB execution routines. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the MariaDB header file. */
+/***********************************************************************/
+#include <my_global.h>
+
+/***********************************************************************/
+/* Include application header files: */
+/* global.h is header containing all global declarations. */
+/* plgdbsem.h is header containing the DB application declarations. */
+/* tdbdos.h is header containing the TDBDOS declarations. */
+/* json.h is header containing the JSON classes declarations. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+//#include "xtable.h"
+//#include "mycat.h" // for FNC_COL
+#include "maputil.h"
+#include "filamtxt.h"
+#include "tabdos.h"
+//#include "resource.h" // for IDS_COLUMNS
+#include "tabjson.h"
+#include "filamap.h"
+#if defined(ZIP_SUPPORT)
+#include "filamzip.h"
+#endif // ZIP_SUPPORT
+#include "tabmul.h"
+#include "checklvl.h"
+
+/***********************************************************************/
+/* External function. */
+/***********************************************************************/
+USETEMP UseTemp(void);
+
+/* -------------------------- Class JSONDEF -------------------------- */
+
+JSONDEF::JSONDEF(void)
+{
+ Jmode = MODE_OBJECT;
+ Objname = NULL;
+ Xcol = NULL;
+ Limit = 1;
+ ReadMode = 0;
+} // end of JSONDEF constructor
+
+/***********************************************************************/
+/* DefineAM: define specific AM block values. */
+/***********************************************************************/
+bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+{
+ Jmode = (JMODE)GetIntCatInfo("Jmode", MODE_OBJECT);
+ Objname = GetStringCatInfo(g, "Object", NULL);
+ Xcol = GetStringCatInfo(g, "Expand", NULL);
+ Pretty = GetIntCatInfo("Pretty", 2);
+ Limit = GetIntCatInfo("Limit", 10);
+ return DOSDEF::DefineAM(g, "DOS", poff);
+} // end of DefineAM
+
+/***********************************************************************/
+/* GetTable: makes a new Table Description Block. */
+/***********************************************************************/
+PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
+{
+ PTDBASE tdbp;
+ PTXF txfp = NULL;
+
+ // JSN not used for pretty=1 for insert or delete
+ if (!Pretty || (Pretty == 1 && (m == MODE_READ || m == MODE_UPDATE))) {
+ USETEMP tmp = UseTemp();
+ bool map = Mapped && m != MODE_INSERT &&
+ !(tmp != TMP_NO && m == MODE_UPDATE) &&
+ !(tmp == TMP_FORCE &&
+ (m == MODE_UPDATE || m == MODE_DELETE));
+
+ if (Compressed) {
+#if defined(ZIP_SUPPORT)
+ if (Compressed == 1)
+ txfp = new(g) ZIPFAM(this);
+ else
+ txfp = new(g) ZLBFAM(this);
+#else // !ZIP_SUPPORT
+ sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
+ return NULL;
+#endif // !ZIP_SUPPORT
+ } else if (map)
+ txfp = new(g) MAPFAM(this);
+ else
+ txfp = new(g) DOSFAM(this);
+
+ // Txfp must be set for TDBDOS
+ tdbp = new(g) TDBJSN(this, txfp);
+ } else {
+ txfp = new(g) DOSFAM(this);
+ tdbp = new(g) TDBJSON(this, txfp);
+ } // endif Pretty
+
+ if (Multiple)
+ tdbp = new(g) TDBMUL(tdbp);
+
+ return tdbp;
+} // end of GetTable
+
+/* --------------------------- Class TDBJSN -------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBJSN class. */
+/***********************************************************************/
+TDBJSN::TDBJSN(PJDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
+ {
+ Row = NULL;
+ Colp = NULL;
+ Jmode = tdp->Jmode;
+ Xcol = tdp->Xcol;
+ Fpos = -1;
+ Spos = N = 0;
+ Limit = tdp->Limit;
+ Pretty = tdp->Pretty;
+ Strict = tdp->Strict;
+ NextSame = false;
+ Comma = false;
+ SameRow = 0;
+ Xval = -1;
+ } // end of TDBJSN standard constructor
+
+TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
+ {
+ Row = tdbp->Row;
+ Colp = tdbp->Colp;
+ Jmode = tdbp->Jmode;
+ Xcol = tdbp->Xcol;
+ Fpos = tdbp->Fpos;
+ Spos = tdbp->Spos;
+ N = tdbp->N;
+ Limit = tdbp->Limit;
+ Pretty = tdbp->Pretty;
+ Strict = tdbp->Strict;
+ NextSame = tdbp->NextSame;
+ Comma = tdbp->Comma;
+ SameRow = tdbp->SameRow;
+ Xval = tdbp->Xval;
+ } // end of TDBJSN copy constructor
+
+// Used for update
+PTDB TDBJSN::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PJCOL cp1, cp2;
+ PGLOBAL g = t->G;
+
+ tp = new(g) TDBJSN(this);
+
+ for (cp1 = (PJCOL)Columns; cp1; cp1 = (PJCOL)cp1->GetNext()) {
+ cp2 = new(g) JSONCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate JSN column description block. */
+/***********************************************************************/
+PCOL TDBJSN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ PJCOL colp = new(g) JSONCOL(g, cdp, this, cprec, n);
+
+ return (colp->ParseJpath(g)) ? NULL : colp;
+ } // end of MakeCol
+
+/***********************************************************************/
+/* InsertSpecialColumn: Put a special column ahead of the column list.*/
+/***********************************************************************/
+PCOL TDBJSN::InsertSpecialColumn(PGLOBAL g, PCOL colp)
+ {
+ if (!colp->IsSpecial())
+ return NULL;
+
+//if (Xcol && ((SPCBLK*)colp)->GetRnm())
+// colp->SetKey(0); // Rownum is no more a key
+
+ colp->SetNext(Columns);
+ Columns = colp;
+ return colp;
+ } // end of InsertSpecialColumn
+
+/***********************************************************************/
+/* JSON Cardinality: returns table size in number of rows. */
+/***********************************************************************/
+int TDBJSN::Cardinality(PGLOBAL g)
+ {
+ if (!g)
+ return 0;
+ else if (Cardinal < 0)
+ Cardinal = TDBDOS::Cardinality(g);
+
+ return Cardinal;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* JSON GetMaxSize: returns file size estimate in number of lines. */
+/***********************************************************************/
+int TDBJSN::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0)
+ MaxSize = TDBDOS::GetMaxSize(g) * ((Xcol) ? Limit : 1);
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* OpenDB: Data Base open routine for JSN access method. */
+/***********************************************************************/
+bool TDBJSN::OpenDB(PGLOBAL g)
+ {
+ if (Use == USE_OPEN) {
+ /*******************************************************************/
+ /* Table already open replace it at its beginning. */
+ /*******************************************************************/
+ for (PJCOL cp = (PJCOL)Columns; cp; cp = (PJCOL)cp->GetNext()) {
+ cp->Nx = 0;
+ cp->Arp = NULL;
+ } // endfor cp
+
+ Fpos= -1;
+ Spos = 0;
+ NextSame = false;
+ SameRow = 0;
+ } else {
+ /*******************************************************************/
+ /* First opening. */
+ /*******************************************************************/
+ if (Mode == MODE_INSERT)
+ switch (Jmode) {
+ case MODE_OBJECT: Row = new(g) JOBJECT; break;
+ case MODE_ARRAY: Row = new(g) JARRAY; break;
+ case MODE_VALUE: Row = new(g) JVALUE; break;
+ default:
+ sprintf(g->Message, "Invalid Jmode %d", Jmode);
+ return true;
+ } // endswitch Jmode
+
+ } // endif Use
+
+ return TDBDOS::OpenDB(g);
+ } // end of OpenDB
+
+/***********************************************************************/
+/* SkipHeader: Physically skip first header line if applicable. */
+/* This is called from TDBDOS::OpenDB and must be executed before */
+/* Kindex construction if the file is accessed using an index. */
+/***********************************************************************/
+bool TDBJSN::SkipHeader(PGLOBAL g)
+ {
+ int len = GetFileLength(g);
+ bool rc = false;
+
+#if defined(_DEBUG)
+ if (len < 0)
+ return true;
+#endif // _DEBUG
+
+#if defined(WIN32)
+#define Ending 2
+#else // !WIN32
+#define Ending 1
+#endif // !WIN32
+
+ if (Pretty == 1) {
+ if (Mode == MODE_INSERT || Mode == MODE_DELETE) {
+ // Mode Insert and delete are no more handled here
+ assert(false);
+ } else if (len) // !Insert && !Delete
+ rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
+
+ } // endif Pretty
+
+ return rc;
+ } // end of SkipHeader
+
+/***********************************************************************/
+/* ReadDB: Data Base read routine for JSN access method. */
+/***********************************************************************/
+int TDBJSN::ReadDB(PGLOBAL g)
+ {
+ int rc;
+
+ N++;
+
+ if (NextSame) {
+ SameRow++;
+ return RC_OK;
+ } else if ((rc = TDBDOS::ReadDB(g)) == RC_OK)
+ if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK)) {
+ // Deferred reading failed
+ } else if (!(Row = ParseJson(g, To_Line,
+ strlen(To_Line), Pretty, &Comma))) {
+ rc = (Pretty == 1 && !strcmp(To_Line, "]")) ? RC_EF : RC_FX;
+ } else {
+ SameRow = 0;
+ Fpos++;
+ rc = RC_OK;
+ } // endif's
+
+ return rc;
+ } // end of ReadDB
+
+/***********************************************************************/
+/* PrepareWriting: Prepare the line for WriteDB. */
+/***********************************************************************/
+ bool TDBJSN::PrepareWriting(PGLOBAL g)
+ {
+ PSZ s = Serialize(g, Row, NULL, Pretty);
+
+ if (s) {
+ if (Comma)
+ strcat(s, ",");
+
+ if ((signed)strlen(s) > Lrecl) {
+ sprintf(g->Message, "Line would be truncated (lrecl=%d)", Lrecl);
+ return true;
+ } else
+ strcpy(To_Line, s);
+
+ Row->Clear();
+ return false;
+ } else
+ return true;
+
+ } // end of PrepareWriting
+
+/* ----------------------------- JSNCOL ------------------------------- */
+
+/***********************************************************************/
+/* JSNCOL public constructor. */
+/***********************************************************************/
+JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
+ : DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
+ {
+ Tjp = (TDBJSN *)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
+ Arp = NULL;
+ Jpath = cdp->GetFmt();
+ MulVal = NULL;
+ Nodes = NULL;
+ Nod = Nx =0;
+ Ival = -1;
+ Xpd = false;
+ Parsed = false;
+ } // end of JSONCOL constructor
+
+/***********************************************************************/
+/* JSONCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+JSONCOL::JSONCOL(JSONCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
+ {
+ Tjp = col1->Tjp;
+ Arp = col1->Arp;
+ Jpath = col1->Jpath;
+ MulVal = col1->MulVal;
+ Nodes = col1->Nodes;
+ Nod = col1->Nod;
+ Ival = col1->Ival;
+ Nx = col1->Nx;
+ Xpd = col1->Xpd;
+ Parsed = col1->Parsed;
+ } // end of JSONCOL copy constructor
+
+/***********************************************************************/
+/* SetBuffer: prepare a column block for write operation. */
+/***********************************************************************/
+bool JSONCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
+ {
+ if (DOSCOL::SetBuffer(g, value, ok, check))
+ return true;
+
+ // Parse the json path
+ if (ParseJpath(g))
+ return true;
+
+ Tjp = (TDBJSN*)To_Tdb;
+ return false;
+ } // end of SetBuffer
+
+/***********************************************************************/
+/* Analyse array processing options. */
+/***********************************************************************/
+bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
+ {
+ if (Tjp->Xcol && nm && !strcmp(nm, Tjp->Xcol) &&
+ (Tjp->Xval < 0 || Tjp->Xval == i)) {
+ Xpd = true; // Expandable object
+ Nodes[i].Op = OP_XX;
+ Tjp->Xval = i;
+ } else if (b) {
+ strcpy(g->Message, "Cannot expand more than one array");
+ return true;
+ } // endif Xcol
+
+ return false;
+ } // end of CheckExpand
+
+/***********************************************************************/
+/* Analyse array processing options. */
+/***********************************************************************/
+bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
+ {
+ int n = (int)strlen(p);
+ bool dg = true;
+ PJNODE jnp = &Nodes[i];
+
+ if (*p) {
+ if (p[--n] == ']') {
+ p[n--] = 0;
+ p++;
+ } else {
+ // Wrong array specification
+ sprintf(g->Message,
+ "Invalid array specification %s for %s", p, Name);
+ return true;
+ } // endif p
+
+ } // endif *p
+
+ // To check whether a numeric Rank was specified
+ for (int k = 0; dg && p[k]; k++)
+ dg = isdigit(p[k]) > 0;
+
+ if (!n) {
+ // Default specifications
+ if (CheckExpand(g, i, nm, false))
+ return true;
+ else if (jnp->Op != OP_XX)
+ if (!Value->IsTypeNum()) {
+ jnp->CncVal = AllocateValue(g, ", ", TYPE_STRING);
+ jnp->Op = OP_CNC;
+ } else
+ jnp->Op = OP_ADD;
+
+ } else if (dg) {
+ if (atoi(p) > 0) {
+ // Return nth value
+ jnp->Rank = atoi(p);
+ jnp->Op = OP_EQ;
+ } else // Ignore array
+ jnp->Op = OP_NULL;
+
+ } else if (n == 1) {
+ // Set the Op value;
+ switch (*p) {
+ case '+': jnp->Op = OP_ADD; break;
+ case '*': jnp->Op = OP_MULT; break;
+ case '>': jnp->Op = OP_MAX; break;
+ case '<': jnp->Op = OP_MIN; break;
+ case '#': jnp->Op = OP_NUM; break;
+ case '!': jnp->Op = OP_SEP; break; // Average
+ case 'x':
+ case 'X': // Expand this array
+ if (!Tjp->Xcol && nm) {
+ Xpd = true;
+ jnp->Op = OP_XX;
+ Tjp->Xval = i;
+ Tjp->Xcol = nm;
+ } else if (CheckExpand(g, i, nm, true))
+ return true;
+
+ break;
+ default:
+ sprintf(g->Message,
+ "Invalid function specification %c for %s", *p, Name);
+ return true;
+ } // endswitch *p
+
+ } else if (*p == '"' && p[n - 1] == '"') {
+ // This is a concat specification
+ jnp->Op = OP_CNC;
+
+ if (n > 2) {
+ // Set concat intermediate string
+ p[n - 1] = 0;
+ jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
+ } // endif n
+
+ } else {
+ sprintf(g->Message, "Wrong array specification for %s", Name);
+ return true;
+ } // endif's
+
+ return false;
+ } // end of SetArrayOptions
+
+/***********************************************************************/
+/* Parse the eventual passed Jpath information. */
+/* This information can be specified in the Fieldfmt column option */
+/* when creating the table. It permits to indicate the position of */
+/* the node corresponding to that column. */
+/***********************************************************************/
+bool JSONCOL::ParseJpath(PGLOBAL g)
+ {
+ char *p, *p2 = NULL, *pbuf = NULL;
+ int i;
+ bool mul = false;
+
+ if (Parsed)
+ return false; // Already done
+ else if (InitValue(g))
+ return true;
+ else if (!Jpath)
+ Jpath = Name;
+
+ pbuf = (char*)PlugSubAlloc(g, NULL, strlen(Jpath) + 1);
+ strcpy(pbuf, Jpath);
+
+ // The Jpath must be analyzed
+ for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++)
+ Nod++; // One path node found
+
+ Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE));
+ memset(Nodes, 0, (Nod) * sizeof(JNODE));
+
+ // Analyze the Jpath for this column
+ for (i = 0, p = pbuf; i < Nod; i++, p = (p2 ? p2 + 1 : p + strlen(p))) {
+ if ((p2 = strchr(p, ':')))
+ *p2 = 0;
+
+ // Jpath must be explicit
+ if (*p == 0 || *p == '[') {
+ // Analyse intermediate array processing
+ if (SetArrayOptions(g, p, i, Nodes[i-1].Key))
+ return true;
+
+ } else {
+ Nodes[i].Key = p;
+ Nodes[i].Op = OP_EXIST;
+ } // endif's
+
+ } // endfor i, p
+
+ MulVal = AllocateValue(g, Value);
+ Parsed = true;
+ return false;
+ } // end of ParseJpath
+
+/***********************************************************************/
+/* SetValue: Set a value from a JVALUE contains. */
+/***********************************************************************/
+void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
+ {
+ if (val) {
+ if (Nodes[n].Op == OP_NUM)
+ vp->SetValue(1);
+ else {
+ again:
+ switch (val->GetValType()) {
+ case TYPE_STRING:
+ case TYPE_INT:
+ case TYPE_DOUBLE:
+ vp->SetValue_pval(val->GetValue());
+ break;
+ case TYPE_TINY:
+ if (vp->IsTypeNum())
+ vp->SetValue(val->GetInteger() ? 1 : 0);
+ else
+ vp->SetValue_psz(val->GetInteger() ? "true" : "false");
+
+ break;
+ case TYPE_JAR:
+ val = val->GetArray()->GetValue(0);
+ goto again;
+ case TYPE_JOB:
+ if (!vp->IsTypeNum()) {
+ vp->SetValue_psz(val->GetObject()->GetText(g));
+ break;
+ } // endif Type
+
+ default:
+ vp->Reset();
+ } // endswitch Type
+
+ } // endelse
+
+ } else
+ vp->Reset();
+
+ } // end of SetJsonValue
+
+/***********************************************************************/
+/* GetRow: Get the object containing this column. */
+/***********************************************************************/
+PJSON JSONCOL::GetRow(PGLOBAL g, int mode)
+ {
+ PJVAL val;
+ PJAR arp;
+ PJSON nwr, row = Tjp->Row;
+
+ for (int i = 0; i < Nod-1 && row; i++) {
+ switch (row->GetType()) {
+ case TYPE_JOB:
+ if (!Nodes[i].Key)
+ // Expected Array was not there
+ continue;
+
+ val = ((PJOB)row)->GetValue(Nodes[i].Key);
+ break;
+ case TYPE_JAR:
+ if (!Nodes[i].Key) {
+ if (Nodes[i].Op != OP_NULL) {
+ Ival = i;
+ arp = (PJAR)row;
+
+ if (mode < 2) // First pass
+ Arp = arp;
+
+ if (Nodes[i].Op != OP_XX) {
+ if (Nodes[i].Rank)
+ val = arp->GetValue(Nodes[i].Rank - 1);
+ else
+ val = arp->GetValue(arp == Arp ? Nx : 0);
+
+ } else
+ val = arp->GetValue(Tjp->SameRow);
+
+ } else
+ val = NULL;
+
+ } else {
+ strcpy(g->Message, "Unexpected array");
+ val = NULL; // Not an expected array
+ } // endif Nodes
+
+ break;
+ case TYPE_JVAL:
+ val = (PJVAL)row;
+ break;
+ default:
+ sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
+ val = NULL;
+ } // endswitch Type
+
+ if (val) {
+ row = val->GetJson();
+ } else if (mode == 1) { // mode write
+ // Construct missing objects
+ for (i++; row && i < Nod; i++) {
+ if (!Nodes[i].Key) {
+ // Construct intermediate array
+ nwr = new(g) JARRAY;
+ } else {
+ nwr = new(g) JOBJECT;
+ } // endif Nodes
+
+ if (row->GetType() == TYPE_JOB) {
+ ((PJOB)row)->SetValue(g, new(g) JVALUE(nwr), Nodes[i-1].Key);
+ } else if (row->GetType() == TYPE_JAR) {
+ ((PJAR)row)->AddValue(g, new(g) JVALUE(nwr));
+ ((PJAR)row)->InitArray(g);
+ } else {
+ strcpy(g->Message, "Wrong type when writing new row");
+ nwr = NULL;
+ } // endif's
+
+ row = nwr;
+ } // endfor i
+
+ break;
+ } else
+ row = NULL;
+
+ } // endfor i
+
+ return row;
+ } // end of GetRow
+
+/***********************************************************************/
+/* ReadColumn: */
+/***********************************************************************/
+void JSONCOL::ReadColumn(PGLOBAL g)
+ {
+ int mode = 0, n = Nod - 1;
+ PJSON row;
+ PJVAL val = NULL;
+
+ evenmore:
+ row = GetRow(g, mode);
+
+ more:
+ if (row) switch (row->GetType()) {
+ case TYPE_JOB:
+ if (Nodes[n].Key)
+ val = row->GetValue(Nodes[n].Key);
+ else
+ val = new(g) JVALUE(row);
+
+ break;
+ case TYPE_JAR:
+ // Multiple column ?
+ if (Nodes[n].Op != OP_NULL) {
+ Arp = (PJAR)row;
+ val = Arp->GetValue(Nodes[n].Rank > 0 ?
+ Nodes[n].Rank - 1 :
+ Nodes[n].Op == OP_XX ? Tjp->SameRow : Nx);
+ Ival = n;
+ } else
+ val = NULL;
+
+ break;
+ case TYPE_JVAL:
+ val = (PJVAL)row;
+ break;
+ default:
+ sprintf(g->Message, "Wrong return value type %d", row->GetType());
+ Value->Reset();
+ return;
+ } // endswitch Type
+
+ if (!Nx /*|| (Xpd)*/)
+ SetJsonValue(g, Value, val, n);
+
+ if (Arp) {
+ // Multiple column
+ int ars = (Nodes[Ival].Rank > 0) ? 1 : MY_MIN(Tjp->Limit, Arp->size());
+
+ if (Nodes[Ival].Op == OP_XX) {
+ if (ars > Tjp->SameRow + 1)
+ Tjp->NextSame = true; // More to come
+ else {
+ Tjp->NextSame = false;
+ Arp = NULL;
+ } // endelse
+
+ } else {
+ if (Nx && val) {
+ SetJsonValue(g, MulVal, val, Ival);
+
+ if (!MulVal->IsZero()) {
+ PVAL val[2];
+ bool err;
+
+ switch (Nodes[Ival].Op) {
+ case OP_CNC:
+ if (Nodes[Ival].CncVal) {
+ val[0] = Nodes[Ival].CncVal;
+ err = Value->Compute(g, val, 1, Nodes[Ival].Op);
+ } // endif CncVal
+
+ val[0] = MulVal;
+ err = Value->Compute(g, val, 1, Nodes[Ival].Op);
+ break;
+ case OP_NUM:
+ case OP_SEP:
+ val[0] = Value;
+ val[1] = MulVal;
+ err = Value->Compute(g, val, 2, OP_ADD);
+ break;
+ default:
+ val[0] = Value;
+ val[1] = MulVal;
+ err = Value->Compute(g, val, 2, Nodes[Ival].Op);
+ } // endswitch Op
+
+ if (err)
+ Value->Reset();
+
+ } // endif Zero
+
+ } // endif Nx
+
+ if (ars > ++Nx) {
+ if (Ival != n) {
+ mode = 2;
+ goto evenmore;
+ } else
+ goto more;
+
+ } else {
+ if (Nodes[Ival].Op == OP_SEP) {
+ // Calculate average
+ PVAL val[2];
+
+ MulVal->SetValue(ars);
+ val[0] = Value;
+ val[1] = MulVal;
+
+ if (Value->Compute(g, val, 2, OP_DIV))
+ Value->Reset();
+
+ } // endif Op
+
+ Arp = NULL;
+ Nx = 0;
+ } // endif ars
+
+ } // endif Op
+
+ } // endif Arp
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: */
+/***********************************************************************/
+void JSONCOL::WriteColumn(PGLOBAL g)
+ {
+ /*********************************************************************/
+ /* Check whether this node must be written. */
+ /*********************************************************************/
+ if (Value != To_Val)
+ Value->SetValue_pval(To_Val, FALSE); // Convert the updated value
+
+ /*********************************************************************/
+ /* On INSERT Null values are represented by no node. */
+ /*********************************************************************/
+ if (Value->IsNull() && Tjp->Mode == MODE_INSERT)
+ return;
+
+ PJOB objp = NULL;
+ PJAR arp = NULL;
+ PJVAL jvp = NULL;
+ PJSON row = GetRow(g, 1);
+ JTYP type = row->GetType();
+
+ switch (row->GetType()) {
+ case TYPE_JOB: objp = (PJOB)row; break;
+ case TYPE_JAR: arp = (PJAR)row; break;
+ case TYPE_JVAL: jvp = (PJVAL)row; break;
+ default: row = NULL; // ???????????????????????????
+ } // endswitch Type
+
+ if (row) switch (Buf_Type) {
+ case TYPE_STRING:
+ case TYPE_DATE:
+ case TYPE_INT:
+ case TYPE_DOUBLE:
+ if (arp) {
+ if (Nodes[Nod-1].Rank)
+ arp->SetValue(g, new(g) JVALUE(g, Value), Nodes[Nod-1].Rank-1);
+ else
+ arp->AddValue(g, new(g) JVALUE(g, Value));
+
+ arp->InitArray(g);
+ } else if (objp) {
+ if (Nodes[Nod-1].Key)
+ objp->SetValue(g, new(g) JVALUE(g, Value), Nodes[Nod-1].Key);
+
+ } else if (jvp)
+ jvp->SetValue(Value);
+
+ break;
+ default: // ??????????
+ sprintf(g->Message, "Invalid column type %d", Buf_Type);
+ } // endswitch Type
+
+ } // end of WriteColumn
+
+/* -------------------------- Class TDBJSON -------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBJSON class. */
+/***********************************************************************/
+TDBJSON::TDBJSON(PJDEF tdp, PTXF txfp) : TDBJSN(tdp, txfp)
+ {
+ Top = NULL;
+ Doc = NULL;
+ Objname = tdp->Objname;
+ Multiple = tdp->Multiple;
+ Done = Changed = false;
+ } // end of TDBJSON standard constructor
+
+TDBJSON::TDBJSON(PJTDB tdbp) : TDBJSN(tdbp)
+ {
+ Top = tdbp->Top;
+ Doc = tdbp->Doc;
+ Objname = tdbp->Objname;
+ Multiple = tdbp->Multiple;
+ Done = tdbp->Done;
+ Changed = tdbp->Changed;
+ } // end of TDBJSON copy constructor
+
+// Used for update
+PTDB TDBJSON::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PJCOL cp1, cp2;
+ PGLOBAL g = t->G;
+
+ tp = new(g) TDBJSON(this);
+
+ for (cp1 = (PJCOL)Columns; cp1; cp1 = (PJCOL)cp1->GetNext()) {
+ cp2 = new(g) JSONCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Make the document tree from a file. */
+/***********************************************************************/
+int TDBJSON::MakeNewDoc(PGLOBAL g)
+ {
+ // Create a void table that will be populated
+ Doc = new(g) JARRAY;
+
+ if (Objname) {
+ // Parse and allocate Objname item(s)
+ char *p;
+ char *objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname)+1);
+ int i;
+ PJOB objp;
+ PJAR arp;
+ PJVAL val = NULL;
+
+ strcpy(objpath, Objname);
+ Top = NULL;
+
+ for (; objpath; objpath = p) {
+ if ((p = strchr(objpath, ':')))
+ *p++ = 0;
+
+ if (*objpath != '[') {
+ objp = new(g) JOBJECT;
+
+ if (!Top)
+ Top = objp;
+
+ if (val)
+ val->SetValue(objp);
+
+ val = new(g) JVALUE;
+ objp->SetValue(g, val, objpath);
+ } else if (objpath[strlen(objpath)-1] == ']') {
+ arp = new(g) JARRAY;
+
+ if (!Top)
+ Top = arp;
+
+ if (val)
+ val->SetValue(arp);
+
+ val = new(g) JVALUE;
+ i = atoi(objpath+1) - 1;
+ arp->SetValue(g, val, i);
+ arp->InitArray(g);
+ } else {
+ sprintf(g->Message, "Invalid Table path %s", Objname);
+ return RC_FX;
+ } // endif objpath
+
+ } // endfor p
+
+ val->SetValue(Doc);
+ } else
+ Top = Doc;
+
+ return RC_OK;
+ } // end of MakeNewDoc
+
+/***********************************************************************/
+/* Make the document tree from a file. */
+/***********************************************************************/
+int TDBJSON::MakeDocument(PGLOBAL g)
+ {
+ char *p, *memory, *objpath, *key, filename[_MAX_PATH];
+ int i, len;
+ HANDLE hFile;
+ MEMMAP mm;
+ PJSON jsp;
+ PJOB objp = NULL;
+ PJAR arp = NULL;
+ PJVAL val = NULL;
+
+ if (Done)
+ return RC_OK;
+ else
+ Done = true;
+
+ // Now open the JSON file
+ PlugSetPath(filename, Txfp->To_File, GetPath());
+
+ /*********************************************************************/
+ /* Create the mapping file object. */
+ /*********************************************************************/
+ hFile = CreateFileMap(g, filename, &mm, MODE_READ, false);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ DWORD drc = GetLastError();
+
+ if (drc != ERROR_FILE_NOT_FOUND || Mode != MODE_INSERT) {
+ sprintf(g->Message, MSG(OPEN_ERROR), drc, 10, filename);
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
+ (LPTSTR)filename, sizeof(filename), NULL);
+ strcat(g->Message, filename);
+ return RC_FX;
+ } else
+ return MakeNewDoc(g);
+
+ } // endif hFile
+
+ /*********************************************************************/
+ /* Get the file size (assuming file is smaller than 4 GB) */
+ /*********************************************************************/
+ len = mm.lenL;
+ memory = (char *)mm.memory;
+
+ if (!len) { // Empty file
+ CloseFileHandle(hFile);
+ UnmapViewOfFile(memory);
+
+ if (Mode == MODE_INSERT)
+ return MakeNewDoc(g);
+
+ } // endif len
+
+ if (!memory) {
+ CloseFileHandle(hFile);
+ sprintf(g->Message, MSG(MAP_VIEW_ERROR), filename, GetLastError());
+ return RC_FX;
+ } // endif Memory
+
+ CloseFileHandle(hFile); // Not used anymore
+ hFile = INVALID_HANDLE_VALUE; // For Fblock
+
+ /*********************************************************************/
+ /* Parse the json file and allocate its tree structure. */
+ /*********************************************************************/
+ g->Message[0] = 0;
+ jsp = Top = ParseJson(g, memory, len, Pretty);
+ UnmapViewOfFile(memory);
+
+ if (!jsp && g->Message[0])
+ return RC_FX;
+
+ if (Objname) {
+ objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname) + 1);
+ strcpy(objpath, Objname);
+ } else
+ objpath = NULL;
+
+ /*********************************************************************/
+ /* Find the table in the tree structure. */
+ /*********************************************************************/
+ for (; jsp && objpath; objpath = p) {
+ if ((p = strchr(objpath, ':')))
+ *p++ = 0;
+
+ if (*objpath != '[') { // objpass is a key
+ if (jsp->GetType() != TYPE_JOB) {
+ strcpy(g->Message, "Table path does no match json file");
+ return RC_FX;
+ } // endif Type
+
+ key = objpath;
+ objp = jsp->GetObject();
+ arp = NULL;
+ val = objp->GetValue(key);
+
+ if (!val || !(jsp = val->GetJson())) {
+ sprintf(g->Message, "Cannot find object key %s", key);
+ return RC_FX;
+ } // endif val
+
+ } else if (objpath[strlen(objpath)-1] == ']') {
+ if (jsp->GetType() != TYPE_JAR) {
+ strcpy(g->Message, "Table path does no match json file");
+ return RC_FX;
+ } // endif Type
+
+ arp = jsp->GetArray();
+ objp = NULL;
+ i = atoi(objpath+1) - 1;
+ val = arp->GetValue(i);
+
+ if (!val) {
+ sprintf(g->Message, "Cannot find array value %d", i);
+ return RC_FX;
+ } // endif val
+
+ } else {
+ sprintf(g->Message, "Invalid Table path %s", Objname);
+ return RC_FX;
+ } // endif objpath
+
+ jsp = val->GetJson();
+ } // endfor objpath
+
+ if (jsp && jsp->GetType() == TYPE_JAR)
+ Doc = jsp->GetArray();
+ else {
+ // The table is void or is just one object or one value
+ Doc = new(g) JARRAY;
+
+ if (val) {
+ Doc->AddValue(g, val);
+ Doc->InitArray(g);
+ } else if (jsp) {
+ Doc->AddValue(g, new(g) JVALUE(jsp));
+ Doc->InitArray(g);
+ } // endif val
+
+ if (objp)
+ objp->SetValue(g, new(g) JVALUE(Doc), key);
+ else if (arp)
+ arp->SetValue(g, new(g) JVALUE(Doc), i);
+ else
+ Top = Doc;
+
+ } // endif jsp
+
+ return RC_OK;
+ } // end of MakeDocument
+
+/***********************************************************************/
+/* JSON Cardinality: returns table size in number of rows. */
+/***********************************************************************/
+int TDBJSON::Cardinality(PGLOBAL g)
+ {
+ if (!g)
+ return (Xcol || Multiple) ? 0 : 1;
+ else if (Cardinal < 0)
+ if (!Multiple) {
+ if (MakeDocument(g) == RC_OK)
+ Cardinal = Doc->size();
+
+ } else
+ return 10;
+
+ return Cardinal;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* JSON GetMaxSize: returns table size estimate in number of rows. */
+/***********************************************************************/
+int TDBJSON::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0)
+ MaxSize = Cardinality(g) * ((Xcol) ? Limit : 1);
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* ResetSize: call by TDBMUL when calculating size estimate. */
+/***********************************************************************/
+void TDBJSON::ResetSize(void)
+ {
+ MaxSize = Cardinal = -1;
+ Fpos = -1;
+ N = 0;
+ Done = false;
+ } // end of ResetSize
+
+/***********************************************************************/
+/* TDBJSON is not indexable. */
+/***********************************************************************/
+int TDBJSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
+ {
+ if (pxdf) {
+ strcpy(g->Message, "JSON not indexable when pretty = 2");
+ return RC_FX;
+ } else
+ return RC_OK;
+
+ } // end of MakeIndex
+
+/***********************************************************************/
+/* JSON Access Method opening routine. */
+/***********************************************************************/
+bool TDBJSON::OpenDB(PGLOBAL g)
+ {
+ if (Use == USE_OPEN) {
+ /*******************************************************************/
+ /* Table already open replace it at its beginning. */
+ /*******************************************************************/
+ for (PJCOL cp = (PJCOL)Columns; cp; cp = (PJCOL)cp->GetNext()) {
+ cp->Nx = 0;
+ cp->Arp = NULL;
+ } // endfor cp
+
+ Fpos= -1;
+ Spos = 0;
+ NextSame = false;
+ SameRow = 0;
+ return false;
+ } // endif use
+
+ /*********************************************************************/
+ /* OpenDB: initialize the JSON file processing. */
+ /*********************************************************************/
+ if (MakeDocument(g) != RC_OK)
+ return true;
+
+ if (Mode == MODE_INSERT)
+ switch (Jmode) {
+ case MODE_OBJECT: Row = new(g) JOBJECT; break;
+ case MODE_ARRAY: Row = new(g) JARRAY; break;
+ case MODE_VALUE: Row = new(g) JVALUE; break;
+ default:
+ sprintf(g->Message, "Invalid Jmode %d", Jmode);
+ return true;
+ } // endswitch Jmode
+
+ Use = USE_OPEN;
+ return false;
+ } // end of OpenDB
+
+/***********************************************************************/
+/* ReadDB: Data Base read routine for JSON access method. */
+/***********************************************************************/
+int TDBJSON::ReadDB(PGLOBAL g)
+ {
+ int rc;
+
+ N++;
+
+ if (NextSame) {
+ SameRow++;
+ rc = RC_OK;
+ } else if (++Fpos < (signed)Doc->size()) {
+ Row = Doc->GetValue(Fpos);
+
+ if (Row->GetType() == TYPE_JVAL)
+ Row = ((PJVAL)Row)->GetJson();
+
+ SameRow = 0;
+ rc = RC_OK;
+ } else
+ rc = RC_EF;
+
+ return rc;
+ } // end of ReadDB
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for JSON access method. */
+/***********************************************************************/
+int TDBJSON::WriteDB(PGLOBAL g)
+ {
+ if (Jmode == MODE_OBJECT) {
+ PJVAL vp = new(g) JVALUE(Row);
+
+ if (Mode == MODE_INSERT) {
+ Doc->AddValue(g, vp);
+ Row = new(g) JOBJECT;
+ } else if (Doc->SetValue(g, vp, Fpos))
+ return RC_FX;
+
+ } else if (Jmode == MODE_ARRAY) {
+ PJVAL vp = new(g) JVALUE(Row);
+
+ if (Mode == MODE_INSERT) {
+ Doc->AddValue(g, vp);
+ Row = new(g) JARRAY;
+ } else if (Doc->SetValue(g, vp, Fpos))
+ return RC_FX;
+
+ } else { // if (Jmode == MODE_VALUE)
+ if (Mode == MODE_INSERT)
+ Doc->AddValue(g, (PJVAL)Row);
+ else if (Doc->SetValue(g, (PJVAL)Row, Fpos))
+ return RC_FX;
+
+ } // endif Jmode
+
+ Changed = true;
+ return RC_OK;
+ } // end of WriteDB
+
+/***********************************************************************/
+/* Data Base delete line routine for JSON access method. */
+/***********************************************************************/
+int TDBJSON::DeleteDB(PGLOBAL g, int irc)
+ {
+ if (irc == RC_OK) {
+ // Deleted current row
+ if (Doc->DeleteValue(Fpos)) {
+ sprintf(g->Message, "Value %d does not exist", Fpos + 1);
+ return RC_FX;
+ } // endif Delete
+
+ Changed = true;
+ } else if (irc == RC_FX)
+ // Delete all
+ for (int i = 0; i < Doc->size(); i++) {
+ Doc->DeleteValue(i);
+ Changed = true;
+ } // endfor i
+
+ return RC_OK;
+ } // end of DeleteDB
+
+/***********************************************************************/
+/* Data Base close routine for JSON access methods. */
+/***********************************************************************/
+void TDBJSON::CloseDB(PGLOBAL g)
+ {
+ if (!Changed)
+ return;
+
+ // Save the modified document
+ char filename[_MAX_PATH];
+ PSZ msg;
+ FILE *fop;
+
+ Doc->InitArray(g);
+
+ // We used the file name relative to recorded datapath
+ PlugSetPath(filename, ((PJDEF)To_Def)->Fn, GetPath());
+
+ if (!(fop = fopen(filename, "wb"))) {
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "w", (int)errno, filename);
+ strcat(strcat(g->Message, ": "), strerror(errno));
+ } else {
+ // Serialize the modified table
+ if ((msg = Serialize(g, Top, fop, Pretty)))
+ printf(msg);
+
+ fclose(fop);
+ } // endif fop
+
+ } // end of CloseDB
+
+/* -------------------------- End of json --------------------------- */