mariadb/storage/connect/tabjson.cpp
Mikhail Chalov fc6e8a3d32 Minimize unsafe C functions usage - replace strcat() and strcpy()
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
2023-04-20 15:27:20 +01:00

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