2015-01-19 18:55:25 +01:00
|
|
|
/************* tabjson C++ Program Source Code File (.CPP) *************/
|
2019-01-25 13:02:40 +01:00
|
|
|
/* PROGRAM NAME: tabjson Version 1.7 */
|
|
|
|
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2019 */
|
2015-01-19 18:55:25 +01:00
|
|
|
/* 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 "maputil.h"
|
|
|
|
#include "filamtxt.h"
|
|
|
|
#include "tabdos.h"
|
|
|
|
//#include "resource.h" // for IDS_COLUMNS
|
|
|
|
#include "tabjson.h"
|
|
|
|
#include "filamap.h"
|
2016-12-02 23:03:43 +01:00
|
|
|
#if defined(GZ_SUPPORT)
|
|
|
|
#include "filamgz.h"
|
|
|
|
#endif // GZ_SUPPORT
|
2016-12-12 10:57:19 +01:00
|
|
|
#if defined(ZIP_SUPPORT)
|
|
|
|
#include "filamzip.h"
|
|
|
|
#endif // ZIP_SUPPORT
|
2017-10-15 16:13:23 +02:00
|
|
|
#if defined(JAVA_SUPPORT)
|
2017-07-02 22:29:31 +02:00
|
|
|
#include "jmgfam.h"
|
2017-10-15 16:13:23 +02:00
|
|
|
#endif // JAVA_SUPPORT
|
2017-08-05 18:08:51 +02:00
|
|
|
#if defined(CMGO_SUPPORT)
|
2017-07-18 13:16:55 +02:00
|
|
|
#include "cmgfam.h"
|
2017-08-06 19:56:57 +02:00
|
|
|
#endif // CMGO_SUPPORT
|
2015-01-19 18:55:25 +01:00
|
|
|
#include "tabmul.h"
|
|
|
|
#include "checklvl.h"
|
2015-03-18 13:30:14 +01:00
|
|
|
#include "resource.h"
|
2015-04-17 20:05:41 +02:00
|
|
|
#include "mycat.h" // for FNC_COL
|
2015-03-18 13:30:14 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* This should be an option. */
|
|
|
|
/***********************************************************************/
|
|
|
|
#define MAXCOL 200 /* Default max column nb in result */
|
|
|
|
#define TYPE_UNKNOWN 12 /* Must be greater than other types */
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2017-05-23 19:35:50 +02:00
|
|
|
/* External functions. */
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
USETEMP UseTemp(void);
|
2017-07-18 13:16:55 +02:00
|
|
|
char *GetJsonNull(void);
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2018-02-12 16:26:12 +01:00
|
|
|
//typedef struct _jncol {
|
|
|
|
// struct _jncol *Next;
|
|
|
|
// char *Name;
|
|
|
|
// char *Fmt;
|
|
|
|
// int Type;
|
|
|
|
// int Len;
|
|
|
|
// int Scale;
|
|
|
|
// bool Cbn;
|
|
|
|
// bool Found;
|
|
|
|
//} JCOL, *PJCL;
|
2015-03-18 13:30:14 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* JSONColumns: construct the result blocks containing the description */
|
|
|
|
/* of all the columns of a table contained inside a JSON file. */
|
|
|
|
/***********************************************************************/
|
2017-07-02 22:29:31 +02:00
|
|
|
PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
|
2015-03-18 13:30:14 +01:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
|
2015-03-18 13:30:14 +01:00
|
|
|
TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
|
2019-08-17 16:58:58 +02:00
|
|
|
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
|
2015-03-18 13:30:14 +01:00
|
|
|
FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
|
|
|
|
static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
|
2019-08-17 16:58:58 +02:00
|
|
|
int i, n = 0;
|
2015-03-18 13:30:14 +01:00
|
|
|
int ncol = sizeof(buftyp) / sizeof(int);
|
2019-08-17 16:58:58 +02:00
|
|
|
PJCL jcp;
|
|
|
|
JSONDISC *pjdc = NULL;
|
2015-03-18 13:30:14 +01:00
|
|
|
PQRYRES qrp;
|
|
|
|
PCOLRES crp;
|
|
|
|
|
|
|
|
if (info) {
|
|
|
|
length[0] = 128;
|
|
|
|
length[7] = 256;
|
|
|
|
goto skipit;
|
|
|
|
} // endif info
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
|
|
|
|
strcpy(g->Message, "Cannot find column definition for multiple table");
|
|
|
|
return NULL;
|
|
|
|
} // endif Multiple
|
2016-12-14 14:20:23 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
pjdc = new(g) JSONDISC(g, length);
|
2018-02-12 16:26:12 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (!(n = pjdc->GetColumns(g, db, dsn, topt)))
|
|
|
|
return NULL;
|
2018-02-12 16:26:12 +01:00
|
|
|
|
|
|
|
skipit:
|
|
|
|
if (trace(1))
|
|
|
|
htrc("JSONColumns: n=%d len=%d\n", n, length[0]);
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Allocate the structures used to refer to the result set. */
|
|
|
|
/*********************************************************************/
|
|
|
|
qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
|
|
|
|
buftyp, fldtyp, length, false, false);
|
|
|
|
|
|
|
|
crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
|
2019-08-17 16:58:58 +02:00
|
|
|
crp->Name = PlugDup(g, "Nullable");
|
|
|
|
crp->Next->Name = PlugDup(g, "Jpath");
|
2018-02-12 16:26:12 +01:00
|
|
|
|
|
|
|
if (info || !qrp)
|
|
|
|
return qrp;
|
|
|
|
|
|
|
|
qrp->Nblin = n;
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Now get the results into blocks. */
|
|
|
|
/*********************************************************************/
|
|
|
|
for (i = 0, jcp = pjdc->fjcp; jcp; i++, jcp = jcp->Next) {
|
2019-08-17 16:58:58 +02:00
|
|
|
if (jcp->Type == TYPE_UNKNOWN)
|
|
|
|
jcp->Type = TYPE_STRING; // Void column
|
2018-02-12 16:26:12 +01:00
|
|
|
|
|
|
|
crp = qrp->Colresp; // Column Name
|
|
|
|
crp->Kdata->SetValue(jcp->Name, i);
|
|
|
|
crp = crp->Next; // Data Type
|
|
|
|
crp->Kdata->SetValue(jcp->Type, i);
|
|
|
|
crp = crp->Next; // Type Name
|
|
|
|
crp->Kdata->SetValue(GetTypeName(jcp->Type), i);
|
|
|
|
crp = crp->Next; // Precision
|
|
|
|
crp->Kdata->SetValue(jcp->Len, i);
|
|
|
|
crp = crp->Next; // Length
|
|
|
|
crp->Kdata->SetValue(jcp->Len, i);
|
|
|
|
crp = crp->Next; // Scale (precision)
|
|
|
|
crp->Kdata->SetValue(jcp->Scale, i);
|
|
|
|
crp = crp->Next; // Nullable
|
|
|
|
crp->Kdata->SetValue(jcp->Cbn ? 1 : 0, i);
|
|
|
|
crp = crp->Next; // Field format
|
|
|
|
|
|
|
|
if (crp->Kdata)
|
|
|
|
crp->Kdata->SetValue(jcp->Fmt, i);
|
|
|
|
|
|
|
|
} // endfor i
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Return the result pointer. */
|
2015-03-18 13:30:14 +01:00
|
|
|
/*********************************************************************/
|
2018-02-12 16:26:12 +01:00
|
|
|
return qrp;
|
|
|
|
} // end of JSONColumns
|
|
|
|
|
|
|
|
/* -------------------------- Class JSONDISC ------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Class used to get the columns of a JSON table. */
|
|
|
|
/***********************************************************************/
|
2018-02-25 14:31:28 +01:00
|
|
|
JSONDISC::JSONDISC(PGLOBAL g, uint *lg)
|
2018-02-12 16:26:12 +01:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
length = lg;
|
|
|
|
jcp = fjcp = pjcp = NULL;
|
|
|
|
tjnp = NULL;
|
|
|
|
jpp = NULL;
|
|
|
|
tjsp = NULL;
|
|
|
|
jsp = NULL;
|
|
|
|
row = NULL;
|
|
|
|
sep = NULL;
|
|
|
|
i = n = bf = ncol = lvl = 0;
|
|
|
|
all = false;
|
|
|
|
} // end of JSONDISC constructor
|
2018-02-12 16:26:12 +01:00
|
|
|
|
|
|
|
int JSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
|
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
char filename[_MAX_PATH];
|
|
|
|
bool mgo = (GetTypeID(topt->type) == TAB_MONGO);
|
|
|
|
PCSZ level = GetStringTableOption(g, topt, "Level", NULL);
|
|
|
|
|
2020-10-01 19:18:26 +02:00
|
|
|
if (level = GetStringTableOption(g, topt, "Depth", level)) {
|
|
|
|
lvl = atoi(level);
|
2019-08-17 16:58:58 +02:00
|
|
|
lvl = (lvl > 16) ? 16 : lvl;
|
|
|
|
} else
|
|
|
|
lvl = 0;
|
|
|
|
|
|
|
|
sep = GetStringTableOption(g, topt, "Separator", ".");
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Open the input file. */
|
|
|
|
/*********************************************************************/
|
|
|
|
tdp = new(g) JSONDEF;
|
2016-12-12 10:57:19 +01:00
|
|
|
#if defined(ZIP_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
|
|
|
|
tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
|
2016-12-12 10:57:19 +01:00
|
|
|
#endif // ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);
|
|
|
|
|
|
|
|
if (!(tdp->Database = SetPath(g, db)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
tdp->Objname = GetStringTableOption(g, topt, "Object", NULL);
|
|
|
|
tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0;
|
|
|
|
tdp->Pretty = GetIntegerTableOption(g, topt, "Pretty", 2);
|
|
|
|
tdp->Xcol = GetStringTableOption(g, topt, "Expand", NULL);
|
|
|
|
tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false);
|
|
|
|
tdp->Uri = (dsn && *dsn ? dsn : NULL);
|
|
|
|
|
|
|
|
if (!tdp->Fn && !tdp->Uri) {
|
|
|
|
strcpy(g->Message, MSG(MISSING_FNAME));
|
|
|
|
return 0;
|
|
|
|
} // endif Fn
|
|
|
|
|
|
|
|
if (tdp->Fn) {
|
|
|
|
// We used the file name relative to recorded datapath
|
|
|
|
PlugSetPath(filename, tdp->Fn, tdp->GetPath());
|
|
|
|
tdp->Fn = PlugDup(g, filename);
|
|
|
|
} // endif Fn
|
|
|
|
|
|
|
|
if (trace(1))
|
|
|
|
htrc("File %s objname=%s pretty=%d lvl=%d\n",
|
|
|
|
tdp->Fn, tdp->Objname, tdp->Pretty, lvl);
|
|
|
|
|
|
|
|
if (tdp->Uri) {
|
2017-10-15 16:13:23 +02:00
|
|
|
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tdp->Collname = GetStringTableOption(g, topt, "Name", NULL);
|
|
|
|
tdp->Collname = GetStringTableOption(g, topt, "Tabname", tdp->Collname);
|
|
|
|
tdp->Schema = GetStringTableOption(g, topt, "Dbname", "test");
|
|
|
|
tdp->Options = (PSZ)GetStringTableOption(g, topt, "Colist", "all");
|
|
|
|
tdp->Pipe = GetBooleanTableOption(g, topt, "Pipeline", false);
|
|
|
|
tdp->Driver = (PSZ)GetStringTableOption(g, topt, "Driver", NULL);
|
|
|
|
tdp->Version = GetIntegerTableOption(g, topt, "Version", 3);
|
|
|
|
tdp->Wrapname = (PSZ)GetStringTableOption(g, topt, "Wrapper",
|
|
|
|
(tdp->Version == 2) ? "Mongo2Interface" : "Mongo3Interface");
|
|
|
|
tdp->Pretty = 0;
|
2017-08-05 18:08:51 +02:00
|
|
|
#else // !MONGO_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
|
|
return 0;
|
2017-08-05 18:08:51 +02:00
|
|
|
#endif // !MONGO_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif Uri
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (tdp->Pretty == 2) {
|
|
|
|
if (tdp->Zipped) {
|
2016-12-23 16:58:32 +01:00
|
|
|
#if defined(ZIP_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tjsp = new(g) TDBJSON(tdp, new(g) UNZFAM(tdp));
|
2016-12-23 16:58:32 +01:00
|
|
|
#else // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
|
|
return 0;
|
2016-12-23 16:58:32 +01:00
|
|
|
#endif // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} else
|
|
|
|
tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp));
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (tjsp->MakeDocument(g))
|
|
|
|
return 0;
|
2015-03-18 13:30:14 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
jsp = (tjsp->GetDoc()) ? tjsp->GetDoc()->GetValue(0) : NULL;
|
|
|
|
} else {
|
|
|
|
if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))
|
|
|
|
if (!mgo) {
|
|
|
|
sprintf(g->Message, "LRECL must be specified for pretty=%d", tdp->Pretty);
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
tdp->Lrecl = 8192; // Should be enough
|
2015-03-18 13:30:14 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF);
|
2016-12-12 10:57:19 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (tdp->Zipped) {
|
2016-12-23 16:58:32 +01:00
|
|
|
#if defined(ZIP_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tjnp = new(g)TDBJSN(tdp, new(g) UNZFAM(tdp));
|
2016-12-23 16:58:32 +01:00
|
|
|
#else // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
|
|
return NULL;
|
2016-12-23 16:58:32 +01:00
|
|
|
#endif // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} else if (tdp->Uri) {
|
|
|
|
if (tdp->Driver && toupper(*tdp->Driver) == 'C') {
|
2017-08-05 18:08:51 +02:00
|
|
|
#if defined(CMGO_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp));
|
2017-08-05 18:08:51 +02:00
|
|
|
#else
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, "Mongo %s Driver not available", "C");
|
|
|
|
return 0;
|
2017-08-05 18:08:51 +02:00
|
|
|
#endif
|
2019-08-17 16:58:58 +02:00
|
|
|
} else if (tdp->Driver && toupper(*tdp->Driver) == 'J') {
|
2017-10-15 16:13:23 +02:00
|
|
|
#if defined(JAVA_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tjnp = new(g) TDBJSN(tdp, new(g) JMGFAM(tdp));
|
2017-07-02 22:29:31 +02:00
|
|
|
#else
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, "Mongo %s Driver not available", "Java");
|
|
|
|
return 0;
|
2017-08-05 18:08:51 +02:00
|
|
|
#endif
|
2019-08-17 16:58:58 +02:00
|
|
|
} else { // Driver not specified
|
2017-08-05 18:08:51 +02:00
|
|
|
#if defined(CMGO_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp));
|
2017-10-15 16:13:23 +02:00
|
|
|
#elif defined(JAVA_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
tjnp = new(g) TDBJSN(tdp, new(g) JMGFAM(tdp));
|
2017-10-15 16:13:23 +02:00
|
|
|
#else
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
|
|
return 0;
|
2017-07-02 22:29:31 +02:00
|
|
|
#endif
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif Driver
|
|
|
|
|
|
|
|
} else
|
|
|
|
tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp));
|
|
|
|
|
|
|
|
tjnp->SetMode(MODE_READ);
|
|
|
|
|
|
|
|
// Allocate the parse work memory
|
|
|
|
PGLOBAL G = (PGLOBAL)PlugSubAlloc(g, NULL, sizeof(GLOBAL));
|
|
|
|
memset(G, 0, sizeof(GLOBAL));
|
|
|
|
G->Sarea_Size = tdp->Lrecl * 10;
|
|
|
|
G->Sarea = PlugSubAlloc(g, NULL, G->Sarea_Size);
|
|
|
|
PlugSubSet(G->Sarea, G->Sarea_Size);
|
|
|
|
G->jump_level = 0;
|
|
|
|
tjnp->SetG(G);
|
|
|
|
|
|
|
|
if (tjnp->OpenDB(g))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (tjnp->ReadDB(g)) {
|
|
|
|
case RC_EF:
|
|
|
|
strcpy(g->Message, "Void json table");
|
|
|
|
case RC_FX:
|
|
|
|
goto err;
|
|
|
|
default:
|
|
|
|
jsp = tjnp->GetRow();
|
|
|
|
} // endswitch ReadDB
|
|
|
|
|
|
|
|
} // endif pretty
|
|
|
|
|
|
|
|
if (!(row = (jsp) ? jsp->GetObject() : NULL)) {
|
|
|
|
strcpy(g->Message, "Can only retrieve columns from object rows");
|
|
|
|
goto err;
|
|
|
|
} // endif row
|
|
|
|
|
|
|
|
all = GetBooleanTableOption(g, topt, "Fullarray", false);
|
|
|
|
jcol.Name = jcol.Fmt = NULL;
|
|
|
|
jcol.Next = NULL;
|
|
|
|
jcol.Found = true;
|
|
|
|
colname[0] = 0;
|
|
|
|
|
|
|
|
if (!tdp->Uri) {
|
|
|
|
fmt[0] = '$';
|
|
|
|
fmt[1] = '.';
|
|
|
|
bf = 2;
|
|
|
|
} // endif Uri
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Analyse the JSON tree and define columns. */
|
|
|
|
/*********************************************************************/
|
|
|
|
for (i = 1; ; i++) {
|
|
|
|
for (jpp = row->GetFirst(); jpp; jpp = jpp->GetNext()) {
|
|
|
|
strncpy(colname, jpp->GetKey(), 64);
|
|
|
|
fmt[bf] = 0;
|
|
|
|
|
|
|
|
if (Find(g, jpp->GetVal(), colname, MY_MIN(lvl, 0)))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
} // endfor jpp
|
|
|
|
|
|
|
|
// Missing column can be null
|
|
|
|
for (jcp = fjcp; jcp; jcp = jcp->Next) {
|
|
|
|
jcp->Cbn |= !jcp->Found;
|
|
|
|
jcp->Found = false;
|
|
|
|
} // endfor jcp
|
|
|
|
|
|
|
|
if (tdp->Pretty != 2) {
|
|
|
|
// Read next record
|
|
|
|
switch (tjnp->ReadDB(g)) {
|
|
|
|
case RC_EF:
|
|
|
|
jsp = NULL;
|
|
|
|
break;
|
|
|
|
case RC_FX:
|
|
|
|
goto err;
|
|
|
|
default:
|
|
|
|
jsp = tjnp->GetRow();
|
|
|
|
} // endswitch ReadDB
|
|
|
|
|
|
|
|
} else
|
|
|
|
jsp = tjsp->GetDoc()->GetValue(i);
|
|
|
|
|
|
|
|
if (!(row = (jsp) ? jsp->GetObject() : NULL))
|
|
|
|
break;
|
|
|
|
|
|
|
|
} // endfor i
|
|
|
|
|
|
|
|
if (tdp->Pretty != 2)
|
|
|
|
tjnp->CloseDB(g);
|
|
|
|
|
|
|
|
return n;
|
2015-03-18 13:30:14 +01:00
|
|
|
|
2018-02-12 16:26:12 +01:00
|
|
|
err:
|
2019-08-17 16:58:58 +02:00
|
|
|
if (tdp->Pretty != 2)
|
|
|
|
tjnp->CloseDB(g);
|
2015-03-18 13:30:14 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
return 0;
|
|
|
|
} // end of GetColumns
|
2015-03-18 13:30:14 +01:00
|
|
|
|
2019-01-01 14:05:54 +01:00
|
|
|
bool JSONDISC::Find(PGLOBAL g, PJVAL jvp, PCSZ key, int j)
|
2018-02-12 16:26:12 +01:00
|
|
|
{
|
2019-11-26 19:22:46 +01:00
|
|
|
char *p, *pc = colname + strlen(colname);
|
|
|
|
int ars;
|
|
|
|
size_t n;
|
|
|
|
PJOB job;
|
|
|
|
PJAR jar;
|
2019-08-17 16:58:58 +02:00
|
|
|
|
|
|
|
if ((valp = jvp ? jvp->GetValue() : NULL)) {
|
|
|
|
jcol.Type = valp->GetType();
|
|
|
|
jcol.Len = valp->GetValLen();
|
|
|
|
jcol.Scale = valp->GetValPrec();
|
|
|
|
jcol.Cbn = valp->IsNull();
|
|
|
|
} else if (!jvp || jvp->IsNull()) {
|
|
|
|
jcol.Type = TYPE_UNKNOWN;
|
|
|
|
jcol.Len = jcol.Scale = 0;
|
|
|
|
jcol.Cbn = true;
|
|
|
|
} else if (j < lvl) {
|
|
|
|
if (!fmt[bf])
|
|
|
|
strcat(fmt, colname);
|
|
|
|
|
|
|
|
p = fmt + strlen(fmt);
|
|
|
|
jsp = jvp->GetJson();
|
|
|
|
|
|
|
|
switch (jsp->GetType()) {
|
|
|
|
case TYPE_JOB:
|
|
|
|
job = (PJOB)jsp;
|
|
|
|
|
|
|
|
for (PJPR jrp = job->GetFirst(); jrp; jrp = jrp->GetNext()) {
|
|
|
|
PCSZ k = jrp->GetKey();
|
|
|
|
|
|
|
|
if (*k != '$') {
|
2019-11-26 19:22:46 +01:00
|
|
|
n = sizeof(fmt) - strlen(fmt) -1;
|
|
|
|
strncat(strncat(fmt, sep, n), k, n - strlen(sep));
|
|
|
|
n = sizeof(colname) - strlen(colname) - 1;
|
|
|
|
strncat(strncat(colname, "_", n), k, n - 1);
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif Key
|
|
|
|
|
|
|
|
if (Find(g, jrp->GetVal(), k, j + 1))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
*p = *pc = 0;
|
|
|
|
} // endfor jrp
|
|
|
|
|
|
|
|
return false;
|
|
|
|
case TYPE_JAR:
|
|
|
|
jar = (PJAR)jsp;
|
|
|
|
|
|
|
|
if (all || (tdp->Xcol && !stricmp(tdp->Xcol, key)))
|
|
|
|
ars = jar->GetSize(false);
|
|
|
|
else
|
|
|
|
ars = MY_MIN(jar->GetSize(false), 1);
|
|
|
|
|
|
|
|
for (int k = 0; k < ars; k++) {
|
2019-11-26 19:22:46 +01:00
|
|
|
n = sizeof(fmt) - (strlen(fmt) + 1);
|
2019-08-17 16:58:58 +02:00
|
|
|
|
2019-11-26 19:22:46 +01:00
|
|
|
if (!tdp->Xcol || stricmp(tdp->Xcol, key)) {
|
|
|
|
sprintf(buf, "%d", k);
|
2019-08-17 16:58:58 +02:00
|
|
|
|
2019-11-26 19:22:46 +01:00
|
|
|
if (tdp->Uri) {
|
|
|
|
strncat(strncat(fmt, sep, n), buf, n - strlen(sep));
|
|
|
|
} else {
|
|
|
|
strncat(strncat(fmt, "[", n), buf, n - 1);
|
|
|
|
strncat(fmt, "]", n - (strlen(buf) + 1));
|
|
|
|
} // endif uri
|
|
|
|
|
|
|
|
if (all) {
|
|
|
|
n = sizeof(colname) - (strlen(colname) + 1);
|
|
|
|
strncat(strncat(colname, "_", n), buf, n - 1);
|
|
|
|
} // endif all
|
|
|
|
|
|
|
|
} else {
|
|
|
|
strncat(fmt, (tdp->Uri ? sep : "[*]"), n);
|
|
|
|
}
|
2019-08-17 16:58:58 +02:00
|
|
|
|
|
|
|
if (Find(g, jar->GetValue(k), "", j))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
*p = *pc = 0;
|
|
|
|
} // endfor k
|
|
|
|
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
sprintf(g->Message, "Logical error after %s", fmt);
|
|
|
|
return true;
|
|
|
|
} // endswitch Type
|
|
|
|
|
|
|
|
} else if (lvl >= 0) {
|
|
|
|
jcol.Type = TYPE_STRING;
|
|
|
|
jcol.Len = 256;
|
|
|
|
jcol.Scale = 0;
|
|
|
|
jcol.Cbn = true;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
AddColumn(g);
|
|
|
|
return false;
|
|
|
|
} // end of Find
|
2015-03-18 13:30:14 +01:00
|
|
|
|
2018-02-12 16:26:12 +01:00
|
|
|
void JSONDISC::AddColumn(PGLOBAL g)
|
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
bool b = fmt[bf] != 0; // True if formatted
|
|
|
|
|
|
|
|
// Check whether this column was already found
|
|
|
|
for (jcp = fjcp; jcp; jcp = jcp->Next)
|
|
|
|
if (!strcmp(colname, jcp->Name))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (jcp) {
|
|
|
|
if (jcp->Type != jcol.Type) {
|
|
|
|
if (jcp->Type == TYPE_UNKNOWN)
|
|
|
|
jcp->Type = jcol.Type;
|
|
|
|
else if (jcol.Type != TYPE_UNKNOWN)
|
|
|
|
jcp->Type = TYPE_STRING;
|
|
|
|
|
|
|
|
} // endif Type
|
|
|
|
|
|
|
|
if (b && (!jcp->Fmt || strlen(jcp->Fmt) < strlen(fmt))) {
|
|
|
|
jcp->Fmt = PlugDup(g, fmt);
|
|
|
|
length[7] = MY_MAX(length[7], strlen(fmt));
|
|
|
|
} // endif fmt
|
|
|
|
|
|
|
|
jcp->Len = MY_MAX(jcp->Len, jcol.Len);
|
|
|
|
jcp->Scale = MY_MAX(jcp->Scale, jcol.Scale);
|
|
|
|
jcp->Cbn |= jcol.Cbn;
|
|
|
|
jcp->Found = true;
|
|
|
|
} else if (jcol.Type != TYPE_UNKNOWN || tdp->Accept) {
|
|
|
|
// New column
|
|
|
|
jcp = (PJCL)PlugSubAlloc(g, NULL, sizeof(JCOL));
|
|
|
|
*jcp = jcol;
|
|
|
|
jcp->Cbn |= (i > 1);
|
|
|
|
jcp->Name = PlugDup(g, colname);
|
|
|
|
length[0] = MY_MAX(length[0], strlen(colname));
|
|
|
|
|
|
|
|
if (b) {
|
|
|
|
jcp->Fmt = PlugDup(g, fmt);
|
|
|
|
length[7] = MY_MAX(length[7], strlen(fmt));
|
|
|
|
} else
|
|
|
|
jcp->Fmt = NULL;
|
|
|
|
|
|
|
|
if (pjcp) {
|
|
|
|
jcp->Next = pjcp->Next;
|
|
|
|
pjcp->Next = jcp;
|
|
|
|
} else
|
|
|
|
fjcp = jcp;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
} // endif jcp
|
|
|
|
|
|
|
|
if (jcp)
|
|
|
|
pjcp = jcp;
|
2019-01-01 14:05:54 +01:00
|
|
|
|
2018-02-12 16:26:12 +01:00
|
|
|
} // end of AddColumn
|
2015-03-18 13:30:14 +01:00
|
|
|
|
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
/* -------------------------- Class JSONDEF -------------------------- */
|
|
|
|
|
|
|
|
JSONDEF::JSONDEF(void)
|
|
|
|
{
|
|
|
|
Jmode = MODE_OBJECT;
|
|
|
|
Objname = NULL;
|
|
|
|
Xcol = NULL;
|
2015-03-18 13:30:14 +01:00
|
|
|
Pretty = 2;
|
2015-01-19 18:55:25 +01:00
|
|
|
Limit = 1;
|
2015-05-26 01:02:33 +02:00
|
|
|
Base = 0;
|
2015-04-17 20:05:41 +02:00
|
|
|
Strict = false;
|
2019-08-17 16:58:58 +02:00
|
|
|
Sep = '.';
|
|
|
|
Uri = NULL;
|
|
|
|
Collname = Options = Filter = NULL;
|
|
|
|
Pipe = false;
|
|
|
|
Driver = NULL;
|
|
|
|
Version = 0;
|
|
|
|
Wrapname = NULL;
|
2015-01-19 18:55:25 +01:00
|
|
|
} // end of JSONDEF constructor
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* DefineAM: define specific AM block values. */
|
|
|
|
/***********************************************************************/
|
2019-01-01 14:05:54 +01:00
|
|
|
bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
2015-01-19 18:55:25 +01:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
Schema = GetStringCatInfo(g, "DBname", Schema);
|
|
|
|
Jmode = (JMODE)GetIntCatInfo("Jmode", MODE_OBJECT);
|
2015-01-19 18:55:25 +01:00
|
|
|
Objname = GetStringCatInfo(g, "Object", NULL);
|
|
|
|
Xcol = GetStringCatInfo(g, "Expand", NULL);
|
|
|
|
Pretty = GetIntCatInfo("Pretty", 2);
|
|
|
|
Limit = GetIntCatInfo("Limit", 10);
|
2015-05-26 01:02:33 +02:00
|
|
|
Base = GetIntCatInfo("Base", 0) ? 1 : 0;
|
2019-08-17 16:58:58 +02:00
|
|
|
Sep = *GetStringCatInfo(g, "Separator", ".");
|
|
|
|
Accept = GetBoolCatInfo("Accept", false);
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
// Don't use url as MONGO uri when called from REST
|
|
|
|
if (stricmp(am, "REST") && (Uri = GetStringCatInfo(g, "Connect", NULL))) {
|
2017-10-15 16:13:23 +02:00
|
|
|
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
Collname = GetStringCatInfo(g, "Name",
|
|
|
|
(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
|
|
|
|
Collname = GetStringCatInfo(g, "Tabname", Collname);
|
|
|
|
Options = GetStringCatInfo(g, "Colist", NULL);
|
|
|
|
Filter = GetStringCatInfo(g, "Filter", NULL);
|
|
|
|
Pipe = GetBoolCatInfo("Pipeline", false);
|
|
|
|
Driver = GetStringCatInfo(g, "Driver", NULL);
|
|
|
|
Version = GetIntCatInfo("Version", 3);
|
|
|
|
Pretty = 0;
|
2017-10-15 16:13:23 +02:00
|
|
|
#if defined(JAVA_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Version == 2)
|
|
|
|
Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo2Interface");
|
|
|
|
else
|
|
|
|
Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo3Interface");
|
2017-10-15 16:13:23 +02:00
|
|
|
#endif // JAVA_SUPPORT
|
2017-08-05 18:08:51 +02:00
|
|
|
#else // !MONGO_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
|
|
return true;
|
2017-08-05 18:08:51 +02:00
|
|
|
#endif // !MONGO_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif Uri
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
return DOSDEF::DefineAM(g, (Uri ? "XMGO" : "DOS"), poff);
|
2015-01-19 18:55:25 +01:00
|
|
|
} // end of DefineAM
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* GetTable: makes a new Table Description Block. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
|
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
if (trace(1))
|
|
|
|
htrc("JSON GetTable Pretty=%d Uri=%s\n", Pretty, SVP(Uri));
|
|
|
|
|
2015-03-18 13:30:14 +01:00
|
|
|
if (Catfunc == FNC_COL)
|
|
|
|
return new(g)TDBJCL(this);
|
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
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));
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Uri) {
|
|
|
|
if (Driver && toupper(*Driver) == 'C') {
|
2017-08-05 18:08:51 +02:00
|
|
|
#if defined(CMGO_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
txfp = new(g) CMGFAM(this);
|
2017-07-02 22:29:31 +02:00
|
|
|
#else
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, "Mongo %s Driver not available", "C");
|
|
|
|
return NULL;
|
2017-08-05 18:08:51 +02:00
|
|
|
#endif
|
2019-08-17 16:58:58 +02:00
|
|
|
} else if (Driver && toupper(*Driver) == 'J') {
|
2017-10-15 16:13:23 +02:00
|
|
|
#if defined(JAVA_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
txfp = new(g) JMGFAM(this);
|
2017-08-05 18:08:51 +02:00
|
|
|
#else
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, "Mongo %s Driver not available", "Java");
|
|
|
|
return NULL;
|
2017-08-05 18:08:51 +02:00
|
|
|
#endif
|
2019-08-17 16:58:58 +02:00
|
|
|
} else { // Driver not specified
|
2017-08-05 18:08:51 +02:00
|
|
|
#if defined(CMGO_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
txfp = new(g) CMGFAM(this);
|
2017-10-15 16:13:23 +02:00
|
|
|
#elif defined(JAVA_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
txfp = new(g) JMGFAM(this);
|
|
|
|
#else // !MONGO_SUPPORT
|
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
|
|
return NULL;
|
2017-10-15 16:13:23 +02:00
|
|
|
#endif // !MONGO_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif Driver
|
2017-10-15 16:13:23 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} else if (Zipped) {
|
2016-12-12 10:57:19 +01:00
|
|
|
#if defined(ZIP_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
|
|
|
|
txfp = new(g) UNZFAM(this);
|
|
|
|
} else if (m == MODE_INSERT) {
|
|
|
|
txfp = new(g) ZIPFAM(this);
|
|
|
|
} else {
|
|
|
|
strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
|
|
|
|
return NULL;
|
|
|
|
} // endif's m
|
2016-12-12 10:57:19 +01:00
|
|
|
#else // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
|
|
return NULL;
|
2016-12-12 10:57:19 +01:00
|
|
|
#endif // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} else if (Compressed) {
|
2016-12-02 23:03:43 +01:00
|
|
|
#if defined(GZ_SUPPORT)
|
2015-01-19 18:55:25 +01:00
|
|
|
if (Compressed == 1)
|
2016-12-02 23:03:43 +01:00
|
|
|
txfp = new(g) GZFAM(this);
|
2015-01-19 18:55:25 +01:00
|
|
|
else
|
|
|
|
txfp = new(g) ZLBFAM(this);
|
2016-12-02 23:03:43 +01:00
|
|
|
#else // !GZ_SUPPORT
|
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
|
2015-01-19 18:55:25 +01:00
|
|
|
return NULL;
|
2016-12-02 23:03:43 +01:00
|
|
|
#endif // !GZ_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} else if (map)
|
2015-01-19 18:55:25 +01:00
|
|
|
txfp = new(g) MAPFAM(this);
|
|
|
|
else
|
|
|
|
txfp = new(g) DOSFAM(this);
|
|
|
|
|
|
|
|
// Txfp must be set for TDBDOS
|
|
|
|
tdbp = new(g) TDBJSN(this, txfp);
|
2015-12-08 16:39:13 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Lrecl) {
|
|
|
|
// Allocate the parse work memory
|
|
|
|
PGLOBAL G = (PGLOBAL)PlugSubAlloc(g, NULL, sizeof(GLOBAL));
|
|
|
|
memset(G, 0, sizeof(GLOBAL));
|
|
|
|
G->Sarea_Size = Lrecl * 10;
|
|
|
|
G->Sarea = PlugSubAlloc(g, NULL, G->Sarea_Size);
|
|
|
|
PlugSubSet(G->Sarea, G->Sarea_Size);
|
|
|
|
G->jump_level = 0;
|
|
|
|
((TDBJSN*)tdbp)->G = G;
|
|
|
|
} else {
|
|
|
|
strcpy(g->Message, "LRECL is not defined");
|
|
|
|
return NULL;
|
|
|
|
} // endif Lrecl
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (Zipped) {
|
2016-12-23 16:58:32 +01:00
|
|
|
#if defined(ZIP_SUPPORT)
|
2019-08-17 16:58:58 +02:00
|
|
|
if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
|
|
|
|
txfp = new(g) UNZFAM(this);
|
|
|
|
} else if (m == MODE_INSERT) {
|
|
|
|
strcpy(g->Message, "INSERT supported only for zipped JSON when pretty=0");
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
|
|
|
|
return NULL;
|
|
|
|
} // endif's m
|
2016-12-23 16:58:32 +01:00
|
|
|
#else // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
|
|
return NULL;
|
2016-12-23 16:58:32 +01:00
|
|
|
#endif // !ZIP_SUPPORT
|
2019-08-17 16:58:58 +02:00
|
|
|
} else
|
|
|
|
txfp = new(g) MAPFAM(this);
|
2016-12-12 10:57:19 +01:00
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
tdbp = new(g) TDBJSON(this, txfp);
|
2019-08-17 16:58:58 +02:00
|
|
|
((TDBJSON*)tdbp)->G = g;
|
2015-01-19 18:55:25 +01:00
|
|
|
} // endif Pretty
|
|
|
|
|
2016-12-14 14:20:23 +01:00
|
|
|
if (Multiple)
|
2015-01-19 18:55:25 +01:00
|
|
|
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)
|
|
|
|
{
|
2020-07-13 16:30:57 +02:00
|
|
|
G = NULL;
|
2015-04-17 20:05:41 +02:00
|
|
|
Top = NULL;
|
2015-01-19 18:55:25 +01:00
|
|
|
Row = NULL;
|
2015-04-17 20:05:41 +02:00
|
|
|
Val = NULL;
|
2015-01-19 18:55:25 +01:00
|
|
|
Colp = NULL;
|
2015-04-17 20:05:41 +02:00
|
|
|
|
|
|
|
if (tdp) {
|
|
|
|
Jmode = tdp->Jmode;
|
|
|
|
Objname = tdp->Objname;
|
|
|
|
Xcol = tdp->Xcol;
|
|
|
|
Limit = tdp->Limit;
|
|
|
|
Pretty = tdp->Pretty;
|
2015-05-17 15:22:42 +02:00
|
|
|
B = tdp->Base ? 1 : 0;
|
2019-08-17 16:58:58 +02:00
|
|
|
Sep = tdp->Sep;
|
2015-04-17 20:05:41 +02:00
|
|
|
Strict = tdp->Strict;
|
|
|
|
} else {
|
|
|
|
Jmode = MODE_OBJECT;
|
|
|
|
Objname = NULL;
|
2019-08-17 16:58:58 +02:00
|
|
|
Xcol = NULL;
|
2015-04-17 20:05:41 +02:00
|
|
|
Limit = 1;
|
|
|
|
Pretty = 0;
|
2015-05-20 11:19:44 +02:00
|
|
|
B = 0;
|
2019-08-17 16:58:58 +02:00
|
|
|
Sep = '.';
|
2015-04-17 20:05:41 +02:00
|
|
|
Strict = false;
|
|
|
|
} // endif tdp
|
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
Fpos = -1;
|
2015-05-17 15:22:42 +02:00
|
|
|
N = M = 0;
|
2015-01-30 10:57:00 +01:00
|
|
|
NextSame = 0;
|
|
|
|
SameRow = 0;
|
|
|
|
Xval = -1;
|
2015-01-19 18:55:25 +01:00
|
|
|
Comma = false;
|
|
|
|
} // end of TDBJSN standard constructor
|
|
|
|
|
|
|
|
TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
|
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
G = NULL;
|
2015-04-17 20:05:41 +02:00
|
|
|
Top = tdbp->Top;
|
2015-01-19 18:55:25 +01:00
|
|
|
Row = tdbp->Row;
|
2015-04-17 20:05:41 +02:00
|
|
|
Val = tdbp->Val;
|
2015-01-19 18:55:25 +01:00
|
|
|
Colp = tdbp->Colp;
|
|
|
|
Jmode = tdbp->Jmode;
|
2015-04-17 20:05:41 +02:00
|
|
|
Objname = tdbp->Objname;
|
2015-01-19 18:55:25 +01:00
|
|
|
Xcol = tdbp->Xcol;
|
|
|
|
Fpos = tdbp->Fpos;
|
|
|
|
N = tdbp->N;
|
2015-05-17 15:22:42 +02:00
|
|
|
M = tdbp->M;
|
2015-01-19 18:55:25 +01:00
|
|
|
Limit = tdbp->Limit;
|
|
|
|
NextSame = tdbp->NextSame;
|
|
|
|
SameRow = tdbp->SameRow;
|
|
|
|
Xval = tdbp->Xval;
|
2015-05-20 11:19:44 +02:00
|
|
|
B = tdbp->B;
|
2019-08-17 16:58:58 +02:00
|
|
|
Sep = tdbp->Sep;
|
2015-01-30 10:57:00 +01:00
|
|
|
Pretty = tdbp->Pretty;
|
|
|
|
Strict = tdbp->Strict;
|
|
|
|
Comma = tdbp->Comma;
|
2015-01-19 18:55:25 +01:00
|
|
|
} // end of TDBJSN copy constructor
|
|
|
|
|
|
|
|
// Used for update
|
2017-02-16 18:01:48 +01:00
|
|
|
PTDB TDBJSN::Clone(PTABS t)
|
2015-01-19 18:55:25 +01:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
G = NULL;
|
2015-01-19 18:55:25 +01:00
|
|
|
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;
|
2017-02-16 18:01:48 +01:00
|
|
|
} // end of Clone
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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.*/
|
|
|
|
/***********************************************************************/
|
2015-05-09 17:30:20 +02:00
|
|
|
PCOL TDBJSN::InsertSpecialColumn(PCOL colp)
|
2015-01-19 18:55:25 +01:00
|
|
|
{
|
|
|
|
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
|
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Find the row in the tree structure. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PJSON TDBJSN::FindRow(PGLOBAL g)
|
|
|
|
{
|
|
|
|
char *p, *objpath;
|
|
|
|
PJSON jsp = Row;
|
|
|
|
PJVAL val = NULL;
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
for (objpath = PlugDup(g, Objname); jsp && objpath; objpath = p) {
|
|
|
|
if ((p = strchr(objpath, Sep)))
|
|
|
|
*p++ = 0;
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (*objpath != '[' && !IsNum(objpath)) { // objpass is a key
|
|
|
|
val = (jsp->GetType() == TYPE_JOB) ?
|
|
|
|
jsp->GetObject()->GetValue(objpath) : NULL;
|
|
|
|
} else {
|
|
|
|
if (*objpath == '[') {
|
|
|
|
if (objpath[strlen(objpath) - 1] == ']')
|
|
|
|
objpath++;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
} // endif [
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
val = (jsp->GetType() == TYPE_JAR) ?
|
|
|
|
jsp->GetArray()->GetValue(atoi(objpath) - B) : NULL;
|
|
|
|
} // endif objpath
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
jsp = (val) ? val->GetJson() : NULL;
|
|
|
|
} // endfor objpath
|
2015-04-17 20:05:41 +02:00
|
|
|
|
|
|
|
return jsp;
|
|
|
|
} // end of FindRow
|
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
2019-08-17 16:58:58 +02:00
|
|
|
/* OpenDB: Data Base open routine for JSN access method. */
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBJSN::OpenDB(PGLOBAL g)
|
|
|
|
{
|
|
|
|
if (Use == USE_OPEN) {
|
|
|
|
/*******************************************************************/
|
|
|
|
/* Table already open replace it at its beginning. */
|
|
|
|
/*******************************************************************/
|
|
|
|
Fpos= -1;
|
2015-01-30 10:57:00 +01:00
|
|
|
NextSame = 0;
|
2015-01-19 18:55:25 +01:00
|
|
|
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
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif Use
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (TDBDOS::OpenDB(g))
|
|
|
|
return true;
|
2017-07-02 22:29:31 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Xcol)
|
|
|
|
To_Filter = NULL; // Imcompatible
|
2017-07-02 22:29:31 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
return false;
|
2017-07-02 22:29:31 +02:00
|
|
|
} // end of OpenDB
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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
|
|
|
|
|
2015-05-27 16:23:38 +02:00
|
|
|
#if defined(__WIN__)
|
2015-01-19 18:55:25 +01:00
|
|
|
#define Ending 2
|
2015-05-27 16:23:38 +02:00
|
|
|
#else // !__WIN__
|
2015-01-19 18:55:25 +01:00
|
|
|
#define Ending 1
|
2015-05-27 16:23:38 +02:00
|
|
|
#endif // !__WIN__
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
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) {
|
2015-01-30 10:57:00 +01:00
|
|
|
SameRow = NextSame;
|
|
|
|
NextSame = 0;
|
2015-05-17 15:22:42 +02:00
|
|
|
M++;
|
2015-01-19 18:55:25 +01:00
|
|
|
return RC_OK;
|
2019-08-17 16:58:58 +02:00
|
|
|
} else if ((rc = TDBDOS::ReadDB(g)) == RC_OK) {
|
|
|
|
if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK))
|
|
|
|
// Deferred reading failed
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
// Recover the memory used for parsing
|
|
|
|
PlugSubSet(G->Sarea, G->Sarea_Size);
|
|
|
|
|
|
|
|
if ((Row = ParseJson(G, To_Line, strlen(To_Line), &Pretty, &Comma))) {
|
|
|
|
Row = FindRow(g);
|
|
|
|
SameRow = 0;
|
|
|
|
Fpos++;
|
|
|
|
M = 1;
|
|
|
|
rc = RC_OK;
|
|
|
|
} else if (Pretty != 1 || strcmp(To_Line, "]")) {
|
|
|
|
strcpy(g->Message, G->Message);
|
|
|
|
rc = RC_FX;
|
|
|
|
} else
|
|
|
|
rc = RC_EF;
|
|
|
|
|
|
|
|
} // endif ReadDB
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
} // end of ReadDB
|
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Make the top tree from the object path. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp)
|
2017-05-23 19:35:50 +02:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Objname) {
|
|
|
|
if (!Val) {
|
|
|
|
// Parse and allocate Objname item(s)
|
|
|
|
char *p;
|
|
|
|
char *objpath = PlugDup(g, Objname);
|
|
|
|
int i;
|
|
|
|
PJOB objp;
|
|
|
|
PJAR arp;
|
|
|
|
PJVAL val = NULL;
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
Top = NULL;
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
for (; objpath; objpath = p) {
|
|
|
|
if ((p = strchr(objpath, Sep)))
|
|
|
|
*p++ = 0;
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (*objpath != '[' && !IsNum(objpath)) {
|
|
|
|
objp = new(g) JOBJECT;
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (!Top)
|
|
|
|
Top = objp;
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (val)
|
|
|
|
val->SetValue(objp);
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
val = new(g) JVALUE;
|
|
|
|
objp->SetValue(g, val, objpath);
|
|
|
|
} else {
|
|
|
|
if (*objpath == '[') {
|
|
|
|
// Old style
|
|
|
|
if (objpath[strlen(objpath) - 1] != ']') {
|
|
|
|
sprintf(g->Message, "Invalid Table path %s", Objname);
|
|
|
|
return RC_FX;
|
|
|
|
} else
|
|
|
|
objpath++;
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif objpath
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
arp = new(g) JARRAY;
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (!Top)
|
|
|
|
Top = arp;
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (val)
|
|
|
|
val->SetValue(arp);
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
val = new(g) JVALUE;
|
|
|
|
i = atoi(objpath) - B;
|
|
|
|
arp->SetValue(g, val, i);
|
|
|
|
arp->InitArray(g);
|
|
|
|
} // endif objpath
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endfor p
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
Val = val;
|
|
|
|
} // endif Val
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
Val->SetValue(jsp);
|
|
|
|
} else
|
|
|
|
Top = jsp;
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
return RC_OK;
|
2017-05-23 19:35:50 +02:00
|
|
|
} // end of MakeTopTree
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* PrepareWriting: Prepare the line for WriteDB. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBJSN::PrepareWriting(PGLOBAL g)
|
|
|
|
{
|
2015-04-17 20:05:41 +02:00
|
|
|
PSZ s;
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
if (MakeTopTree(g, Row))
|
|
|
|
return true;
|
|
|
|
|
2015-12-08 16:39:13 +01:00
|
|
|
if ((s = Serialize(G, Top, NULL, Pretty))) {
|
2015-01-19 18:55:25 +01:00
|
|
|
if (Comma)
|
|
|
|
strcat(s, ",");
|
|
|
|
|
|
|
|
if ((signed)strlen(s) > Lrecl) {
|
2015-04-17 20:05:41 +02:00
|
|
|
strncpy(To_Line, s, Lrecl);
|
|
|
|
sprintf(g->Message, "Line truncated (lrecl=%d)", Lrecl);
|
|
|
|
return PushWarning(g, this);
|
2015-01-19 18:55:25 +01:00
|
|
|
} else
|
|
|
|
strcpy(To_Line, s);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
} else
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} // end of PrepareWriting
|
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
/***********************************************************************/
|
2018-12-01 16:56:55 +01:00
|
|
|
/* WriteDB: Data Base write routine for JSON access method. */
|
2017-05-23 19:35:50 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
int TDBJSN::WriteDB(PGLOBAL g)
|
2015-12-08 16:39:13 +01:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
int rc = TDBDOS::WriteDB(g);
|
2015-12-08 16:39:13 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
PlugSubSet(G->Sarea, G->Sarea_Size);
|
|
|
|
Row->Clear();
|
|
|
|
return rc;
|
2015-12-08 16:39:13 +01:00
|
|
|
} // end of WriteDB
|
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
/* ---------------------------- JSONCOL ------------------------------ */
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2015-01-30 10:57:00 +01:00
|
|
|
/* JSONCOL public constructor. */
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
|
|
|
|
: DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
|
2015-12-08 16:39:13 +01:00
|
|
|
{
|
2015-01-19 18:55:25 +01:00
|
|
|
Tjp = (TDBJSN *)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
|
2019-08-17 16:58:58 +02:00
|
|
|
G = Tjp->G;
|
2015-01-19 18:55:25 +01:00
|
|
|
Jpath = cdp->GetFmt();
|
|
|
|
MulVal = NULL;
|
|
|
|
Nodes = NULL;
|
2015-01-30 10:57:00 +01:00
|
|
|
Nod = 0;
|
2019-08-17 16:58:58 +02:00
|
|
|
Sep = Tjp->Sep;
|
2015-01-30 10:57:00 +01:00
|
|
|
Xnod = -1;
|
2015-01-19 18:55:25 +01:00
|
|
|
Xpd = false;
|
|
|
|
Parsed = false;
|
2015-12-08 16:39:13 +01:00
|
|
|
} // end of JSONCOL constructor
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* JSONCOL constructor used for copying columns. */
|
|
|
|
/* tdbp is the pointer to the new table descriptor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
JSONCOL::JSONCOL(JSONCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
|
2015-12-08 16:39:13 +01:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
G = col1->G;
|
2015-01-19 18:55:25 +01:00
|
|
|
Tjp = col1->Tjp;
|
|
|
|
Jpath = col1->Jpath;
|
|
|
|
MulVal = col1->MulVal;
|
|
|
|
Nodes = col1->Nodes;
|
|
|
|
Nod = col1->Nod;
|
2019-08-17 16:58:58 +02:00
|
|
|
Sep = col1->Sep;
|
|
|
|
Xnod = col1->Xnod;
|
2015-01-19 18:55:25 +01:00
|
|
|
Xpd = col1->Xpd;
|
|
|
|
Parsed = col1->Parsed;
|
2015-12-08 16:39:13 +01:00
|
|
|
} // end of JSONCOL copy constructor
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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;
|
2019-08-17 16:58:58 +02:00
|
|
|
G = Tjp->G;
|
2015-01-19 18:55:25 +01:00
|
|
|
return false;
|
|
|
|
} // end of SetBuffer
|
|
|
|
|
|
|
|
/***********************************************************************/
|
2015-01-30 10:57:00 +01:00
|
|
|
/* Check whether this object is expanded. */
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
|
|
|
|
{
|
2015-01-30 10:57:00 +01:00
|
|
|
if ((Tjp->Xcol && nm && !strcmp(nm, Tjp->Xcol) &&
|
|
|
|
(Tjp->Xval < 0 || Tjp->Xval == i)) || Xpd) {
|
2015-01-19 18:55:25 +01:00
|
|
|
Xpd = true; // Expandable object
|
2015-02-11 21:39:41 +01:00
|
|
|
Nodes[i].Op = OP_EXP;
|
2015-01-19 18:55:25 +01:00
|
|
|
} else if (b) {
|
2015-01-30 10:57:00 +01:00
|
|
|
strcpy(g->Message, "Cannot expand more than one branch");
|
2015-01-19 18:55:25 +01:00
|
|
|
return true;
|
|
|
|
} // endif Xcol
|
|
|
|
|
|
|
|
return false;
|
|
|
|
} // end of CheckExpand
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Analyse array processing options. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
|
2017-05-23 19:35:50 +02:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
int n;
|
|
|
|
bool dg = true, b = false;
|
|
|
|
PJNODE jnp = &Nodes[i];
|
|
|
|
|
|
|
|
//if (*p == '[') p++; // Old syntax .[ or :[
|
|
|
|
n = (int)strlen(p);
|
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
if (p[n - 1] == ']') {
|
|
|
|
p[--n] = 0;
|
|
|
|
} else if (!IsNum(p)) {
|
|
|
|
// Wrong array specification
|
|
|
|
sprintf(g->Message, "Invalid array specification %s for %s", p, Name);
|
|
|
|
return true;
|
|
|
|
} // endif p
|
|
|
|
|
|
|
|
} else
|
|
|
|
b = true;
|
|
|
|
|
|
|
|
// To check whether a numeric Rank was specified
|
|
|
|
dg = IsNum(p);
|
|
|
|
|
|
|
|
if (!n) {
|
|
|
|
// Default specifications
|
|
|
|
if (CheckExpand(g, i, nm, false))
|
|
|
|
return true;
|
|
|
|
else if (jnp->Op != OP_EXP) {
|
|
|
|
if (b) {
|
|
|
|
// Return 1st value (B is the index base)
|
|
|
|
jnp->Rank = Tjp->B;
|
|
|
|
jnp->Op = OP_EQ;
|
|
|
|
} else if (!Value->IsTypeNum()) {
|
|
|
|
jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING);
|
|
|
|
jnp->Op = OP_CNC;
|
|
|
|
} else
|
|
|
|
jnp->Op = OP_ADD;
|
|
|
|
|
|
|
|
} // endif OP
|
|
|
|
|
|
|
|
} else if (dg) {
|
|
|
|
// Return nth value
|
|
|
|
jnp->Rank = atoi(p) - Tjp->B;
|
|
|
|
jnp->Op = OP_EQ;
|
|
|
|
} else if (n == 1) {
|
|
|
|
// Set the Op value;
|
|
|
|
if (Sep == ':')
|
|
|
|
switch (*p) {
|
|
|
|
case '*': *p = 'x'; break;
|
|
|
|
case 'x':
|
|
|
|
case 'X': *p = '*'; break; // Expand this array
|
|
|
|
default: break;
|
|
|
|
} // endswitch p
|
|
|
|
|
|
|
|
switch (*p) {
|
|
|
|
case '+': jnp->Op = OP_ADD; break;
|
|
|
|
case 'x': jnp->Op = OP_MULT; break;
|
|
|
|
case '>': jnp->Op = OP_MAX; break;
|
|
|
|
case '<': jnp->Op = OP_MIN; break;
|
|
|
|
case '!': jnp->Op = OP_SEP; break; // Average
|
|
|
|
case '#': jnp->Op = OP_NUM; break;
|
|
|
|
case '*': // Expand this array
|
|
|
|
if (!Tjp->Xcol && nm) {
|
|
|
|
Xpd = true;
|
|
|
|
jnp->Op = OP_EXP;
|
|
|
|
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
|
|
|
|
|
|
|
|
// For calculated arrays, a local Value must be used
|
|
|
|
switch (jnp->Op) {
|
|
|
|
case OP_NUM:
|
|
|
|
jnp->Valp = AllocateValue(g, TYPE_INT);
|
|
|
|
break;
|
|
|
|
case OP_ADD:
|
|
|
|
case OP_MULT:
|
|
|
|
case OP_SEP:
|
|
|
|
if (!IsTypeChar(Buf_Type))
|
|
|
|
jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
|
|
|
|
else
|
|
|
|
jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case OP_MIN:
|
|
|
|
case OP_MAX:
|
|
|
|
jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
|
|
|
|
break;
|
|
|
|
case OP_CNC:
|
|
|
|
if (IsTypeChar(Buf_Type))
|
|
|
|
jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
|
|
|
|
else
|
|
|
|
jnp->Valp = AllocateValue(g, TYPE_STRING, 512);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // endswitch Op
|
|
|
|
|
|
|
|
if (jnp->Valp)
|
|
|
|
MulVal = AllocateValue(g, jnp->Valp);
|
|
|
|
|
|
|
|
return false;
|
2017-05-23 19:35:50 +02:00
|
|
|
} // end of SetArrayOptions
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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)
|
2017-05-23 19:35:50 +02:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
char *p, *p1 = NULL, *p2 = NULL, *pbuf = NULL;
|
|
|
|
int i;
|
|
|
|
bool a, mul = false;
|
|
|
|
|
|
|
|
if (Parsed)
|
|
|
|
return false; // Already done
|
|
|
|
else if (InitValue(g))
|
|
|
|
return true;
|
|
|
|
else if (!Jpath)
|
|
|
|
Jpath = Name;
|
|
|
|
|
|
|
|
if (To_Tdb->GetOrig()) {
|
|
|
|
// This is an updated column, get nodes from origin
|
|
|
|
for (PJCOL colp = (PJCOL)Tjp->GetColumns(); colp;
|
|
|
|
colp = (PJCOL)colp->GetNext())
|
|
|
|
if (!stricmp(Name, colp->GetName())) {
|
|
|
|
Nod = colp->Nod;
|
|
|
|
Nodes = colp->Nodes;
|
|
|
|
Xpd = colp->Xpd;
|
|
|
|
goto fin;
|
|
|
|
} // endif Name
|
|
|
|
|
|
|
|
sprintf(g->Message, "Cannot parse updated column %s", Name);
|
|
|
|
return true;
|
|
|
|
} // endif To_Orig
|
|
|
|
|
|
|
|
pbuf = PlugDup(g, Jpath);
|
|
|
|
if (*pbuf == '$') pbuf++;
|
|
|
|
if (*pbuf == Sep) pbuf++;
|
|
|
|
if (*pbuf == '[') p1 = pbuf++;
|
|
|
|
|
|
|
|
// Estimate the required number of nodes
|
|
|
|
for (i = 0, p = pbuf; (p = NextChr(p, Sep)); 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; p && i < Nod; i++, p = (p2 ? p2 : NULL)) {
|
|
|
|
a = (p1 != NULL);
|
|
|
|
p1 = strchr(p, '[');
|
|
|
|
p2 = strchr(p, Sep);
|
|
|
|
|
|
|
|
if (!p2)
|
|
|
|
p2 = p1;
|
|
|
|
else if (p1) {
|
|
|
|
if (p1 < p2)
|
|
|
|
p2 = p1;
|
|
|
|
else if (p1 == p2 + 1)
|
|
|
|
*p2++ = 0; // Old syntax .[ or :[
|
|
|
|
else
|
|
|
|
p1 = NULL;
|
|
|
|
|
|
|
|
} // endif p1
|
|
|
|
|
|
|
|
if (p2)
|
|
|
|
*p2++ = 0;
|
|
|
|
|
|
|
|
// Jpath must be explicit
|
|
|
|
if (a || *p == 0 || *p == '[' || IsNum(p)) {
|
|
|
|
// Analyse intermediate array processing
|
|
|
|
if (SetArrayOptions(g, p, i, Nodes[i - 1].Key))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} else if (*p == '*') {
|
|
|
|
// Return JSON
|
|
|
|
Nodes[i].Op = OP_XX;
|
|
|
|
} else {
|
|
|
|
Nodes[i].Key = p;
|
|
|
|
Nodes[i].Op = OP_EXIST;
|
|
|
|
} // endif's
|
|
|
|
|
|
|
|
} // endfor i, p
|
|
|
|
|
|
|
|
Nod = i;
|
2017-05-23 19:35:50 +02:00
|
|
|
|
|
|
|
fin:
|
2019-08-17 16:58:58 +02:00
|
|
|
MulVal = AllocateValue(g, Value);
|
|
|
|
Parsed = true;
|
|
|
|
return false;
|
2017-05-23 19:35:50 +02:00
|
|
|
} // end of ParseJpath
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Get Jpath converted to Mongo path. */
|
|
|
|
/***********************************************************************/
|
2017-07-02 22:29:31 +02:00
|
|
|
PSZ JSONCOL::GetJpath(PGLOBAL g, bool proj)
|
2017-05-23 19:35:50 +02:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Jpath) {
|
|
|
|
char *p1, *p2, *mgopath;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (strcmp(Jpath, "*")) {
|
|
|
|
p1 = Jpath;
|
|
|
|
if (*p1 == '$') p1++;
|
|
|
|
if (*p1 == '.') p1++;
|
|
|
|
mgopath = PlugDup(g, p1);
|
|
|
|
} else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (p1 = p2 = mgopath; *p1; p1++)
|
|
|
|
if (i) { // Inside []
|
|
|
|
if (isdigit(*p1)) {
|
|
|
|
if (!proj)
|
|
|
|
*p2++ = *p1;
|
|
|
|
|
|
|
|
} else if (*p1 == ']' && i == 1) {
|
|
|
|
if (proj && p1[1] == '.')
|
|
|
|
p1++;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
} else if (*p1 == '.' && i == 2) {
|
|
|
|
if (!proj)
|
|
|
|
*p2++ = '.';
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
} else if (!proj)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
} else switch (*p1) {
|
|
|
|
case ':':
|
|
|
|
case '.':
|
|
|
|
if (isdigit(p1[1]))
|
|
|
|
i = 2;
|
|
|
|
|
|
|
|
*p2++ = '.';
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
if (*(p2 - 1) != '.')
|
|
|
|
*p2++ = '.';
|
|
|
|
|
|
|
|
i = 1;
|
|
|
|
break;
|
|
|
|
case '*':
|
|
|
|
if (*(p2 - 1) == '.' && !*(p1 + 1)) {
|
|
|
|
p2--; // Suppress last :*
|
|
|
|
break;
|
|
|
|
} // endif p2
|
|
|
|
|
|
|
|
default:
|
|
|
|
*p2++ = *p1;
|
|
|
|
break;
|
|
|
|
} // endswitch p1;
|
|
|
|
|
|
|
|
*p2 = 0;
|
|
|
|
return mgopath;
|
|
|
|
} else
|
|
|
|
return NULL;
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
} // end of GetJpath
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-02-11 21:39:41 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeJson: Serialize the json item and set value to it. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PVAL JSONCOL::MakeJson(PGLOBAL g, PJSON jsp)
|
2017-05-23 19:35:50 +02:00
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Value->IsTypeNum()) {
|
2015-02-11 21:39:41 +01:00
|
|
|
strcpy(g->Message, "Cannot make Json for a numeric column");
|
|
|
|
Value->Reset();
|
2020-10-05 12:29:51 +02:00
|
|
|
} else if (Value->GetType() == TYPE_BIN) {
|
|
|
|
if (Value->GetClen() >= sizeof(BSON)) {
|
|
|
|
ULONG len = Tjp->Lrecl ? Tjp->Lrecl : 500;
|
|
|
|
PBSON bsp = JbinAlloc(g, NULL, len, jsp);
|
|
|
|
|
|
|
|
strcat(bsp->Msg, " column");
|
|
|
|
((BINVAL*)Value)->SetBinValue(bsp, sizeof(BSON));
|
|
|
|
} else {
|
|
|
|
strcpy(g->Message, "Column size too small");
|
|
|
|
Value->SetValue_char(NULL, 0);
|
|
|
|
} // endif Clen
|
|
|
|
} else
|
2015-02-11 21:39:41 +01:00
|
|
|
Value->SetValue_psz(Serialize(g, jsp, NULL, 0));
|
|
|
|
|
|
|
|
return Value;
|
|
|
|
} // end of MakeJson
|
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* SetValue: Set a value from a JVALUE contains. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
|
|
|
|
{
|
|
|
|
if (val) {
|
2019-08-17 16:58:58 +02:00
|
|
|
vp->SetNull(false);
|
2017-07-19 23:30:40 +02:00
|
|
|
|
2015-01-30 10:57:00 +01:00
|
|
|
switch (val->GetValType()) {
|
|
|
|
case TYPE_STRG:
|
|
|
|
case TYPE_INTG:
|
2019-08-17 16:58:58 +02:00
|
|
|
case TYPE_BINT:
|
|
|
|
case TYPE_DBL:
|
|
|
|
case TYPE_DTM:
|
|
|
|
vp->SetValue_pval(val->GetValue());
|
2015-01-30 10:57:00 +01:00
|
|
|
break;
|
|
|
|
case TYPE_BOOL:
|
|
|
|
if (vp->IsTypeNum())
|
|
|
|
vp->SetValue(val->GetInteger() ? 1 : 0);
|
|
|
|
else
|
|
|
|
vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));
|
2019-08-17 16:58:58 +02:00
|
|
|
|
2015-01-30 10:57:00 +01:00
|
|
|
break;
|
|
|
|
case TYPE_JAR:
|
|
|
|
SetJsonValue(g, vp, val->GetArray()->GetValue(0), n);
|
|
|
|
break;
|
|
|
|
case TYPE_JOB:
|
|
|
|
// if (!vp->IsTypeNum() || !Strict) {
|
2015-02-24 23:18:04 +01:00
|
|
|
vp->SetValue_psz(val->GetObject()->GetText(g, NULL));
|
2015-01-19 18:55:25 +01:00
|
|
|
break;
|
2015-01-30 10:57:00 +01:00
|
|
|
// } // endif Type
|
2019-08-17 16:58:58 +02:00
|
|
|
|
2015-01-30 10:57:00 +01:00
|
|
|
default:
|
2019-08-17 16:58:58 +02:00
|
|
|
vp->Reset();
|
|
|
|
vp->SetNull(true);
|
|
|
|
} // endswitch Type
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} else {
|
|
|
|
vp->Reset();
|
|
|
|
vp->SetNull(true);
|
|
|
|
} // endif val
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
} // end of SetJsonValue
|
|
|
|
|
2015-01-31 15:05:43 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* ReadColumn: */
|
|
|
|
/***********************************************************************/
|
|
|
|
void JSONCOL::ReadColumn(PGLOBAL g)
|
|
|
|
{
|
|
|
|
if (!Tjp->SameRow || Xnod >= Tjp->SameRow)
|
|
|
|
Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0));
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Xpd && Value->IsNull() && !((PJDEF)Tjp->To_Def)->Accept)
|
|
|
|
throw("Null expandable JSON value");
|
2018-02-12 16:26:12 +01:00
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
// Set null when applicable
|
2017-05-23 19:35:50 +02:00
|
|
|
if (!Nullable)
|
|
|
|
Value->SetNull(false);
|
2015-04-17 20:05:41 +02:00
|
|
|
|
2015-01-31 15:05:43 +01:00
|
|
|
} // end of ReadColumn
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* GetColumnValue: */
|
|
|
|
/***********************************************************************/
|
|
|
|
PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i)
|
|
|
|
{
|
|
|
|
int n = Nod - 1;
|
|
|
|
bool expd = false;
|
|
|
|
PJAR arp;
|
|
|
|
PJVAL val = NULL;
|
|
|
|
|
|
|
|
for (; i < Nod && row; i++) {
|
|
|
|
if (Nodes[i].Op == OP_NUM) {
|
|
|
|
Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1);
|
|
|
|
return(Value);
|
2015-02-11 21:39:41 +01:00
|
|
|
} else if (Nodes[i].Op == OP_XX) {
|
2015-12-08 16:39:13 +01:00
|
|
|
return MakeJson(G, row);
|
2015-01-31 15:05:43 +01:00
|
|
|
} else switch (row->GetType()) {
|
|
|
|
case TYPE_JOB:
|
|
|
|
if (!Nodes[i].Key) {
|
2015-12-04 22:38:16 +01:00
|
|
|
// Expected Array was not there, wrap the value
|
2015-01-31 15:05:43 +01:00
|
|
|
if (i < Nod-1)
|
|
|
|
continue;
|
|
|
|
else
|
2015-12-08 16:39:13 +01:00
|
|
|
val = new(G) JVALUE(row);
|
2015-01-31 15:05:43 +01:00
|
|
|
|
|
|
|
} else
|
|
|
|
val = ((PJOB)row)->GetValue(Nodes[i].Key);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case TYPE_JAR:
|
|
|
|
arp = (PJAR)row;
|
|
|
|
|
|
|
|
if (!Nodes[i].Key) {
|
2015-05-17 15:22:42 +02:00
|
|
|
if (Nodes[i].Op == OP_EQ)
|
|
|
|
val = arp->GetValue(Nodes[i].Rank);
|
|
|
|
else if (Nodes[i].Op == OP_EXP)
|
|
|
|
return ExpandArray(g, arp, i);
|
|
|
|
else
|
|
|
|
return CalculateArray(g, arp, i);
|
2015-01-31 15:05:43 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} else {
|
|
|
|
// Unexpected array, unwrap it as [0]
|
|
|
|
val = arp->GetValue(0);
|
|
|
|
i--;
|
|
|
|
} // endif's
|
2015-01-31 15:05:43 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
case TYPE_JVAL:
|
|
|
|
val = (PJVAL)row;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
|
|
|
|
val = NULL;
|
|
|
|
} // endswitch Type
|
|
|
|
|
|
|
|
if (i < Nod-1)
|
|
|
|
row = (val) ? val->GetJson() : NULL;
|
|
|
|
|
|
|
|
} // endfor i
|
|
|
|
|
|
|
|
SetJsonValue(g, Value, val, n);
|
|
|
|
return Value;
|
|
|
|
} // end of GetColumnValue
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ExpandArray: */
|
|
|
|
/***********************************************************************/
|
|
|
|
PVAL JSONCOL::ExpandArray(PGLOBAL g, PJAR arp, int n)
|
|
|
|
{
|
2018-02-12 16:26:12 +01:00
|
|
|
int ars = MY_MIN(Tjp->Limit, arp->size());
|
2015-01-31 15:05:43 +01:00
|
|
|
PJVAL jvp;
|
|
|
|
JVALUE jval;
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (!ars) {
|
|
|
|
Value->Reset();
|
|
|
|
Value->SetNull(true);
|
|
|
|
Tjp->NextSame = 0;
|
|
|
|
return Value;
|
|
|
|
} // endif ars
|
2015-02-11 21:39:41 +01:00
|
|
|
|
2015-05-01 15:59:12 +02:00
|
|
|
if (!(jvp = arp->GetValue((Nodes[n].Rx = Nodes[n].Nx)))) {
|
2015-02-11 21:39:41 +01:00
|
|
|
strcpy(g->Message, "Logical error expanding array");
|
2019-08-17 16:58:58 +02:00
|
|
|
throw 666;
|
|
|
|
} // endif jvp
|
2015-01-31 15:05:43 +01:00
|
|
|
|
|
|
|
if (n < Nod - 1 && jvp->GetJson()) {
|
|
|
|
jval.SetValue(GetColumnValue(g, jvp->GetJson(), n + 1));
|
|
|
|
jvp = &jval;
|
|
|
|
} // endif n
|
|
|
|
|
|
|
|
if (n >= Tjp->NextSame) {
|
|
|
|
if (++Nodes[n].Nx == ars) {
|
|
|
|
Nodes[n].Nx = 0;
|
|
|
|
Xnod = 0;
|
|
|
|
} else
|
|
|
|
Xnod = n;
|
|
|
|
|
|
|
|
Tjp->NextSame = Xnod;
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif NextSame
|
2015-01-31 15:05:43 +01:00
|
|
|
|
|
|
|
SetJsonValue(g, Value, jvp, n);
|
|
|
|
return Value;
|
|
|
|
} // end of ExpandArray
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* CalculateArray: */
|
|
|
|
/***********************************************************************/
|
|
|
|
PVAL JSONCOL::CalculateArray(PGLOBAL g, PJAR arp, int n)
|
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
int i, ars, nv = 0, nextsame = Tjp->NextSame;
|
|
|
|
bool err;
|
2015-01-31 15:05:43 +01:00
|
|
|
OPVAL op = Nodes[n].Op;
|
|
|
|
PVAL val[2], vp = Nodes[n].Valp;
|
2015-02-11 21:39:41 +01:00
|
|
|
PJVAL jvrp, jvp;
|
2015-01-31 15:05:43 +01:00
|
|
|
JVALUE jval;
|
|
|
|
|
|
|
|
vp->Reset();
|
2019-08-17 16:58:58 +02:00
|
|
|
ars = MY_MIN(Tjp->Limit, arp->size());
|
2017-10-15 16:13:23 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (trace(1))
|
|
|
|
htrc("CalculateArray: size=%d op=%d nextsame=%d\n",
|
|
|
|
ars, op, nextsame);
|
2015-01-31 15:05:43 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
for (i = 0; i < ars; i++) {
|
|
|
|
jvrp = arp->GetValue(i);
|
2015-01-31 15:05:43 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (trace(1))
|
|
|
|
htrc("i=%d nv=%d\n", i, nv);
|
2017-10-15 16:13:23 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (!jvrp->IsNull() || (op == OP_CNC && GetJsonNull())) do {
|
|
|
|
if (jvrp->IsNull()) {
|
|
|
|
jvrp->Value = AllocateValue(g, GetJsonNull(), TYPE_STRING);
|
|
|
|
jvp = jvrp;
|
|
|
|
} else if (n < Nod - 1 && jvrp->GetJson()) {
|
2015-02-11 21:39:41 +01:00
|
|
|
Tjp->NextSame = nextsame;
|
|
|
|
jval.SetValue(GetColumnValue(g, jvrp->GetJson(), n + 1));
|
|
|
|
jvp = &jval;
|
|
|
|
} else
|
|
|
|
jvp = jvrp;
|
2017-10-16 00:48:03 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (trace(1))
|
|
|
|
htrc("jvp=%s null=%d\n",
|
|
|
|
jvp->GetString(g), jvp->IsNull() ? 1 : 0);
|
|
|
|
|
|
|
|
if (!nv++) {
|
2015-02-11 21:39:41 +01:00
|
|
|
SetJsonValue(g, vp, jvp, n);
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
SetJsonValue(g, MulVal, jvp, n);
|
2017-10-16 15:08:17 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (!MulVal->IsNull()) {
|
|
|
|
switch (op) {
|
2015-02-11 21:39:41 +01:00
|
|
|
case OP_CNC:
|
|
|
|
if (Nodes[n].CncVal) {
|
|
|
|
val[0] = Nodes[n].CncVal;
|
|
|
|
err = vp->Compute(g, val, 1, op);
|
|
|
|
} // endif CncVal
|
2019-08-17 16:58:58 +02:00
|
|
|
|
2015-02-11 21:39:41 +01:00
|
|
|
val[0] = MulVal;
|
2015-01-31 15:05:43 +01:00
|
|
|
err = vp->Compute(g, val, 1, op);
|
2019-08-17 16:58:58 +02:00
|
|
|
break;
|
2015-02-11 21:39:41 +01:00
|
|
|
// case OP_NUM:
|
|
|
|
case OP_SEP:
|
|
|
|
val[0] = Nodes[n].Valp;
|
|
|
|
val[1] = MulVal;
|
|
|
|
err = vp->Compute(g, val, 2, OP_ADD);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
val[0] = Nodes[n].Valp;
|
|
|
|
val[1] = MulVal;
|
|
|
|
err = vp->Compute(g, val, 2, op);
|
|
|
|
} // endswitch Op
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
vp->Reset();
|
2017-10-16 00:48:03 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (trace(1)) {
|
|
|
|
char buf(32);
|
2017-10-16 15:08:17 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
htrc("vp='%s' err=%d\n",
|
|
|
|
vp->GetCharString(&buf), err ? 1 : 0);
|
2017-10-16 00:48:03 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} // endif trace
|
|
|
|
|
|
|
|
} // endif Null
|
2015-01-31 15:05:43 +01:00
|
|
|
|
2015-02-11 21:39:41 +01:00
|
|
|
} while (Tjp->NextSame > nextsame);
|
2015-01-31 15:05:43 +01:00
|
|
|
|
|
|
|
} // endfor i
|
|
|
|
|
|
|
|
if (op == OP_SEP) {
|
|
|
|
// Calculate average
|
2015-02-11 21:39:41 +01:00
|
|
|
MulVal->SetValue(nv);
|
2015-01-31 15:05:43 +01:00
|
|
|
val[0] = vp;
|
|
|
|
val[1] = MulVal;
|
|
|
|
|
|
|
|
if (vp->Compute(g, val, 2, OP_DIV))
|
|
|
|
vp->Reset();
|
|
|
|
|
|
|
|
} // endif Op
|
|
|
|
|
2015-02-11 21:39:41 +01:00
|
|
|
Tjp->NextSame = nextsame;
|
2015-01-31 15:05:43 +01:00
|
|
|
return vp;
|
|
|
|
} // end of CalculateArray
|
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* GetRow: Get the object containing this column. */
|
|
|
|
/***********************************************************************/
|
2015-01-30 10:57:00 +01:00
|
|
|
PJSON JSONCOL::GetRow(PGLOBAL g)
|
2015-01-19 18:55:25 +01:00
|
|
|
{
|
2015-04-17 20:05:41 +02:00
|
|
|
PJVAL val = NULL;
|
2015-01-19 18:55:25 +01:00
|
|
|
PJAR arp;
|
|
|
|
PJSON nwr, row = Tjp->Row;
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
for (int i = 0; i < Nod && row; i++) {
|
|
|
|
if (Nodes[i+1].Op == OP_XX)
|
2015-02-19 00:59:02 +01:00
|
|
|
break;
|
|
|
|
else switch (row->GetType()) {
|
2015-01-19 18:55:25 +01:00
|
|
|
case TYPE_JOB:
|
|
|
|
if (!Nodes[i].Key)
|
2015-12-04 22:38:16 +01:00
|
|
|
// Expected Array was not there, wrap the value
|
2015-01-19 18:55:25 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
val = ((PJOB)row)->GetValue(Nodes[i].Key);
|
|
|
|
break;
|
|
|
|
case TYPE_JAR:
|
2019-08-17 16:58:58 +02:00
|
|
|
arp = (PJAR)row;
|
2015-01-31 15:05:43 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (!Nodes[i].Key) {
|
2015-05-17 15:22:42 +02:00
|
|
|
if (Nodes[i].Op == OP_EQ)
|
|
|
|
val = arp->GetValue(Nodes[i].Rank);
|
|
|
|
else
|
|
|
|
val = arp->GetValue(Nodes[i].Rx);
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
} else {
|
2019-08-17 16:58:58 +02:00
|
|
|
// Unexpected array, unwrap it as [0]
|
|
|
|
val = arp->GetValue(0);
|
|
|
|
i--;
|
|
|
|
} // endif Nodes
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
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();
|
2015-01-30 10:57:00 +01:00
|
|
|
} else {
|
2015-01-19 18:55:25 +01:00
|
|
|
// Construct missing objects
|
|
|
|
for (i++; row && i < Nod; i++) {
|
2015-02-11 21:39:41 +01:00
|
|
|
if (Nodes[i].Op == OP_XX)
|
|
|
|
break;
|
|
|
|
else if (!Nodes[i].Key)
|
2015-01-19 18:55:25 +01:00
|
|
|
// Construct intermediate array
|
2015-12-08 16:39:13 +01:00
|
|
|
nwr = new(G) JARRAY;
|
2015-02-11 21:39:41 +01:00
|
|
|
else
|
2015-12-08 16:39:13 +01:00
|
|
|
nwr = new(G) JOBJECT;
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
if (row->GetType() == TYPE_JOB) {
|
2015-12-08 16:39:13 +01:00
|
|
|
((PJOB)row)->SetValue(G, new(G) JVALUE(nwr), Nodes[i-1].Key);
|
2015-01-19 18:55:25 +01:00
|
|
|
} else if (row->GetType() == TYPE_JAR) {
|
2015-12-08 16:39:13 +01:00
|
|
|
((PJAR)row)->AddValue(G, new(G) JVALUE(nwr));
|
|
|
|
((PJAR)row)->InitArray(G);
|
2015-01-19 18:55:25 +01:00
|
|
|
} else {
|
|
|
|
strcpy(g->Message, "Wrong type when writing new row");
|
|
|
|
nwr = NULL;
|
|
|
|
} // endif's
|
|
|
|
|
|
|
|
row = nwr;
|
|
|
|
} // endfor i
|
|
|
|
|
|
|
|
break;
|
2015-01-30 10:57:00 +01:00
|
|
|
} // endelse
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
} // endfor i
|
|
|
|
|
|
|
|
return row;
|
|
|
|
} // end of GetRow
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* WriteColumn: */
|
|
|
|
/***********************************************************************/
|
|
|
|
void JSONCOL::WriteColumn(PGLOBAL g)
|
|
|
|
{
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Xpd && Tjp->Pretty < 2) {
|
|
|
|
strcpy(g->Message, "Cannot write expanded column when Pretty is not 2");
|
|
|
|
throw 666;
|
|
|
|
} // endif Xpd
|
2015-08-11 21:15:33 +02:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
/*********************************************************************/
|
2015-01-19 18:55:25 +01:00
|
|
|
/* 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;
|
|
|
|
|
2015-02-11 21:39:41 +01:00
|
|
|
char *s;
|
2015-01-19 18:55:25 +01:00
|
|
|
PJOB objp = NULL;
|
|
|
|
PJAR arp = NULL;
|
|
|
|
PJVAL jvp = NULL;
|
2015-02-11 21:39:41 +01:00
|
|
|
PJSON jsp, row = GetRow(g);
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
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:
|
2015-02-11 21:39:41 +01:00
|
|
|
if (Nodes[Nod-1].Op == OP_XX) {
|
|
|
|
s = Value->GetCharValue();
|
2015-02-22 17:53:02 +01:00
|
|
|
|
2015-12-08 16:39:13 +01:00
|
|
|
if (!(jsp = ParseJson(G, s, (int)strlen(s)))) {
|
2015-02-22 17:53:02 +01:00
|
|
|
strcpy(g->Message, s);
|
2019-08-17 16:58:58 +02:00
|
|
|
throw 666;
|
|
|
|
} // endif jsp
|
2015-02-11 21:39:41 +01:00
|
|
|
|
|
|
|
if (arp) {
|
2015-05-17 15:22:42 +02:00
|
|
|
if (Nod > 1 && Nodes[Nod-2].Op == OP_EQ)
|
2015-12-08 16:39:13 +01:00
|
|
|
arp->SetValue(G, new(G) JVALUE(jsp), Nodes[Nod-2].Rank);
|
2015-02-19 00:59:02 +01:00
|
|
|
else
|
2015-12-08 16:39:13 +01:00
|
|
|
arp->AddValue(G, new(G) JVALUE(jsp));
|
2015-02-19 00:59:02 +01:00
|
|
|
|
2015-12-08 16:39:13 +01:00
|
|
|
arp->InitArray(G);
|
2015-02-11 21:39:41 +01:00
|
|
|
} else if (objp) {
|
|
|
|
if (Nod > 1 && Nodes[Nod-2].Key)
|
2015-12-08 16:39:13 +01:00
|
|
|
objp->SetValue(G, new(G) JVALUE(jsp), Nodes[Nod-2].Key);
|
2015-02-11 21:39:41 +01:00
|
|
|
|
|
|
|
} else if (jvp)
|
|
|
|
jvp->SetValue(jsp);
|
|
|
|
|
|
|
|
break;
|
|
|
|
} // endif Op
|
|
|
|
|
2017-08-31 01:22:50 +02:00
|
|
|
// fall through
|
2015-01-19 18:55:25 +01:00
|
|
|
case TYPE_DATE:
|
|
|
|
case TYPE_INT:
|
2019-08-17 16:58:58 +02:00
|
|
|
case TYPE_TINY:
|
|
|
|
case TYPE_SHORT:
|
|
|
|
case TYPE_BIGINT:
|
|
|
|
case TYPE_DOUBLE:
|
2015-01-19 18:55:25 +01:00
|
|
|
if (arp) {
|
2015-05-17 15:22:42 +02:00
|
|
|
if (Nodes[Nod-1].Op == OP_EQ)
|
2015-12-08 16:39:13 +01:00
|
|
|
arp->SetValue(G, new(G) JVALUE(G, Value), Nodes[Nod-1].Rank);
|
2015-01-19 18:55:25 +01:00
|
|
|
else
|
2015-12-08 16:39:13 +01:00
|
|
|
arp->AddValue(G, new(G) JVALUE(G, Value));
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-12-08 16:39:13 +01:00
|
|
|
arp->InitArray(G);
|
2015-01-19 18:55:25 +01:00
|
|
|
} else if (objp) {
|
|
|
|
if (Nodes[Nod-1].Key)
|
2015-12-08 16:39:13 +01:00
|
|
|
objp->SetValue(G, new(G) JVALUE(G, Value), Nodes[Nod-1].Key);
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
} 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)
|
|
|
|
{
|
|
|
|
Doc = NULL;
|
|
|
|
Multiple = tdp->Multiple;
|
|
|
|
Done = Changed = false;
|
|
|
|
} // end of TDBJSON standard constructor
|
|
|
|
|
|
|
|
TDBJSON::TDBJSON(PJTDB tdbp) : TDBJSN(tdbp)
|
|
|
|
{
|
|
|
|
Doc = tdbp->Doc;
|
|
|
|
Multiple = tdbp->Multiple;
|
|
|
|
Done = tdbp->Done;
|
|
|
|
Changed = tdbp->Changed;
|
|
|
|
} // end of TDBJSON copy constructor
|
|
|
|
|
|
|
|
// Used for update
|
2017-02-16 18:01:48 +01:00
|
|
|
PTDB TDBJSON::Clone(PTABS t)
|
2015-01-19 18:55:25 +01:00
|
|
|
{
|
|
|
|
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;
|
2017-02-16 18:01:48 +01:00
|
|
|
} // end of Clone
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2015-04-17 20:05:41 +02:00
|
|
|
/* Make the document tree from the object path. */
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
int TDBJSON::MakeNewDoc(PGLOBAL g)
|
|
|
|
{
|
|
|
|
// Create a void table that will be populated
|
|
|
|
Doc = new(g) JARRAY;
|
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
if (MakeTopTree(g, Doc))
|
|
|
|
return RC_FX;
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
Done = true;
|
2015-01-19 18:55:25 +01:00
|
|
|
return RC_OK;
|
|
|
|
} // end of MakeNewDoc
|
|
|
|
|
2015-03-18 13:30:14 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Make the document tree from a file. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBJSON::MakeDocument(PGLOBAL g)
|
|
|
|
{
|
2015-05-09 17:30:20 +02:00
|
|
|
char *p, *memory, *objpath, *key = NULL;
|
2015-01-20 01:21:56 +01:00
|
|
|
int len, i = 0;
|
2015-04-17 20:05:41 +02:00
|
|
|
MODE mode = Mode;
|
2015-01-19 18:55:25 +01:00
|
|
|
PJSON jsp;
|
|
|
|
PJOB objp = NULL;
|
|
|
|
PJAR arp = NULL;
|
|
|
|
PJVAL val = NULL;
|
|
|
|
|
|
|
|
if (Done)
|
|
|
|
return RC_OK;
|
|
|
|
|
|
|
|
/*********************************************************************/
|
2019-08-17 16:58:58 +02:00
|
|
|
/* Create the mapping file object in mode read. */
|
2015-01-19 18:55:25 +01:00
|
|
|
/*********************************************************************/
|
2015-04-17 20:05:41 +02:00
|
|
|
Mode = MODE_READ;
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
if (!Txfp->OpenTableFile(g)) {
|
|
|
|
PFBLOCK fp = Txfp->GetTo_Fb();
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
if (fp) {
|
|
|
|
len = fp->Length;
|
|
|
|
memory = fp->Memory;
|
|
|
|
} else {
|
|
|
|
Mode = mode; // Restore saved Mode
|
|
|
|
return MakeNewDoc(g);
|
|
|
|
} // endif fp
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
} else
|
2015-01-19 18:55:25 +01:00
|
|
|
return RC_FX;
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Parse the json file and allocate its tree structure. */
|
|
|
|
/*********************************************************************/
|
|
|
|
g->Message[0] = 0;
|
2015-12-04 22:38:16 +01:00
|
|
|
jsp = Top = ParseJson(g, memory, len, &Pretty);
|
2015-04-17 20:05:41 +02:00
|
|
|
Txfp->CloseTableFile(g, false);
|
|
|
|
Mode = mode; // Restore saved Mode
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
if (!jsp && g->Message[0])
|
|
|
|
return RC_FX;
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if ((objpath = PlugDup(g, Objname))) {
|
|
|
|
if (*objpath == '$') objpath++;
|
|
|
|
if (*objpath == '.') objpath++;
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Find the table in the tree structure. */
|
|
|
|
/*********************************************************************/
|
|
|
|
for (; jsp && objpath; objpath = p) {
|
|
|
|
if ((p = strchr(objpath, Sep)))
|
|
|
|
*p++ = 0;
|
|
|
|
|
|
|
|
if (*objpath != '[' && !IsNum(objpath)) {
|
|
|
|
// objpass is a key
|
|
|
|
if (jsp->GetType() != TYPE_JOB) {
|
|
|
|
strcpy(g->Message, "Table path does not match the 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 == '[') {
|
|
|
|
// Old style
|
|
|
|
if (objpath[strlen(objpath) - 1] != ']') {
|
|
|
|
sprintf(g->Message, "Invalid Table path %s", Objname);
|
|
|
|
return RC_FX;
|
|
|
|
} else
|
|
|
|
objpath++;
|
|
|
|
|
|
|
|
} // endif objpath
|
|
|
|
|
|
|
|
if (jsp->GetType() != TYPE_JAR) {
|
|
|
|
strcpy(g->Message, "Table path does not match the json file");
|
|
|
|
return RC_FX;
|
|
|
|
} // endif Type
|
|
|
|
|
|
|
|
arp = jsp->GetArray();
|
|
|
|
objp = NULL;
|
|
|
|
i = atoi(objpath) - B;
|
|
|
|
val = arp->GetValue(i);
|
|
|
|
|
|
|
|
if (!val) {
|
|
|
|
sprintf(g->Message, "Cannot find array value %d", i);
|
|
|
|
return RC_FX;
|
|
|
|
} // endif val
|
|
|
|
|
|
|
|
} // endif
|
|
|
|
|
|
|
|
jsp = val->GetJson();
|
|
|
|
} // endfor objpath
|
|
|
|
|
|
|
|
} // endif objpath
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
if (jsp && jsp->GetType() == TYPE_JAR)
|
|
|
|
Doc = jsp->GetArray();
|
2019-08-17 16:58:58 +02:00
|
|
|
else {
|
2015-01-19 18:55:25 +01:00
|
|
|
// 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
|
|
|
|
|
2015-04-17 20:05:41 +02:00
|
|
|
Done = true;
|
2015-01-19 18:55:25 +01:00
|
|
|
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. */
|
|
|
|
/***********************************************************************/
|
2015-05-09 17:30:20 +02:00
|
|
|
int TDBJSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool)
|
2015-01-19 18:55:25 +01:00
|
|
|
{
|
|
|
|
if (pxdf) {
|
|
|
|
strcpy(g->Message, "JSON not indexable when pretty = 2");
|
|
|
|
return RC_FX;
|
|
|
|
} else
|
|
|
|
return RC_OK;
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
} // end of MakeIndex
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2015-02-28 23:01:55 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Return the position in the table. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBJSON::GetRecpos(void)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
union {
|
|
|
|
uint Rpos;
|
|
|
|
BYTE Spos[4];
|
|
|
|
};
|
|
|
|
|
|
|
|
Rpos = htonl(Fpos);
|
|
|
|
Spos[0] = (BYTE)NextSame;
|
|
|
|
return Rpos;
|
|
|
|
#endif // 0
|
|
|
|
return Fpos;
|
|
|
|
} // end of GetRecpos
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Set the position in the table. */
|
|
|
|
/***********************************************************************/
|
2015-05-09 17:30:20 +02:00
|
|
|
bool TDBJSON::SetRecpos(PGLOBAL, int recpos)
|
2015-02-28 23:01:55 +01:00
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
union {
|
|
|
|
uint Rpos;
|
|
|
|
BYTE Spos[4];
|
|
|
|
};
|
|
|
|
|
|
|
|
Rpos = recpos;
|
|
|
|
NextSame = Spos[0];
|
|
|
|
Spos[0] = 0;
|
|
|
|
Fpos = (signed)ntohl(Rpos);
|
|
|
|
|
|
|
|
//if (Fpos != (signed)ntohl(Rpos)) {
|
|
|
|
// Fpos = ntohl(Rpos);
|
|
|
|
// same = false;
|
|
|
|
//} else
|
|
|
|
// same = true;
|
|
|
|
#endif // 0
|
|
|
|
|
|
|
|
Fpos = recpos - 1;
|
|
|
|
return false;
|
|
|
|
} // end of SetRecpos
|
|
|
|
|
2015-01-19 18:55:25 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* JSON Access Method opening routine. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBJSON::OpenDB(PGLOBAL g)
|
|
|
|
{
|
|
|
|
if (Use == USE_OPEN) {
|
|
|
|
/*******************************************************************/
|
|
|
|
/* Table already open replace it at its beginning. */
|
|
|
|
/*******************************************************************/
|
|
|
|
Fpos= -1;
|
|
|
|
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
|
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
if (Xcol)
|
|
|
|
To_Filter = NULL; // Imcompatible
|
2017-05-23 19:35:50 +02:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
Use = USE_OPEN;
|
|
|
|
return false;
|
2015-01-19 18:55:25 +01:00
|
|
|
} // end of OpenDB
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ReadDB: Data Base read routine for JSON access method. */
|
|
|
|
/***********************************************************************/
|
2015-05-09 17:30:20 +02:00
|
|
|
int TDBJSON::ReadDB(PGLOBAL)
|
2015-01-19 18:55:25 +01:00
|
|
|
{
|
2015-05-01 15:59:12 +02:00
|
|
|
int rc;
|
2015-01-19 18:55:25 +01:00
|
|
|
|
2019-08-17 16:58:58 +02:00
|
|
|
N++;
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
if (NextSame) {
|
2015-01-30 10:57:00 +01:00
|
|
|
SameRow = NextSame;
|
|
|
|
NextSame = false;
|
2015-05-17 15:22:42 +02:00
|
|
|
M++;
|
2015-01-19 18:55:25 +01:00
|
|
|
rc = RC_OK;
|
|
|
|
} else if (++Fpos < (signed)Doc->size()) {
|
|
|
|
Row = Doc->GetValue(Fpos);
|
|
|
|
|
|
|
|
if (Row->GetType() == TYPE_JVAL)
|
|
|
|
Row = ((PJVAL)Row)->GetJson();
|
|
|
|
|
|
|
|
SameRow = 0;
|
2015-05-17 15:22:42 +02:00
|
|
|
M = 1;
|
2015-01-19 18:55:25 +01:00
|
|
|
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)
|
2015-02-11 21:39:41 +01:00
|
|
|
if (Mode == MODE_INSERT) {
|
2015-01-19 18:55:25 +01:00
|
|
|
Doc->AddValue(g, (PJVAL)Row);
|
2015-02-11 21:39:41 +01:00
|
|
|
Row = new(g) JVALUE;
|
|
|
|
} else if (Doc->SetValue(g, (PJVAL)Row, Fpos))
|
2015-01-19 18:55:25 +01:00
|
|
|
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
|
2015-12-08 16:39:13 +01:00
|
|
|
char filename[_MAX_PATH];
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
Doc->InitArray(g);
|
|
|
|
|
|
|
|
// We used the file name relative to recorded datapath
|
|
|
|
PlugSetPath(filename, ((PJDEF)To_Def)->Fn, GetPath());
|
|
|
|
|
2015-01-20 01:21:56 +01:00
|
|
|
// Serialize the modified table
|
2015-12-08 16:39:13 +01:00
|
|
|
if (!Serialize(g, Top, filename, Pretty))
|
|
|
|
puts(g->Message);
|
2015-01-19 18:55:25 +01:00
|
|
|
|
|
|
|
} // end of CloseDB
|
|
|
|
|
2015-03-18 13:30:14 +01:00
|
|
|
/* ---------------------------TDBJCL class --------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* TDBJCL class constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
TDBJCL::TDBJCL(PJDEF tdp) : TDBCAT(tdp)
|
|
|
|
{
|
2015-05-26 01:02:33 +02:00
|
|
|
Topt = tdp->GetTopt();
|
2019-08-17 16:58:58 +02:00
|
|
|
Db = tdp->Schema;
|
|
|
|
Dsn = tdp->Uri;
|
2015-03-18 13:30:14 +01:00
|
|
|
} // end of TDBJCL constructor
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* GetResult: Get the list the JSON file columns. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PQRYRES TDBJCL::GetResult(PGLOBAL g)
|
|
|
|
{
|
2017-05-23 19:35:50 +02:00
|
|
|
return JSONColumns(g, Db, Dsn, Topt, false);
|
2015-03-18 13:30:14 +01:00
|
|
|
} // end of GetResult
|
|
|
|
|
|
|
|
/* --------------------------- End of json --------------------------- */
|