mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 20:12:31 +01:00
fc6e8a3d32
Similar to 567b6812
continue to replace use of strcat() and
strcpy() with safer options strncat() and strncpy().
All new code of the whole pull request, including one or several files
that are either new files or modified ones, are contributed under the BSD-new
license. I am contributing on behalf of my employer Amazon Web Services
2709 lines
77 KiB
C++
2709 lines
77 KiB
C++
/************* tabjson C++ Program Source Code File (.CPP) *************/
|
|
/* PROGRAM NAME: tabjson Version 1.9 */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2021 */
|
|
/* This program are the JSON class DB execution routines. */
|
|
/***********************************************************************/
|
|
#undef BSON_SUPPORT
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant sections of the MariaDB header file. */
|
|
/***********************************************************************/
|
|
#include <my_global.h>
|
|
#include <mysqld.h>
|
|
#include <sql_error.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"
|
|
#if defined(GZ_SUPPORT)
|
|
#include "filamgz.h"
|
|
#endif // GZ_SUPPORT
|
|
#if defined(ZIP_SUPPORT)
|
|
#include "filamzip.h"
|
|
#endif // ZIP_SUPPORT
|
|
#if defined(JAVA_SUPPORT)
|
|
#include "jmgfam.h"
|
|
#endif // JAVA_SUPPORT
|
|
#if defined(CMGO_SUPPORT)
|
|
#include "cmgfam.h"
|
|
#endif // CMGO_SUPPORT
|
|
#include "tabmul.h"
|
|
#include "checklvl.h"
|
|
#include "resource.h"
|
|
#include "mycat.h" // for FNC_COL
|
|
|
|
/***********************************************************************/
|
|
/* This should be an option. */
|
|
/***********************************************************************/
|
|
#define MAXCOL 200 /* Default max column nb in result */
|
|
//#define TYPE_UNKNOWN 12 /* Must be greater than other types */
|
|
|
|
/***********************************************************************/
|
|
/* External functions. */
|
|
/***********************************************************************/
|
|
USETEMP UseTemp(void);
|
|
bool JsonAllPath(void);
|
|
int GetDefaultDepth(void);
|
|
char *GetJsonNull(void);
|
|
bool Stringified(PCSZ, char*);
|
|
|
|
/***********************************************************************/
|
|
/* JSONColumns: construct the result blocks containing the description */
|
|
/* of all the columns of a table contained inside a JSON file. */
|
|
/***********************************************************************/
|
|
PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
|
|
{
|
|
static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
|
|
TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
|
|
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
|
|
FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
|
|
static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
|
|
int i, n = 0;
|
|
int ncol = sizeof(buftyp) / sizeof(int);
|
|
PJCL jcp;
|
|
JSONDISC *pjdc = NULL;
|
|
PQRYRES qrp;
|
|
PCOLRES crp;
|
|
|
|
if (info) {
|
|
length[0] = 128;
|
|
length[7] = 256;
|
|
goto skipit;
|
|
} // endif info
|
|
|
|
if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Cannot find column definition for multiple table");
|
|
return NULL;
|
|
} // endif Multiple
|
|
|
|
pjdc = new(g) JSONDISC(g, length);
|
|
|
|
if (!(n = pjdc->GetColumns(g, db, dsn, topt)))
|
|
return NULL;
|
|
|
|
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;
|
|
crp->Name = PlugDup(g, "Nullable");
|
|
crp->Next->Name = PlugDup(g, "Jpath");
|
|
|
|
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) {
|
|
if (jcp->Type == TYPE_UNKNOWN)
|
|
jcp->Type = TYPE_STRG; // Void column
|
|
|
|
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. */
|
|
/*********************************************************************/
|
|
return qrp;
|
|
} // end of JSONColumns
|
|
|
|
/* -------------------------- Class JSONDISC ------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Class used to get the columns of a JSON table. */
|
|
/***********************************************************************/
|
|
JSONDISC::JSONDISC(PGLOBAL g, uint *lg)
|
|
{
|
|
length = lg;
|
|
jcp = fjcp = pjcp = NULL;
|
|
tdp = NULL;
|
|
tjnp = NULL;
|
|
jpp = NULL;
|
|
tjsp = NULL;
|
|
jsp = NULL;
|
|
row = NULL;
|
|
sep = NULL;
|
|
strfy = NULL;
|
|
i = n = bf = ncol = lvl = sz = limit = 0;
|
|
all = false;
|
|
} // end of JSONDISC constructor
|
|
|
|
int JSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
|
|
{
|
|
char filename[_MAX_PATH];
|
|
size_t reclg = 0;
|
|
bool mgo = (GetTypeID(topt->type) == TAB_MONGO);
|
|
PGLOBAL G = NULL;
|
|
|
|
lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
|
|
lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
|
|
sep = GetStringTableOption(g, topt, "Separator", ".");
|
|
strfy = GetStringTableOption(g, topt, "Stringify", NULL);
|
|
sz = GetIntegerTableOption(g, topt, "Jsize", 1024);
|
|
limit = GetIntegerTableOption(g, topt, "Limit", 50);
|
|
|
|
/*********************************************************************/
|
|
/* Open the input file. */
|
|
/*********************************************************************/
|
|
tdp = new(g) JSONDEF;
|
|
#if defined(ZIP_SUPPORT)
|
|
tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
|
|
tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
|
|
#endif // ZIP_SUPPORT
|
|
tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);
|
|
|
|
if (!tdp->Fn && topt->http) {
|
|
tdp->Fn = GetStringTableOption(g, topt, "Subtype", NULL);
|
|
topt->subtype = NULL;
|
|
} // endif fn
|
|
|
|
if (!(tdp->Database = SetPath(g, db)))
|
|
return 0;
|
|
|
|
if ((tdp->Objname = GetStringTableOption(g, topt, "Object", NULL))) {
|
|
if (*tdp->Objname == '$') tdp->Objname++;
|
|
if (*tdp->Objname == '.') tdp->Objname++;
|
|
} // endif Objname
|
|
|
|
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) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), MSG(MISSING_FNAME));
|
|
return 0;
|
|
} else
|
|
topt->subtype = NULL;
|
|
|
|
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) {
|
|
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
|
|
tdp->Collname = GetStringTableOption(g, topt, "Tabname", NULL);
|
|
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;
|
|
#else // !MONGO_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
return 0;
|
|
#endif // !MONGO_SUPPORT
|
|
} // endif Uri
|
|
|
|
if (tdp->Pretty == 2) {
|
|
if (tdp->Zipped) {
|
|
#if defined(ZIP_SUPPORT)
|
|
tjsp = new(g) TDBJSON(tdp, new(g) UNZFAM(tdp));
|
|
#else // !ZIP_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
return 0;
|
|
#endif // !ZIP_SUPPORT
|
|
} else
|
|
tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp));
|
|
|
|
if (tjsp->MakeDocument(g))
|
|
return 0;
|
|
|
|
jsp = (tjsp->GetDoc()) ? tjsp->GetDoc()->GetArrayValue(0) : NULL;
|
|
} else {
|
|
if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))
|
|
{
|
|
if (!mgo && !tdp->Uri) {
|
|
snprintf(g->Message, sizeof(g->Message), "LRECL must be specified for pretty=%d", tdp->Pretty);
|
|
return 0;
|
|
} else
|
|
tdp->Lrecl = 8192; // Should be enough
|
|
}
|
|
|
|
tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF);
|
|
|
|
if (tdp->Zipped) {
|
|
#if defined(ZIP_SUPPORT)
|
|
tjnp = new(g)TDBJSN(tdp, new(g) UNZFAM(tdp));
|
|
#else // !ZIP_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
return NULL;
|
|
#endif // !ZIP_SUPPORT
|
|
} else if (tdp->Uri) {
|
|
if (tdp->Driver && toupper(*tdp->Driver) == 'C') {
|
|
#if defined(CMGO_SUPPORT)
|
|
tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp));
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "C");
|
|
return 0;
|
|
#endif
|
|
} else if (tdp->Driver && toupper(*tdp->Driver) == 'J') {
|
|
#if defined(JAVA_SUPPORT)
|
|
tjnp = new(g) TDBJSN(tdp, new(g) JMGFAM(tdp));
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "Java");
|
|
return 0;
|
|
#endif
|
|
} else { // Driver not specified
|
|
#if defined(CMGO_SUPPORT)
|
|
tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp));
|
|
#elif defined(JAVA_SUPPORT)
|
|
tjnp = new(g) TDBJSN(tdp, new(g) JMGFAM(tdp));
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
return 0;
|
|
#endif
|
|
} // endif Driver
|
|
|
|
} else if (tdp->Pretty >= 0)
|
|
tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp));
|
|
else
|
|
tjnp = new(g) TDBJSN(tdp, new(g) BINFAM(tdp));
|
|
|
|
tjnp->SetMode(MODE_READ);
|
|
|
|
// Allocate the parse work memory
|
|
G = PlugInit(NULL, (size_t)tdp->Lrecl * (tdp->Pretty >= 0 ? 10 : 2));
|
|
tjnp->SetG(G);
|
|
|
|
if (tjnp->OpenDB(g))
|
|
return 0;
|
|
|
|
switch (tjnp->ReadDB(g)) {
|
|
case RC_EF:
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Void json table");
|
|
case RC_FX:
|
|
goto err;
|
|
default:
|
|
if (tdp->Pretty != 2)
|
|
reclg = strlen(tjnp->To_Line);
|
|
|
|
jsp = tjnp->Row;
|
|
} // endswitch ReadDB
|
|
|
|
} // endif pretty
|
|
|
|
if (!(row = (jsp) ? jsp->GetObject() : NULL)) {
|
|
safe_strcpy(g->Message, sizeof(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->Next) {
|
|
strncpy(colname, jpp->Key, 64);
|
|
fmt[bf] = 0;
|
|
|
|
if (Find(g, jpp->Val, 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:
|
|
if (tdp->Pretty != 2 && reclg < strlen(tjnp->To_Line))
|
|
reclg = strlen(tjnp->To_Line);
|
|
|
|
jsp = tjnp->Row;
|
|
} // endswitch ReadDB
|
|
|
|
} else
|
|
jsp = tjsp->GetDoc()->GetArrayValue(i);
|
|
|
|
if (!(row = (jsp) ? jsp->GetObject() : NULL))
|
|
break;
|
|
|
|
} // endfor i
|
|
|
|
if (tdp->Pretty != 2) {
|
|
if (!topt->lrecl)
|
|
topt->lrecl = reclg + 10;
|
|
|
|
tjnp->CloseDB(g);
|
|
} // endif Pretty
|
|
|
|
return n;
|
|
|
|
err:
|
|
if (tdp->Pretty != 2)
|
|
tjnp->CloseDB(g);
|
|
|
|
return 0;
|
|
} // end of GetColumns
|
|
|
|
bool JSONDISC::Find(PGLOBAL g, PJVAL jvp, PCSZ key, int j)
|
|
{
|
|
char *p, *pc = colname + strlen(colname);
|
|
int ars;
|
|
size_t n;
|
|
PJOB job;
|
|
PJAR jar;
|
|
|
|
if (jvp && jvp->DataType != TYPE_JSON) {
|
|
if (JsonAllPath() && !fmt[bf])
|
|
safe_strcat(fmt, sizeof(fmt), colname);
|
|
|
|
jcol.Type = jvp->DataType;
|
|
|
|
switch (jvp->DataType) {
|
|
case TYPE_STRG:
|
|
case TYPE_DTM:
|
|
jcol.Len = (int)strlen(jvp->Strp);
|
|
break;
|
|
case TYPE_INTG:
|
|
case TYPE_BINT:
|
|
jcol.Len = (int)strlen(jvp->GetString(g));
|
|
break;
|
|
case TYPE_DBL:
|
|
jcol.Len = (int)strlen(jvp->GetString(g));
|
|
jcol.Scale = jvp->Nd;
|
|
break;
|
|
case TYPE_BOOL:
|
|
jcol.Len = 1;
|
|
break;
|
|
default:
|
|
jcol.Len = 0;
|
|
break;
|
|
} // endswitch Type
|
|
|
|
jcol.Scale = jvp->Nd;
|
|
jcol.Cbn = jvp->DataType == TYPE_NULL;
|
|
} else if (!jvp || jvp->IsNull()) {
|
|
jcol.Type = TYPE_UNKNOWN;
|
|
jcol.Len = jcol.Scale = 0;
|
|
jcol.Cbn = true;
|
|
} else if (j < lvl && !Stringified(strfy, colname)) {
|
|
if (!fmt[bf])
|
|
safe_strcat(fmt, sizeof(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->Next) {
|
|
PCSZ k = jrp->Key;
|
|
|
|
if (*k != '$') {
|
|
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);
|
|
} // endif Key
|
|
|
|
if (Find(g, jrp->Val, 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 = MY_MIN(jar->GetSize(false), limit);
|
|
else
|
|
ars = MY_MIN(jar->GetSize(false), 1);
|
|
|
|
for (int k = 0; k < ars; k++) {
|
|
n = sizeof(fmt) - (strlen(fmt) + 1);
|
|
|
|
if (!tdp->Xcol || stricmp(tdp->Xcol, key)) {
|
|
sprintf(buf, "%d", k);
|
|
|
|
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);
|
|
|
|
if (Find(g, jar->GetArrayValue(k), "", j))
|
|
return true;
|
|
|
|
*p = *pc = 0;
|
|
} // endfor k
|
|
|
|
return false;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), "Logical error after %s", fmt);
|
|
return true;
|
|
} // endswitch Type
|
|
|
|
} else if (lvl >= 0) {
|
|
if (Stringified(strfy, colname)) {
|
|
if (!fmt[bf])
|
|
safe_strcat(fmt, sizeof(fmt), colname);
|
|
|
|
safe_strcat(fmt, sizeof(fmt), ".*");
|
|
} else if (JsonAllPath() && !fmt[bf])
|
|
safe_strcat(fmt, sizeof(fmt), colname);
|
|
|
|
jcol.Type = TYPE_STRG;
|
|
jcol.Len = sz;
|
|
jcol.Scale = 0;
|
|
jcol.Cbn = true;
|
|
} else
|
|
return false;
|
|
|
|
AddColumn(g);
|
|
return false;
|
|
} // end of Find
|
|
|
|
void JSONDISC::AddColumn(PGLOBAL g)
|
|
{
|
|
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 == TYPE_NULL)
|
|
jcp->Type = jcol.Type;
|
|
// else if (jcol.Type != TYPE_UNKNOWN && jcol.Type != TYPE_VOID)
|
|
// jcp->Type = TYPE_STRING;
|
|
else if (jcp->Type != TYPE_STRG)
|
|
switch (jcol.Type) {
|
|
case TYPE_STRG:
|
|
case TYPE_DBL:
|
|
jcp->Type = jcol.Type;
|
|
break;
|
|
case TYPE_BINT:
|
|
if (jcp->Type == TYPE_INTG || jcp->Type == TYPE_BOOL)
|
|
jcp->Type = jcol.Type;
|
|
|
|
break;
|
|
case TYPE_INTG:
|
|
if (jcp->Type == TYPE_BOOL)
|
|
jcp->Type = jcol.Type;
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
} // endswith Type
|
|
|
|
} // 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;
|
|
|
|
} // end of AddColumn
|
|
|
|
|
|
/* -------------------------- Class JSONDEF -------------------------- */
|
|
|
|
JSONDEF::JSONDEF(void)
|
|
{
|
|
Jmode = MODE_OBJECT;
|
|
Objname = NULL;
|
|
Xcol = NULL;
|
|
Pretty = 2;
|
|
Limit = 1;
|
|
Base = 0;
|
|
Strict = false;
|
|
Sep = '.';
|
|
Uri = NULL;
|
|
Collname = Options = Filter = NULL;
|
|
Pipe = false;
|
|
Driver = NULL;
|
|
Version = 0;
|
|
Wrapname = NULL;
|
|
} // end of JSONDEF constructor
|
|
|
|
/***********************************************************************/
|
|
/* DefineAM: define specific AM block values. */
|
|
/***********************************************************************/
|
|
bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
|
{
|
|
Schema = GetStringCatInfo(g, "DBname", Schema);
|
|
Jmode = (JMODE)GetIntCatInfo("Jmode", MODE_OBJECT);
|
|
|
|
if ((Objname = GetStringCatInfo(g, "Object", NULL))) {
|
|
if (*Objname == '$') Objname++;
|
|
if (*Objname == '.') Objname++;
|
|
} // endif Objname
|
|
|
|
Xcol = GetStringCatInfo(g, "Expand", NULL);
|
|
Pretty = GetIntCatInfo("Pretty", 2);
|
|
Limit = GetIntCatInfo("Limit", 50);
|
|
Base = GetIntCatInfo("Base", 0) ? 1 : 0;
|
|
Sep = *GetStringCatInfo(g, "Separator", ".");
|
|
Accept = GetBoolCatInfo("Accept", false);
|
|
|
|
// Don't use url as MONGO uri when called from REST
|
|
if (stricmp(am, "REST") && (Uri = GetStringCatInfo(g, "Connect", NULL))) {
|
|
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
|
|
Collname = GetStringCatInfo(g, "Name",
|
|
(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
|
|
Collname = GetStringCatInfo(g, "Tabname", Collname);
|
|
Options = GetStringCatInfo(g, "Colist", Xcol ? "all" : NULL);
|
|
Filter = GetStringCatInfo(g, "Filter", NULL);
|
|
Pipe = GetBoolCatInfo("Pipeline", false);
|
|
Driver = GetStringCatInfo(g, "Driver", NULL);
|
|
Version = GetIntCatInfo("Version", 3);
|
|
Pretty = 0;
|
|
#if defined(JAVA_SUPPORT)
|
|
if (Version == 2)
|
|
Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo2Interface");
|
|
else
|
|
Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo3Interface");
|
|
#endif // JAVA_SUPPORT
|
|
#else // !MONGO_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
return true;
|
|
#endif // !MONGO_SUPPORT
|
|
} // endif Uri
|
|
|
|
return DOSDEF::DefineAM(g, (Uri ? "XMGO" : "DOS"), poff);
|
|
} // end of DefineAM
|
|
|
|
/***********************************************************************/
|
|
/* GetTable: makes a new Table Description Block. */
|
|
/***********************************************************************/
|
|
PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
|
|
{
|
|
if (trace(1))
|
|
htrc("JSON GetTable Pretty=%d Uri=%s\n", Pretty, SVP(Uri));
|
|
|
|
if (Catfunc == FNC_COL)
|
|
return new(g)TDBJCL(this);
|
|
|
|
PTDBASE tdbp;
|
|
PTXF txfp = NULL;
|
|
|
|
// JSN not used for pretty=1 for insert or delete
|
|
if (Pretty <= 0 || (Pretty == 1 && (m == MODE_READ || m == MODE_UPDATE))) {
|
|
USETEMP tmp = UseTemp();
|
|
bool map = Mapped && Pretty >= 0 && m != MODE_INSERT &&
|
|
!(tmp != TMP_NO && m == MODE_UPDATE) &&
|
|
!(tmp == TMP_FORCE &&
|
|
(m == MODE_UPDATE || m == MODE_DELETE));
|
|
|
|
if (Uri) {
|
|
if (Driver && toupper(*Driver) == 'C') {
|
|
#if defined(CMGO_SUPPORT)
|
|
txfp = new(g) CMGFAM(this);
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "C");
|
|
return NULL;
|
|
#endif
|
|
} else if (Driver && toupper(*Driver) == 'J') {
|
|
#if defined(JAVA_SUPPORT)
|
|
txfp = new(g) JMGFAM(this);
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "Java");
|
|
return NULL;
|
|
#endif
|
|
} else { // Driver not specified
|
|
#if defined(CMGO_SUPPORT)
|
|
txfp = new(g) CMGFAM(this);
|
|
#elif defined(JAVA_SUPPORT)
|
|
txfp = new(g) JMGFAM(this);
|
|
#else // !MONGO_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "MONGO");
|
|
return NULL;
|
|
#endif // !MONGO_SUPPORT
|
|
} // endif Driver
|
|
|
|
Pretty = 4; // Not a file
|
|
} else if (Zipped) {
|
|
#if defined(ZIP_SUPPORT)
|
|
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 {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
|
|
return NULL;
|
|
} // endif's m
|
|
#else // !ZIP_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
return NULL;
|
|
#endif // !ZIP_SUPPORT
|
|
} else if (Compressed) {
|
|
#if defined(GZ_SUPPORT)
|
|
if (Compressed == 1)
|
|
txfp = new(g) GZFAM(this);
|
|
else
|
|
txfp = new(g) ZLBFAM(this);
|
|
#else // !GZ_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "GZ");
|
|
return NULL;
|
|
#endif // !GZ_SUPPORT
|
|
} else if (map)
|
|
txfp = new(g) MAPFAM(this);
|
|
else if (Pretty < 0) // BJsonfile
|
|
txfp = new(g) BINFAM(this);
|
|
else
|
|
txfp = new(g) DOSFAM(this);
|
|
|
|
// Txfp must be set for TDBJSN
|
|
tdbp = new(g) TDBJSN(this, txfp);
|
|
|
|
if (Lrecl) {
|
|
// Allocate the parse work memory
|
|
#if 0
|
|
PGLOBAL G = (PGLOBAL)PlugSubAlloc(g, NULL, sizeof(GLOBAL));
|
|
memset(G, 0, sizeof(GLOBAL));
|
|
G->Sarea_Size = (size_t)Lrecl * 10;
|
|
G->Sarea = PlugSubAlloc(g, NULL, G->Sarea_Size);
|
|
PlugSubSet(G->Sarea, G->Sarea_Size);
|
|
G->jump_level = 0;
|
|
((TDBJSN*)tdbp)->G = G;
|
|
#endif // 0
|
|
((TDBJSN*)tdbp)->G = PlugInit(NULL, (size_t)Lrecl * (Pretty >= 0 ? 12 : 4));
|
|
} else {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "LRECL is not defined");
|
|
return NULL;
|
|
} // endif Lrecl
|
|
|
|
} else {
|
|
if (Zipped) {
|
|
#if defined(ZIP_SUPPORT)
|
|
if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
|
|
txfp = new(g) UNZFAM(this);
|
|
} else if (m == MODE_INSERT) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "INSERT supported only for zipped JSON when pretty=0");
|
|
return NULL;
|
|
} else {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
|
|
return NULL;
|
|
} // endif's m
|
|
#else // !ZIP_SUPPORT
|
|
snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
return NULL;
|
|
#endif // !ZIP_SUPPORT
|
|
} else
|
|
txfp = new(g) MAPFAM(this);
|
|
|
|
tdbp = new(g) TDBJSON(this, txfp);
|
|
((TDBJSON*)tdbp)->G = g;
|
|
} // endif Pretty
|
|
|
|
if (Multiple)
|
|
tdbp = new(g) TDBMUL(tdbp);
|
|
|
|
return tdbp;
|
|
} // end of GetTable
|
|
|
|
/* --------------------------- Class TDBJSN -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Implementation of the TDBJSN class (Pretty < 2) */
|
|
/***********************************************************************/
|
|
TDBJSN::TDBJSN(PJDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
|
|
{
|
|
G = NULL;
|
|
Top = NULL;
|
|
Row = NULL;
|
|
Val = NULL;
|
|
Colp = NULL;
|
|
|
|
if (tdp) {
|
|
Jmode = tdp->Jmode;
|
|
Objname = tdp->Objname;
|
|
Xcol = tdp->Xcol;
|
|
Limit = tdp->Limit;
|
|
Pretty = tdp->Pretty;
|
|
B = tdp->Base ? 1 : 0;
|
|
Sep = tdp->Sep;
|
|
Strict = tdp->Strict;
|
|
} else {
|
|
Jmode = MODE_OBJECT;
|
|
Objname = NULL;
|
|
Xcol = NULL;
|
|
Limit = 1;
|
|
Pretty = 0;
|
|
B = 0;
|
|
Sep = '.';
|
|
Strict = false;
|
|
} // endif tdp
|
|
|
|
Fpos = -1;
|
|
N = M = 0;
|
|
NextSame = 0;
|
|
SameRow = 0;
|
|
Xval = -1;
|
|
Comma = false;
|
|
} // end of TDBJSN standard constructor
|
|
|
|
TDBJSN::TDBJSN(TDBJSN* tdbp) : TDBDOS(NULL, tdbp)
|
|
{
|
|
G = NULL;
|
|
Top = tdbp->Top;
|
|
Row = tdbp->Row;
|
|
Val = tdbp->Val;
|
|
Colp = tdbp->Colp;
|
|
Jmode = tdbp->Jmode;
|
|
Objname = tdbp->Objname;
|
|
Xcol = tdbp->Xcol;
|
|
Fpos = tdbp->Fpos;
|
|
N = tdbp->N;
|
|
M = tdbp->M;
|
|
Limit = tdbp->Limit;
|
|
NextSame = tdbp->NextSame;
|
|
SameRow = tdbp->SameRow;
|
|
Xval = tdbp->Xval;
|
|
B = tdbp->B;
|
|
Sep = tdbp->Sep;
|
|
Pretty = tdbp->Pretty;
|
|
Strict = tdbp->Strict;
|
|
Comma = tdbp->Comma;
|
|
} // end of TDBJSN copy constructor
|
|
|
|
// Used for update
|
|
PTDB TDBJSN::Clone(PTABS t)
|
|
{
|
|
G = NULL;
|
|
PTDB tp;
|
|
PJCOL cp1, cp2;
|
|
PGLOBAL g = t->G;
|
|
|
|
tp = new(g) TDBJSN(this);
|
|
|
|
for (cp1 = (PJCOL)Columns; cp1; cp1 = (PJCOL)cp1->GetNext()) {
|
|
cp2 = new(g) JSONCOL(cp1, tp); // Make a copy
|
|
NewPointer(t, cp1, cp2);
|
|
} // endfor cp1
|
|
|
|
return tp;
|
|
} // end of Clone
|
|
|
|
/***********************************************************************/
|
|
/* Allocate JSN column description block. */
|
|
/***********************************************************************/
|
|
PCOL TDBJSN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
|
{
|
|
PJCOL colp = new(g) JSONCOL(g, cdp, this, cprec, n);
|
|
|
|
return (colp->ParseJpath(g)) ? NULL : colp;
|
|
} // end of MakeCol
|
|
|
|
/***********************************************************************/
|
|
/* InsertSpecialColumn: Put a special column ahead of the column list.*/
|
|
/***********************************************************************/
|
|
PCOL TDBJSN::InsertSpecialColumn(PCOL colp)
|
|
{
|
|
if (!colp->IsSpecial())
|
|
return NULL;
|
|
|
|
//if (Xcol && ((SPCBLK*)colp)->GetRnm())
|
|
// colp->SetKey(0); // Rownum is no more a key
|
|
|
|
colp->SetNext(Columns);
|
|
Columns = colp;
|
|
return colp;
|
|
} // end of InsertSpecialColumn
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* 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);
|
|
|
|
} // endif Cardinal
|
|
|
|
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
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* JSON EstimatedLength. Returns an estimated minimum line length. */
|
|
/***********************************************************************/
|
|
int TDBJSN::EstimatedLength(void)
|
|
{
|
|
if (AvgLen <= 0)
|
|
return (Lrecl ? Lrecl : 1024) / 8; // TODO: make it better
|
|
else
|
|
return AvgLen;
|
|
|
|
} // end of Estimated Length
|
|
|
|
/***********************************************************************/
|
|
/* Find the row in the tree structure. */
|
|
/***********************************************************************/
|
|
PJSON TDBJSN::FindRow(PGLOBAL g)
|
|
{
|
|
char *p, *objpath = PlugDup(g, Objname);
|
|
char *sep = (char*)(Sep == ':' ? ":[" : ".[");
|
|
bool bp = false, b = false;
|
|
PJSON jsp = Row;
|
|
PJVAL val = NULL;
|
|
|
|
for (; jsp && objpath; objpath = p, bp = b) {
|
|
if ((p = strpbrk(objpath + 1, sep))) {
|
|
b = (*p == '[');
|
|
*p++ = 0;
|
|
} // endif p
|
|
|
|
if (!bp && *objpath != '[' && !IsNum(objpath)) { // objpass is a key
|
|
val = (jsp->GetType() == TYPE_JOB) ?
|
|
jsp->GetObject()->GetKeyValue(objpath) : NULL;
|
|
} else {
|
|
if (bp || *objpath == '[') {
|
|
if (objpath[strlen(objpath) - 1] != ']') {
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid Table path %s", Objname);
|
|
return NULL;
|
|
} else if (!bp)
|
|
objpath++;
|
|
|
|
} // endif [
|
|
|
|
val = (jsp->GetType() == TYPE_JAR) ?
|
|
jsp->GetArray()->GetArrayValue(atoi(objpath) - B) : NULL;
|
|
} // endif objpath
|
|
|
|
jsp = (val) ? val->GetJson() : NULL;
|
|
} // endfor objpath
|
|
|
|
if (jsp && jsp->GetType() != TYPE_JOB) {
|
|
if (jsp->GetType() == TYPE_JAR) {
|
|
jsp = jsp->GetArray()->GetArrayValue(B);
|
|
|
|
if (jsp->GetType() != TYPE_JOB)
|
|
jsp = NULL;
|
|
|
|
} else
|
|
jsp = NULL;
|
|
|
|
} // endif Type
|
|
|
|
return jsp;
|
|
} // end of FindRow
|
|
|
|
/***********************************************************************/
|
|
/* OpenDB: Data Base open routine for JSN access method. */
|
|
/***********************************************************************/
|
|
bool TDBJSN::OpenDB(PGLOBAL g)
|
|
{
|
|
if (Use == USE_OPEN) {
|
|
/*******************************************************************/
|
|
/* Table already open replace it at its beginning. */
|
|
/*******************************************************************/
|
|
Fpos= -1;
|
|
NextSame = 0;
|
|
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:
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid Jmode %d", Jmode);
|
|
return true;
|
|
} // endswitch Jmode
|
|
|
|
} // endif Use
|
|
|
|
if (Pretty < 0) {
|
|
/*******************************************************************/
|
|
/* Binary BJSON table. */
|
|
/*******************************************************************/
|
|
xtrc(1, "JSN OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
|
|
this, Tdb_No, Use, Mode);
|
|
|
|
if (Use == USE_OPEN) {
|
|
/*******************************************************************/
|
|
/* Table already open, just replace it at its beginning. */
|
|
/*******************************************************************/
|
|
if (!To_Kindex) {
|
|
Txfp->Rewind(); // see comment in Work.log
|
|
} else // Table is to be accessed through a sorted index table
|
|
To_Kindex->Reset();
|
|
|
|
return false;
|
|
} // endif use
|
|
|
|
/*********************************************************************/
|
|
/* Open according to logical input/output mode required. */
|
|
/* Use conventionnal input/output functions. */
|
|
/*********************************************************************/
|
|
if (Txfp->OpenTableFile(g))
|
|
return true;
|
|
|
|
Use = USE_OPEN; // Do it now in case we are recursively called
|
|
|
|
/*********************************************************************/
|
|
/* Lrecl is Ok. */
|
|
/*********************************************************************/
|
|
size_t linelen = Lrecl;
|
|
MODE mode = Mode;
|
|
|
|
// Buffer must be allocated in g->Sarea
|
|
Mode = MODE_ANY;
|
|
Txfp->AllocateBuffer(g);
|
|
Mode = mode;
|
|
|
|
//To_Line = (char*)PlugSubAlloc(g, NULL, linelen);
|
|
//memset(To_Line, 0, linelen);
|
|
To_Line = Txfp->GetBuf();
|
|
xtrc(1, "OpenJSN: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
|
|
return false;
|
|
} else if (TDBDOS::OpenDB(g))
|
|
return true;
|
|
|
|
if (Xcol)
|
|
To_Filter = NULL; // Imcompatible
|
|
|
|
return false;
|
|
} // end of OpenDB
|
|
|
|
/***********************************************************************/
|
|
/* SkipHeader: Physically skip first header line if applicable. */
|
|
/* This is called from TDBDOS::OpenDB and must be executed before */
|
|
/* Kindex construction if the file is accessed using an index. */
|
|
/***********************************************************************/
|
|
bool TDBJSN::SkipHeader(PGLOBAL g)
|
|
{
|
|
int len = GetFileLength(g);
|
|
bool rc = false;
|
|
|
|
#if defined(_DEBUG)
|
|
if (len < 0)
|
|
return true;
|
|
#endif // _DEBUG
|
|
|
|
if (Pretty == 1) {
|
|
if (Mode == MODE_INSERT || Mode == MODE_DELETE) {
|
|
// Mode Insert and delete are no more handled here
|
|
DBUG_ASSERT(false);
|
|
} else if (len > 0) // !Insert && !Delete
|
|
rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
|
|
|
|
} // endif Pretty
|
|
|
|
return rc;
|
|
} // end of SkipHeader
|
|
|
|
/***********************************************************************/
|
|
/* ReadDB: Data Base read routine for JSN access method. */
|
|
/***********************************************************************/
|
|
int TDBJSN::ReadDB(PGLOBAL g) {
|
|
int rc;
|
|
|
|
N++;
|
|
|
|
if (NextSame) {
|
|
SameRow = NextSame;
|
|
NextSame = 0;
|
|
M++;
|
|
return RC_OK;
|
|
} else if ((rc = TDBDOS::ReadDB(g)) == RC_OK) {
|
|
if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK))
|
|
return rc; // Deferred reading failed
|
|
|
|
if (Pretty >= 0) {
|
|
// 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, "]")) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), G->Message);
|
|
rc = RC_FX;
|
|
} else
|
|
rc = RC_EF;
|
|
|
|
} else {
|
|
// Here we get a movable Json binary tree
|
|
PJSON jsp;
|
|
SWAP* swp;
|
|
|
|
jsp = (PJSON)To_Line;
|
|
swp = new(g) SWAP(G, jsp);
|
|
swp->SwapJson(jsp, false); // Restore pointers from offsets
|
|
Row = jsp;
|
|
Row = FindRow(g);
|
|
SameRow = 0;
|
|
Fpos++;
|
|
M = 1;
|
|
rc = RC_OK;
|
|
} // endif Pretty
|
|
|
|
} // endif ReadDB
|
|
|
|
return rc;
|
|
} // end of ReadDB
|
|
|
|
/***********************************************************************/
|
|
/* Make the top tree from the object path. */
|
|
/***********************************************************************/
|
|
bool TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp)
|
|
{
|
|
if (Objname) {
|
|
if (!Val) {
|
|
// Parse and allocate Objname item(s)
|
|
char *p, *objpath = PlugDup(g, Objname);
|
|
char *sep = (char*)(Sep == ':' ? ":[" : ".[");
|
|
int i;
|
|
bool bp = false, b = false;
|
|
PJOB objp;
|
|
PJAR arp;
|
|
PJVAL val = NULL;
|
|
|
|
Top = NULL;
|
|
|
|
for (; objpath; objpath = p, bp = b) {
|
|
if ((p = strpbrk(objpath + 1, sep))) {
|
|
b = (*p == '[');
|
|
*p++ = 0;
|
|
} // endif p
|
|
|
|
if (!bp && *objpath != '[' && !IsNum(objpath)) {
|
|
objp = new(g) JOBJECT;
|
|
|
|
if (!Top)
|
|
Top = objp;
|
|
|
|
if (val)
|
|
val->SetValue(objp);
|
|
|
|
val = new(g) JVALUE;
|
|
objp->SetKeyValue(g, val, objpath);
|
|
} else {
|
|
if (bp || *objpath == '[') {
|
|
// Old style
|
|
if (objpath[strlen(objpath) - 1] != ']') {
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid Table path %s", Objname);
|
|
return true;
|
|
} else if (!bp)
|
|
objpath++;
|
|
|
|
} // endif bp
|
|
|
|
arp = new(g) JARRAY;
|
|
|
|
if (!Top)
|
|
Top = arp;
|
|
|
|
if (val)
|
|
val->SetValue(arp);
|
|
|
|
val = new(g) JVALUE;
|
|
i = atoi(objpath) - B;
|
|
arp->SetArrayValue(g, val, i);
|
|
arp->InitArray(g);
|
|
} // endif objpath
|
|
|
|
} // endfor p
|
|
|
|
Val = val;
|
|
} // endif Val
|
|
|
|
Val->SetValue(jsp);
|
|
} else
|
|
Top = jsp;
|
|
|
|
return false;
|
|
} // end of MakeTopTree
|
|
|
|
/***********************************************************************/
|
|
/* PrepareWriting: Prepare the line for WriteDB. */
|
|
/***********************************************************************/
|
|
bool TDBJSN::PrepareWriting(PGLOBAL g)
|
|
{
|
|
PSZ s;
|
|
|
|
if (MakeTopTree(g, Row))
|
|
return true;
|
|
|
|
if ((s = Serialize(G, Top, NULL, Pretty))) {
|
|
if (Comma)
|
|
strcat(s, ",");
|
|
|
|
if ((signed)strlen(s) > Lrecl) {
|
|
safe_strcpy(To_Line, Lrecl, s);
|
|
snprintf(g->Message, sizeof(g->Message), "Line truncated (lrecl=%d)", Lrecl);
|
|
return PushWarning(g, this);
|
|
} else
|
|
strcpy(To_Line, s);
|
|
|
|
return false;
|
|
} else
|
|
return true;
|
|
|
|
} // end of PrepareWriting
|
|
|
|
/***********************************************************************/
|
|
/* WriteDB: Data Base write routine for JSON access method. */
|
|
/***********************************************************************/
|
|
int TDBJSN::WriteDB(PGLOBAL g)
|
|
{
|
|
int rc = TDBDOS::WriteDB(g);
|
|
|
|
PlugSubSet(G->Sarea, G->Sarea_Size);
|
|
Row->Clear();
|
|
return rc;
|
|
} // end of WriteDB
|
|
|
|
/***********************************************************************/
|
|
/* Data Base close routine for JSON access method. */
|
|
/***********************************************************************/
|
|
void TDBJSN::CloseDB(PGLOBAL g)
|
|
{
|
|
TDBDOS::CloseDB(g);
|
|
G = PlugExit(G);
|
|
} // end of CloseDB
|
|
|
|
/* ---------------------------- JSONCOL ------------------------------ */
|
|
|
|
/***********************************************************************/
|
|
/* JSONCOL public constructor. */
|
|
/***********************************************************************/
|
|
JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
|
|
: DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
|
|
{
|
|
Tjp = (TDBJSN *)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
|
|
G = Tjp->G;
|
|
Jpath = cdp->GetFmt();
|
|
MulVal = NULL;
|
|
Nodes = NULL;
|
|
Nod = 0;
|
|
Sep = Tjp->Sep;
|
|
Xnod = -1;
|
|
Xpd = false;
|
|
Parsed = false;
|
|
Warned = false;
|
|
Sgfy = false;
|
|
} // end of JSONCOL constructor
|
|
|
|
/***********************************************************************/
|
|
/* JSONCOL constructor used for copying columns. */
|
|
/* tdbp is the pointer to the new table descriptor. */
|
|
/***********************************************************************/
|
|
JSONCOL::JSONCOL(JSONCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
|
|
{
|
|
G = col1->G;
|
|
Tjp = col1->Tjp;
|
|
Jpath = col1->Jpath;
|
|
MulVal = col1->MulVal;
|
|
Nodes = col1->Nodes;
|
|
Nod = col1->Nod;
|
|
Sep = col1->Sep;
|
|
Xnod = col1->Xnod;
|
|
Xpd = col1->Xpd;
|
|
Parsed = col1->Parsed;
|
|
Warned = col1->Warned;
|
|
Sgfy = col1->Sgfy;
|
|
} // end of JSONCOL copy constructor
|
|
|
|
/***********************************************************************/
|
|
/* SetBuffer: prepare a column block for write operation. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
|
|
{
|
|
if (DOSCOL::SetBuffer(g, value, ok, check))
|
|
return true;
|
|
|
|
// Parse the json path
|
|
if (ParseJpath(g))
|
|
return true;
|
|
|
|
Tjp = (TDBJSN*)To_Tdb;
|
|
G = Tjp->G;
|
|
return false;
|
|
} // end of SetBuffer
|
|
|
|
/***********************************************************************/
|
|
/* Check whether this object is expanded. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
|
|
{
|
|
if ((Tjp->Xcol && nm && !strcmp(nm, Tjp->Xcol) &&
|
|
(Tjp->Xval < 0 || Tjp->Xval == i)) || Xpd) {
|
|
Xpd = true; // Expandable object
|
|
Nodes[i].Op = OP_EXP;
|
|
} else if (b) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Cannot expand more than one branch");
|
|
return true;
|
|
} // endif Xcol
|
|
|
|
return false;
|
|
} // end of CheckExpand
|
|
|
|
/***********************************************************************/
|
|
/* Analyse array processing options. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
|
|
{
|
|
int n;
|
|
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
|
|
snprintf(g->Message, sizeof(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:
|
|
snprintf(g->Message, sizeof(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 {
|
|
snprintf(g->Message, sizeof(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;
|
|
} // end of SetArrayOptions
|
|
|
|
/***********************************************************************/
|
|
/* Parse the eventual passed Jpath information. */
|
|
/* This information can be specified in the Fieldfmt column option */
|
|
/* when creating the table. It permits to indicate the position of */
|
|
/* the node corresponding to that column. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::ParseJpath(PGLOBAL g)
|
|
{
|
|
char *p, *p1 = NULL, *p2 = NULL, *pbuf = NULL;
|
|
int i;
|
|
bool a;
|
|
|
|
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
|
|
|
|
snprintf(g->Message, sizeof(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 (Xpd && Tjp->Mode == MODE_DELETE) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Cannot delete expanded columns");
|
|
return true;
|
|
} // endif Xpd
|
|
|
|
} 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;
|
|
|
|
fin:
|
|
MulVal = AllocateValue(g, Value);
|
|
Parsed = true;
|
|
return false;
|
|
} // end of ParseJpath
|
|
|
|
/***********************************************************************/
|
|
/* Get Jpath converted to Mongo path. */
|
|
/***********************************************************************/
|
|
PSZ JSONCOL::GetJpath(PGLOBAL g, bool proj)
|
|
{
|
|
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 {
|
|
Sgfy = true;
|
|
return NULL;
|
|
} // endif
|
|
|
|
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 :*
|
|
Sgfy = true;
|
|
break;
|
|
} // endif p2
|
|
/* falls through */
|
|
default:
|
|
*p2++ = *p1;
|
|
break;
|
|
} // endswitch p1;
|
|
}
|
|
|
|
if (*(p2 - 1) == '.')
|
|
p2--;
|
|
|
|
*p2 = 0;
|
|
return mgopath;
|
|
} else
|
|
return NULL;
|
|
|
|
} // end of GetJpath
|
|
|
|
/***********************************************************************/
|
|
/* MakeJson: Serialize the json item and set value to it. */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::MakeJson(PGLOBAL g, PJSON jsp, int n)
|
|
{
|
|
if (Value->IsTypeNum()) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Cannot make Json for a numeric column");
|
|
|
|
if (!Warned) {
|
|
PushWarning(g, Tjp);
|
|
Warned = true;
|
|
} // endif Warned
|
|
|
|
Value->Reset();
|
|
return Value;
|
|
#if 0
|
|
} else if (Value->GetType() == TYPE_BIN) {
|
|
if ((unsigned)Value->GetClen() >= sizeof(BSON)) {
|
|
ulong len = Tjp->Lrecl ? Tjp->Lrecl : 500;
|
|
PBSON bsp = JbinAlloc(g, NULL, len, jsp);
|
|
|
|
safe_strcat(bsp->Msg, sizeof(bsp->Msg), " column");
|
|
((BINVAL*)Value)->SetBinValue(bsp, sizeof(BSON));
|
|
} else {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Column size too small");
|
|
Value->SetValue_char(NULL, 0);
|
|
} // endif Clen
|
|
#endif // 0
|
|
} else if (n < Nod - 1) {
|
|
if (jsp->GetType() == TYPE_JAR) {
|
|
int ars = jsp->GetSize(false);
|
|
PJNODE jnp = &Nodes[n];
|
|
PJAR jvp = new(g) JARRAY;
|
|
|
|
for (jnp->Rank = 0; jnp->Rank < ars; jnp->Rank++)
|
|
jvp->AddArrayValue(g, GetRowValue(g, jsp, n));
|
|
|
|
jnp->Rank = 0;
|
|
jvp->InitArray(g);
|
|
jsp = jvp;
|
|
} else if (jsp->Type == TYPE_JOB) {
|
|
PJOB jvp = new(g) JOBJECT;
|
|
|
|
for (PJPR prp = ((PJOB)jsp)->GetFirst(); prp; prp = prp->Next)
|
|
jvp->SetKeyValue(g, GetRowValue(g, prp->Val, n + 1), prp->Key);
|
|
|
|
jsp = jvp;
|
|
} // endif Type
|
|
|
|
} // endif
|
|
|
|
Value->SetValue_psz(Serialize(g, jsp, NULL, 0));
|
|
return Value;
|
|
} // end of MakeJson
|
|
|
|
/***********************************************************************/
|
|
/* GetRowValue: */
|
|
/***********************************************************************/
|
|
PJVAL JSONCOL::GetRowValue(PGLOBAL g, PJSON row, int i)
|
|
{
|
|
int n = Nod - 1;
|
|
PJVAL val = NULL;
|
|
|
|
for (; i < Nod && row; i++) {
|
|
switch (row->GetType()) {
|
|
case TYPE_JOB:
|
|
val = (Nodes[i].Key) ? ((PJOB)row)->GetKeyValue(Nodes[i].Key) : NULL;
|
|
break;
|
|
case TYPE_JAR:
|
|
val = ((PJAR)row)->GetArrayValue(Nodes[i].Rank);
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->GetType());
|
|
val = NULL;
|
|
} // endswitch Type
|
|
|
|
if (i < Nod-1)
|
|
row = (val) ? val->GetJson() : NULL;
|
|
|
|
} // endfor i
|
|
|
|
return val;
|
|
} // end of GetRowValue
|
|
|
|
/***********************************************************************/
|
|
/* SetValue: Set a value from a JVALUE contains. */
|
|
/***********************************************************************/
|
|
void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL jvp)
|
|
{
|
|
if (jvp) {
|
|
vp->SetNull(false);
|
|
|
|
switch (jvp->GetValType()) {
|
|
case TYPE_STRG:
|
|
case TYPE_INTG:
|
|
case TYPE_BINT:
|
|
case TYPE_DBL:
|
|
case TYPE_DTM:
|
|
switch (vp->GetType()) {
|
|
case TYPE_STRING:
|
|
vp->SetValue_psz(jvp->GetString(g));
|
|
break;
|
|
case TYPE_INT:
|
|
case TYPE_SHORT:
|
|
case TYPE_TINY:
|
|
vp->SetValue(jvp->GetInteger());
|
|
break;
|
|
case TYPE_BIGINT:
|
|
vp->SetValue(jvp->GetBigint());
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
vp->SetValue(jvp->GetFloat());
|
|
|
|
if (jvp->GetValType() == TYPE_DBL)
|
|
vp->SetPrec(jvp->Nd);
|
|
|
|
break;
|
|
case TYPE_DATE:
|
|
if (jvp->GetValType() == TYPE_STRG) {
|
|
PSZ dat = jvp->GetString(g);
|
|
|
|
if (!IsNum(dat)) {
|
|
if (!((DTVAL*)vp)->IsFormatted())
|
|
((DTVAL*)vp)->SetFormat(g, "YYYY-MM-DDThh:mm:ssZ", 20, 0);
|
|
|
|
vp->SetValue_psz(dat);
|
|
} else
|
|
vp->SetValue(atoi(dat));
|
|
|
|
} else
|
|
vp->SetValue(jvp->GetInteger());
|
|
|
|
break;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), "Unsupported column type %d\n", vp->GetType());
|
|
throw 888;
|
|
} // endswitch Type
|
|
|
|
break;
|
|
case TYPE_BOOL:
|
|
if (vp->IsTypeNum())
|
|
vp->SetValue(jvp->GetInteger() ? 1 : 0);
|
|
else
|
|
vp->SetValue_psz((PSZ)(jvp->GetInteger() ? "true" : "false"));
|
|
|
|
break;
|
|
case TYPE_JAR:
|
|
// SetJsonValue(g, vp, val->GetArray()->GetValue(0));
|
|
vp->SetValue_psz(jvp->GetArray()->GetText(g, NULL));
|
|
break;
|
|
case TYPE_JOB:
|
|
// if (!vp->IsTypeNum() || !Strict) {
|
|
vp->SetValue_psz(jvp->GetObject()->GetText(g, NULL));
|
|
break;
|
|
// } // endif Type
|
|
|
|
default:
|
|
vp->Reset();
|
|
vp->SetNull(true);
|
|
} // endswitch Type
|
|
|
|
} else {
|
|
vp->Reset();
|
|
vp->SetNull(true);
|
|
} // endif val
|
|
|
|
} // end of SetJsonValue
|
|
|
|
/***********************************************************************/
|
|
/* ReadColumn: */
|
|
/***********************************************************************/
|
|
void JSONCOL::ReadColumn(PGLOBAL g)
|
|
{
|
|
if (!Tjp->SameRow || Xnod >= Tjp->SameRow)
|
|
Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0));
|
|
|
|
// if (Xpd && Value->IsNull() && !((PJDEF)Tjp->To_Def)->Accept)
|
|
// throw("Null expandable JSON value");
|
|
|
|
// Set null when applicable
|
|
if (!Nullable)
|
|
Value->SetNull(false);
|
|
|
|
} // end of ReadColumn
|
|
|
|
/***********************************************************************/
|
|
/* GetColumnValue: */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i)
|
|
{
|
|
int n = Nod - 1;
|
|
PJAR arp;
|
|
PJVAL val = NULL;
|
|
|
|
for (; i < Nod && row; i++) {
|
|
if (Nodes[i].Op == OP_NUM) {
|
|
Value->SetValue(row->GetType() == TYPE_JAR ? ((PJAR)row)->size() : 1);
|
|
return(Value);
|
|
} else if (Nodes[i].Op == OP_XX) {
|
|
return MakeJson(G, row, i);
|
|
} else switch (row->GetType()) {
|
|
case TYPE_JOB:
|
|
if (!Nodes[i].Key) {
|
|
// Expected Array was not there, wrap the value
|
|
if (i < Nod-1)
|
|
continue;
|
|
else
|
|
val = new(G) JVALUE(row);
|
|
|
|
} else
|
|
val = ((PJOB)row)->GetKeyValue(Nodes[i].Key);
|
|
|
|
break;
|
|
case TYPE_JAR:
|
|
arp = (PJAR)row;
|
|
|
|
if (!Nodes[i].Key) {
|
|
if (Nodes[i].Op == OP_EQ)
|
|
val = arp->GetArrayValue(Nodes[i].Rank);
|
|
else if (Nodes[i].Op == OP_EXP)
|
|
return ExpandArray(g, arp, i);
|
|
else
|
|
return CalculateArray(g, arp, i);
|
|
|
|
} else {
|
|
// Unexpected array, unwrap it as [0]
|
|
val = arp->GetArrayValue(0);
|
|
i--;
|
|
} // endif's
|
|
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
snprintf(g->Message, sizeof(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);
|
|
return Value;
|
|
} // end of GetColumnValue
|
|
|
|
/***********************************************************************/
|
|
/* ExpandArray: */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::ExpandArray(PGLOBAL g, PJAR arp, int n)
|
|
{
|
|
int ars = MY_MIN(Tjp->Limit, arp->size());
|
|
PJVAL jvp;
|
|
JVALUE jval;
|
|
|
|
if (!ars) {
|
|
Value->Reset();
|
|
Value->SetNull(true);
|
|
Tjp->NextSame = 0;
|
|
return Value;
|
|
} // endif ars
|
|
|
|
if (!(jvp = arp->GetArrayValue((Nodes[n].Rx = Nodes[n].Nx)))) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Logical error expanding array");
|
|
throw 666;
|
|
} // endif jvp
|
|
|
|
if (n < Nod - 1 && jvp->GetJson()) {
|
|
jval.SetValue(g, 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;
|
|
} // endif NextSame
|
|
|
|
SetJsonValue(g, Value, jvp);
|
|
return Value;
|
|
} // end of ExpandArray
|
|
|
|
/***********************************************************************/
|
|
/* CalculateArray: */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::CalculateArray(PGLOBAL g, PJAR arp, int n)
|
|
{
|
|
int i, ars, nv = 0, nextsame = Tjp->NextSame;
|
|
bool err;
|
|
OPVAL op = Nodes[n].Op;
|
|
PVAL val[2], vp = Nodes[n].Valp;
|
|
PJVAL jvrp, jvp;
|
|
JVALUE jval;
|
|
|
|
vp->Reset();
|
|
ars = MY_MIN(Tjp->Limit, arp->size());
|
|
|
|
if (trace(1))
|
|
htrc("CalculateArray: size=%d op=%d nextsame=%d\n",
|
|
ars, op, nextsame);
|
|
|
|
for (i = 0; i < ars; i++) {
|
|
jvrp = arp->GetArrayValue(i);
|
|
|
|
if (trace(1))
|
|
htrc("i=%d nv=%d\n", i, nv);
|
|
|
|
if (!jvrp->IsNull() || (op == OP_CNC && GetJsonNull())) do {
|
|
if (jvrp->IsNull()) {
|
|
jvrp->Strp = PlugDup(g, GetJsonNull());
|
|
jvrp->DataType = TYPE_STRG;
|
|
jvp = jvrp;
|
|
} else if (n < Nod - 1 && jvrp->GetJson()) {
|
|
Tjp->NextSame = nextsame;
|
|
jval.SetValue(g, GetColumnValue(g, jvrp->GetJson(), n + 1));
|
|
jvp = &jval;
|
|
} else
|
|
jvp = jvrp;
|
|
|
|
if (trace(1))
|
|
htrc("jvp=%s null=%d\n",
|
|
jvp->GetString(g), jvp->IsNull() ? 1 : 0);
|
|
|
|
if (!nv++) {
|
|
SetJsonValue(g, vp, jvp);
|
|
continue;
|
|
} else
|
|
SetJsonValue(g, MulVal, jvp);
|
|
|
|
if (!MulVal->IsNull()) {
|
|
switch (op) {
|
|
case OP_CNC:
|
|
if (Nodes[n].CncVal) {
|
|
val[0] = Nodes[n].CncVal;
|
|
err = vp->Compute(g, val, 1, op);
|
|
} // endif CncVal
|
|
|
|
val[0] = MulVal;
|
|
err = vp->Compute(g, val, 1, op);
|
|
break;
|
|
// 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();
|
|
|
|
if (trace(1)) {
|
|
char buf(32);
|
|
|
|
htrc("vp='%s' err=%d\n",
|
|
vp->GetCharString(&buf), err ? 1 : 0);
|
|
|
|
} // endif trace
|
|
|
|
} // endif Null
|
|
|
|
} while (Tjp->NextSame > nextsame);
|
|
|
|
} // endfor i
|
|
|
|
if (op == OP_SEP) {
|
|
// Calculate average
|
|
MulVal->SetValue(nv);
|
|
val[0] = vp;
|
|
val[1] = MulVal;
|
|
|
|
if (vp->Compute(g, val, 2, OP_DIV))
|
|
vp->Reset();
|
|
|
|
} // endif Op
|
|
|
|
Tjp->NextSame = nextsame;
|
|
return vp;
|
|
} // end of CalculateArray
|
|
|
|
/***********************************************************************/
|
|
/* GetRow: Get the object containing this column. */
|
|
/***********************************************************************/
|
|
PJSON JSONCOL::GetRow(PGLOBAL g)
|
|
{
|
|
PJVAL val = NULL;
|
|
PJAR arp;
|
|
PJSON nwr, row = Tjp->Row;
|
|
|
|
for (int i = 0; i < Nod && row; i++) {
|
|
if (i < Nod-1 && Nodes[i+1].Op == OP_XX)
|
|
break;
|
|
else switch (row->GetType()) {
|
|
case TYPE_JOB:
|
|
if (!Nodes[i].Key)
|
|
// Expected Array was not there, wrap the value
|
|
continue;
|
|
|
|
val = ((PJOB)row)->GetKeyValue(Nodes[i].Key);
|
|
break;
|
|
case TYPE_JAR:
|
|
arp = (PJAR)row;
|
|
|
|
if (!Nodes[i].Key) {
|
|
if (Nodes[i].Op == OP_EQ)
|
|
val = arp->GetArrayValue(Nodes[i].Rank);
|
|
else
|
|
val = arp->GetArrayValue(Nodes[i].Rx);
|
|
|
|
} else {
|
|
// Unexpected array, unwrap it as [0]
|
|
val = arp->GetArrayValue(0);
|
|
i--;
|
|
} // endif Nodes
|
|
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->GetType());
|
|
val = NULL;
|
|
} // endswitch Type
|
|
|
|
if (val) {
|
|
row = val->GetJson();
|
|
} else {
|
|
// Construct missing objects
|
|
for (i++; row && i < Nod; i++) {
|
|
if (Nodes[i].Op == OP_XX)
|
|
break;
|
|
else if (!Nodes[i].Key)
|
|
// Construct intermediate array
|
|
nwr = new(G) JARRAY;
|
|
else
|
|
nwr = new(G) JOBJECT;
|
|
|
|
if (row->GetType() == TYPE_JOB) {
|
|
((PJOB)row)->SetKeyValue(G, new(G) JVALUE(nwr), Nodes[i-1].Key);
|
|
} else if (row->GetType() == TYPE_JAR) {
|
|
((PJAR)row)->AddArrayValue(G, new(G) JVALUE(nwr));
|
|
((PJAR)row)->InitArray(G);
|
|
} else {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Wrong type when writing new row");
|
|
nwr = NULL;
|
|
} // endif's
|
|
|
|
row = nwr;
|
|
} // endfor i
|
|
|
|
break;
|
|
} // endelse
|
|
|
|
} // endfor i
|
|
|
|
return row;
|
|
} // end of GetRow
|
|
|
|
/***********************************************************************/
|
|
/* WriteColumn: */
|
|
/***********************************************************************/
|
|
void JSONCOL::WriteColumn(PGLOBAL g)
|
|
{
|
|
if (Xpd && Tjp->Pretty < 2) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Cannot write expanded column when Pretty is not 2");
|
|
throw 666;
|
|
} // endif Xpd
|
|
|
|
/*********************************************************************/
|
|
/* 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;
|
|
|
|
char *s;
|
|
PJOB objp = NULL;
|
|
PJAR arp = NULL;
|
|
PJVAL jvp = NULL;
|
|
PJSON jsp, row = GetRow(g);
|
|
|
|
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:
|
|
if (Nodes[Nod-1].Op == OP_XX) {
|
|
s = Value->GetCharValue();
|
|
|
|
if (s && *s) {
|
|
if (!(jsp = ParseJson(G, s, strlen(s)))) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), s);
|
|
throw 666;
|
|
} // endif jsp
|
|
|
|
} else
|
|
jsp = NULL;
|
|
|
|
if (arp) {
|
|
if (Nod > 1 && Nodes[Nod-2].Op == OP_EQ)
|
|
arp->SetArrayValue(G, new(G) JVALUE(jsp), Nodes[Nod-2].Rank);
|
|
else
|
|
arp->AddArrayValue(G, new(G) JVALUE(jsp));
|
|
|
|
arp->InitArray(G);
|
|
} else if (objp) {
|
|
if (Nod > 1 && Nodes[Nod-2].Key)
|
|
objp->SetKeyValue(G, new(G) JVALUE(jsp), Nodes[Nod-2].Key);
|
|
|
|
} else if (jvp)
|
|
jvp->SetValue(jsp);
|
|
|
|
break;
|
|
} // endif Op
|
|
|
|
// fall through
|
|
case TYPE_DATE:
|
|
case TYPE_INT:
|
|
case TYPE_TINY:
|
|
case TYPE_SHORT:
|
|
case TYPE_BIGINT:
|
|
case TYPE_DOUBLE:
|
|
if (arp) {
|
|
if (Nodes[Nod-1].Op == OP_EQ)
|
|
arp->SetArrayValue(G, new(G) JVALUE(G, Value), Nodes[Nod-1].Rank);
|
|
else
|
|
arp->AddArrayValue(G, new(G) JVALUE(G, Value));
|
|
|
|
arp->InitArray(G);
|
|
} else if (objp) {
|
|
if (Nodes[Nod-1].Key)
|
|
objp->SetKeyValue(G, new(G) JVALUE(G, Value), Nodes[Nod-1].Key);
|
|
|
|
} else if (jvp)
|
|
jvp->SetValue(g, Value);
|
|
|
|
break;
|
|
default: // ??????????
|
|
snprintf(g->Message, sizeof(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
|
|
PTDB TDBJSON::Clone(PTABS t)
|
|
{
|
|
PTDB tp;
|
|
PJCOL cp1, cp2;
|
|
PGLOBAL g = t->G;
|
|
|
|
tp = new(g) TDBJSON(this);
|
|
|
|
for (cp1 = (PJCOL)Columns; cp1; cp1 = (PJCOL)cp1->GetNext()) {
|
|
cp2 = new(g) JSONCOL(cp1, tp); // Make a copy
|
|
NewPointer(t, cp1, cp2);
|
|
} // endfor cp1
|
|
|
|
return tp;
|
|
} // end of Clone
|
|
|
|
/***********************************************************************/
|
|
/* Make the document tree from the object path. */
|
|
/***********************************************************************/
|
|
int TDBJSON::MakeNewDoc(PGLOBAL g)
|
|
{
|
|
// Create a void table that will be populated
|
|
Doc = new(g) JARRAY;
|
|
|
|
if (MakeTopTree(g, Doc))
|
|
return RC_FX;
|
|
|
|
Done = true;
|
|
return RC_OK;
|
|
} // end of MakeNewDoc
|
|
|
|
/***********************************************************************/
|
|
/* Make the document tree from a file. */
|
|
/***********************************************************************/
|
|
int TDBJSON::MakeDocument(PGLOBAL g)
|
|
{
|
|
char *p, *p1, *p2, *memory, *objpath, *key = NULL;
|
|
int i = 0;
|
|
size_t len;
|
|
my_bool a;
|
|
MODE mode = Mode;
|
|
PJSON jsp;
|
|
PJOB objp = NULL;
|
|
PJAR arp = NULL;
|
|
PJVAL val = NULL;
|
|
|
|
if (Done)
|
|
return RC_OK;
|
|
|
|
/*********************************************************************/
|
|
/* Create the mapping file object in mode read. */
|
|
/*********************************************************************/
|
|
Mode = MODE_READ;
|
|
|
|
if (!Txfp->OpenTableFile(g)) {
|
|
PFBLOCK fp = Txfp->GetTo_Fb();
|
|
|
|
if (fp) {
|
|
len = fp->Length;
|
|
memory = fp->Memory;
|
|
} else {
|
|
Mode = mode; // Restore saved Mode
|
|
return MakeNewDoc(g);
|
|
} // endif fp
|
|
|
|
} else
|
|
return RC_FX;
|
|
|
|
/*********************************************************************/
|
|
/* Parse the json file and allocate its tree structure. */
|
|
/*********************************************************************/
|
|
g->Message[0] = 0;
|
|
jsp = Top = ParseJson(g, memory, len, &Pretty);
|
|
Txfp->CloseTableFile(g, false);
|
|
Mode = mode; // Restore saved Mode
|
|
|
|
if (!jsp && g->Message[0])
|
|
return RC_FX;
|
|
|
|
if ((objpath = PlugDup(g, Objname))) {
|
|
if (*objpath == '$') objpath++;
|
|
if (*objpath == '.') objpath++;
|
|
p1 = (*objpath == '[') ? objpath++ : NULL;
|
|
|
|
/*********************************************************************/
|
|
/* Find the table in the tree structure. */
|
|
/*********************************************************************/
|
|
for (p = objpath; jsp && p; p = (p2 ? p2 : NULL)) {
|
|
a = (p1 != NULL);
|
|
p1 = strchr(p, '[');
|
|
p2 = strchr(p, '.');
|
|
|
|
if (!p2)
|
|
p2 = p1;
|
|
else if (p1) {
|
|
if (p1 < p2)
|
|
p2 = p1;
|
|
else if (p1 == p2 + 1)
|
|
*p2++ = 0; // Old syntax .[
|
|
else
|
|
p1 = NULL;
|
|
|
|
} // endif p1
|
|
|
|
if (p2)
|
|
*p2++ = 0;
|
|
|
|
if (!a && *p && *p != '[' && !IsNum(p)) {
|
|
// obj is a key
|
|
if (jsp->GetType() != TYPE_JOB) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
|
|
return RC_FX;
|
|
} // endif Type
|
|
|
|
key = p;
|
|
objp = jsp->GetObject();
|
|
arp = NULL;
|
|
val = objp->GetKeyValue(key);
|
|
|
|
if (!val || !(jsp = val->GetJson())) {
|
|
snprintf(g->Message, sizeof(g->Message), "Cannot find object key %s", key);
|
|
return RC_FX;
|
|
} // endif val
|
|
|
|
} else {
|
|
if (*p == '[') {
|
|
// Old style
|
|
if (p[strlen(p) - 1] != ']') {
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid Table path near %s", p);
|
|
return RC_FX;
|
|
} else
|
|
p++;
|
|
|
|
} // endif p
|
|
|
|
if (jsp->GetType() != TYPE_JAR) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
|
|
return RC_FX;
|
|
} // endif Type
|
|
|
|
arp = jsp->GetArray();
|
|
objp = NULL;
|
|
i = atoi(p) - B;
|
|
val = arp->GetArrayValue(i);
|
|
|
|
if (!val) {
|
|
snprintf(g->Message, sizeof(g->Message), "Cannot find array value %d", i);
|
|
return RC_FX;
|
|
} // endif val
|
|
|
|
} // endif
|
|
|
|
jsp = val->GetJson();
|
|
} // endfor p
|
|
|
|
} // endif objpath
|
|
|
|
if (jsp && jsp->GetType() == TYPE_JAR)
|
|
Doc = jsp->GetArray();
|
|
else {
|
|
// The table is void or is just one object or one value
|
|
Doc = new(g) JARRAY;
|
|
|
|
if (val) {
|
|
Doc->AddArrayValue(g, val);
|
|
Doc->InitArray(g);
|
|
} else if (jsp) {
|
|
Doc->AddArrayValue(g, new(g) JVALUE(jsp));
|
|
Doc->InitArray(g);
|
|
} // endif val
|
|
|
|
if (objp)
|
|
objp->SetKeyValue(g, new(g) JVALUE(Doc), key);
|
|
else if (arp)
|
|
arp->SetArrayValue(g, new(g) JVALUE(Doc), i);
|
|
else
|
|
Top = Doc;
|
|
|
|
} // endif jsp
|
|
|
|
Done = true;
|
|
return RC_OK;
|
|
} // end of MakeDocument
|
|
|
|
/***********************************************************************/
|
|
/* JSON Cardinality: returns table size in number of rows. */
|
|
/***********************************************************************/
|
|
int TDBJSON::Cardinality(PGLOBAL g)
|
|
{
|
|
if (!g)
|
|
return (Xcol || Multiple) ? 0 : 1;
|
|
else if (Cardinal < 0)
|
|
{
|
|
if (!Multiple) {
|
|
if (MakeDocument(g) == RC_OK)
|
|
Cardinal = Doc->size();
|
|
|
|
} else
|
|
return 10;
|
|
}
|
|
return Cardinal;
|
|
} // end of Cardinality
|
|
|
|
/***********************************************************************/
|
|
/* JSON GetMaxSize: returns table size estimate in number of rows. */
|
|
/***********************************************************************/
|
|
int TDBJSON::GetMaxSize(PGLOBAL g)
|
|
{
|
|
if (MaxSize < 0)
|
|
MaxSize = Cardinality(g) * ((Xcol) ? Limit : 1);
|
|
|
|
return MaxSize;
|
|
} // end of GetMaxSize
|
|
|
|
/***********************************************************************/
|
|
/* ResetSize: call by TDBMUL when calculating size estimate. */
|
|
/***********************************************************************/
|
|
void TDBJSON::ResetSize(void)
|
|
{
|
|
MaxSize = Cardinal = -1;
|
|
Fpos = -1;
|
|
N = 0;
|
|
Done = false;
|
|
} // end of ResetSize
|
|
|
|
/***********************************************************************/
|
|
/* TDBJSON is not indexable. */
|
|
/***********************************************************************/
|
|
int TDBJSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool)
|
|
{
|
|
if (pxdf) {
|
|
safe_strcpy(g->Message, sizeof(g->Message), "JSON not indexable when pretty = 2");
|
|
return RC_FX;
|
|
} else
|
|
return RC_OK;
|
|
|
|
} // end of MakeIndex
|
|
|
|
/***********************************************************************/
|
|
/* 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. */
|
|
/***********************************************************************/
|
|
bool TDBJSON::SetRecpos(PGLOBAL, int recpos)
|
|
{
|
|
#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
|
|
|
|
/***********************************************************************/
|
|
/* 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:
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid Jmode %d", Jmode);
|
|
return true;
|
|
} // endswitch Jmode
|
|
|
|
if (Xcol)
|
|
To_Filter = NULL; // Imcompatible
|
|
|
|
Use = USE_OPEN;
|
|
return false;
|
|
} // end of OpenDB
|
|
|
|
/***********************************************************************/
|
|
/* ReadDB: Data Base read routine for JSON access method. */
|
|
/***********************************************************************/
|
|
int TDBJSON::ReadDB(PGLOBAL)
|
|
{
|
|
int rc;
|
|
|
|
N++;
|
|
|
|
if (NextSame) {
|
|
SameRow = NextSame;
|
|
NextSame = false;
|
|
M++;
|
|
rc = RC_OK;
|
|
} else if (++Fpos < (signed)Doc->size()) {
|
|
Row = Doc->GetArrayValue(Fpos);
|
|
|
|
if (Row->GetType() == TYPE_JVAL)
|
|
Row = ((PJVAL)Row)->GetJson();
|
|
|
|
SameRow = 0;
|
|
M = 1;
|
|
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->AddArrayValue(g, vp);
|
|
Row = new(g) JOBJECT;
|
|
} else
|
|
Doc->SetArrayValue(g, vp, Fpos);
|
|
|
|
} else if (Jmode == MODE_ARRAY) {
|
|
PJVAL vp = new(g) JVALUE(Row);
|
|
|
|
if (Mode == MODE_INSERT) {
|
|
Doc->AddArrayValue(g, vp);
|
|
Row = new(g) JARRAY;
|
|
} else
|
|
Doc->SetArrayValue(g, vp, Fpos);
|
|
|
|
} else { // if (Jmode == MODE_VALUE)
|
|
if (Mode == MODE_INSERT) {
|
|
Doc->AddArrayValue(g, (PJVAL)Row);
|
|
Row = new(g) JVALUE;
|
|
} else
|
|
Doc->SetArrayValue(g, (PJVAL)Row, Fpos);
|
|
|
|
} // 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)) {
|
|
snprintf(g->Message, sizeof(g->Message), "Value %d does not exist", Fpos + 1);
|
|
return RC_FX;
|
|
} // endif Delete
|
|
|
|
Changed = true;
|
|
} else if (irc == RC_FX)
|
|
// Delete all
|
|
for (int i = 0; i < Doc->size(); i++) {
|
|
Doc->DeleteValue(i);
|
|
Changed = true;
|
|
} // endfor i
|
|
|
|
return RC_OK;
|
|
} // end of DeleteDB
|
|
|
|
/***********************************************************************/
|
|
/* Data Base close routine for JSON access methods. */
|
|
/***********************************************************************/
|
|
void TDBJSON::CloseDB(PGLOBAL g)
|
|
{
|
|
if (!Changed)
|
|
return;
|
|
|
|
// Save the modified document
|
|
char filename[_MAX_PATH];
|
|
|
|
Doc->InitArray(g);
|
|
|
|
// We used the file name relative to recorded datapath
|
|
PlugSetPath(filename, ((PJDEF)To_Def)->Fn, GetPath());
|
|
|
|
// Serialize the modified table
|
|
if (!Serialize(g, Top, filename, Pretty))
|
|
puts(g->Message);
|
|
|
|
} // end of CloseDB
|
|
|
|
/* ---------------------------TDBJCL class --------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* TDBJCL class constructor. */
|
|
/***********************************************************************/
|
|
TDBJCL::TDBJCL(PJDEF tdp) : TDBCAT(tdp)
|
|
{
|
|
Topt = tdp->GetTopt();
|
|
Db = tdp->Schema;
|
|
Dsn = tdp->Uri;
|
|
} // end of TDBJCL constructor
|
|
|
|
/***********************************************************************/
|
|
/* GetResult: Get the list the JSON file columns. */
|
|
/***********************************************************************/
|
|
PQRYRES TDBJCL::GetResult(PGLOBAL g)
|
|
{
|
|
return JSONColumns(g, Db, Dsn, Topt, false);
|
|
} // end of GetResult
|
|
|
|
/* --------------------------- End of json --------------------------- */
|