mariadb/storage/connect/tabbson.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

2631 lines
74 KiB
C++

/************* tabbson C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: tabbson Version 1.2 */
/* (C) Copyright to the author Olivier BERTRAND 2020 - 2021 */
/* This program are the BSON class DB execution routines. */
/***********************************************************************/
/***********************************************************************/
/* Include relevant sections of the MariaDB header file. */
/***********************************************************************/
#include <my_global.h>
/***********************************************************************/
/* Include application header files: */
/* global.h is header containing all global declarations. */
/* plgdbsem.h is header containing the DB application declarations. */
/* tdbdos.h is header containing the TDBDOS declarations. */
/* json.h is header containing the JSON classes declarations. */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "maputil.h"
#include "filamtxt.h"
#include "tabdos.h"
#include "tabbson.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
#include "m_string.h"
/***********************************************************************/
/* 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*);
/***********************************************************************/
/* BSONColumns: construct the result blocks containing the description */
/* of all the columns of a table contained inside a JSON file. */
/***********************************************************************/
PQRYRES BSONColumns(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;
BSONDISC* 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) BSONDISC(g, length);
if (!(n = pjdc->GetColumns(g, db, dsn, topt)))
return NULL;
skipit:
if (trace(1))
htrc("BSONColumns: 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 BSONColumns
/* -------------------------- Class BSONDISC ------------------------- */
/***********************************************************************/
/* Class used to get the columns of a JSON table. */
/***********************************************************************/
BSONDISC::BSONDISC(PGLOBAL g, uint* lg)
{
length = lg;
jcp = fjcp = pjcp = NULL;
tdp = NULL;
tjnp = NULL;
jpp = NULL;
tjsp = NULL;
jsp = NULL;
bp = NULL;
row = NULL;
sep = NULL;
strfy = NULL;
i = n = bf = ncol = lvl = sz = limit = 0;
all = false;
} // end of BSONDISC constructor
int BSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
{
char filename[_MAX_PATH];
bool mgo = (GetTypeID(topt->type) == TAB_MONGO);
PBVAL bdp = NULL;
lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
sep = GetStringTableOption(g, topt, "Separator", ".");
sz = GetIntegerTableOption(g, topt, "Jsize", 1024);
limit = GetIntegerTableOption(g, topt, "Limit", 50);
strfy = GetStringTableOption(g, topt, "Stringify", NULL);
/*********************************************************************/
/* Open the input file. */
/*********************************************************************/
tdp = new(g) BSONDEF;
tdp->G = NULL;
#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);
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) {
tdp->G = g;
if (tdp->Zipped) {
#if defined(ZIP_SUPPORT)
tjsp = new(g) TDBBSON(g, 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) TDBBSON(g, tdp, new(g) MAPFAM(tdp));
if (tjsp->MakeDocument(g))
return 0;
bp = tjsp->Bp;
// bdp = tjsp->GetDoc() ? bp->GetBson(tjsp->GetDoc()) : NULL;
bdp = tjsp->GetDoc();
jsp = bdp ? bp->GetArrayValue(bdp, 0) : NULL;
} else {
if (!((tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))) {
if (!mgo) {
snprintf(g->Message, sizeof(g->Message), "LRECL must be specified for pretty=%d", tdp->Pretty);
return 0;
} else
tdp->Lrecl = 8192; // Should be enough
} // endif Lrecl
// Allocate the parse work memory
tdp->G = PlugInit(NULL, (size_t)tdp->Lrecl * (tdp->Pretty >= 0 ? 4 : 2));
tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF);
if (tdp->Zipped) {
#if defined(ZIP_SUPPORT)
tjnp = new(g)TDBBSN(g, 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) TDBBSN(g, 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) TDBBSN(g, 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) TDBBSN(g, tdp, new(g) CMGFAM(tdp));
#elif defined(JAVA_SUPPORT)
tjnp = new(g) TDBBSN(g, 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) TDBBSN(g, tdp, new(g) DOSFAM(tdp));
else
tjnp = new(g) TDBBSN(g, tdp, new(g) BINFAM(tdp));
tjnp->SetMode(MODE_READ);
bp = tjnp->Bp;
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:
jsp = tjnp->Row;
} // endswitch ReadDB
} // endif pretty
if (!(row = (jsp) ? bp->GetObject(jsp) : 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; jpp; jpp = bp->GetNext(jpp)) {
strncpy(colname, bp->GetKey(jpp), 64);
fmt[bf] = 0;
if (Find(g, bp->GetVlp(jpp), colname, MY_MIN(lvl, 0)))
goto err;
} // endfor jpp
// Missing column can be null
for (jcp = fjcp; jcp; jcp = jcp->Next) {
jcp->Cbn |= !jcp->Found;
jcp->Found = false;
} // endfor jcp
if (tdp->Pretty != 2) {
// Read next record
switch (tjnp->ReadDB(g)) {
case RC_EF:
jsp = NULL;
break;
case RC_FX:
goto err;
default:
jsp = tjnp->Row;
} // endswitch ReadDB
} else
jsp = bp->GetNext(jsp);
if (!(row = (jsp) ? bp->GetObject(jsp) : NULL))
break;
} // endfor i
if (tdp->Pretty != 2)
tjnp->CloseDB(g);
return n;
err:
if (tdp->Pretty != 2)
tjnp->CloseDB(g);
return 0;
} // end of GetColumns
bool BSONDISC::Find(PGLOBAL g, PBVAL jvp, PCSZ key, int j)
{
char *p, *pc = colname + strlen(colname), buf[32];
int ars;
size_t n;
PBVAL job;
PBVAL jar;
if (jvp && !bp->IsJson(jvp)) {
if (JsonAllPath() && !fmt[bf])
safe_strcat(fmt, sizeof(fmt), colname);
jcol.Type = (JTYP)jvp->Type;
switch (jvp->Type) {
case TYPE_STRG:
case TYPE_DTM:
jcol.Len = (int)strlen(bp->GetString(jvp));
break;
case TYPE_INTG:
case TYPE_BINT:
jcol.Len = (int)strlen(bp->GetString(jvp, buf));
break;
case TYPE_DBL:
case TYPE_FLOAT:
jcol.Len = (int)strlen(bp->GetString(jvp, buf));
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->Type == TYPE_NULL;
} else if (!jvp || bp->IsValueNull(jvp)) {
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;
switch (jsp->Type) {
case TYPE_JOB:
job = jsp;
for (PBPR jrp = bp->GetObject(job); jrp; jrp = bp->GetNext(jrp)) {
PCSZ k = bp->GetKey(jrp);
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, bp->GetVlp(jrp), k, j + 1))
return true;
*p = *pc = 0;
} // endfor jrp
return false;
case TYPE_JAR:
jar = jsp;
if (all || (tdp->Xcol && !stricmp(tdp->Xcol, key)))
ars = MY_MIN(bp->GetArraySize(jar), limit);
else
ars = MY_MIN(bp->GetArraySize(jar), 1);
for (int k = 0; k < ars; k++) {
n = sizeof(fmt) - (strlen(fmt) + 1);
if (!tdp->Xcol || stricmp(tdp->Xcol, key)) {
snprintf(buf, sizeof(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, bp->GetArrayValue(jar, 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 BSONDISC::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 BTUTIL --------------------------- */
/***********************************************************************/
/* Find the row in the tree structure. */
/***********************************************************************/
PBVAL BTUTIL::FindRow(PGLOBAL g)
{
char *p, *objpath = PlugDup(g, Tp->Objname);
char *sep = (char*)(Tp->Sep == ':' ? ":[" : ".[");
bool bp = false, b = false;
PBVAL jsp = Tp->Row;
PBVAL 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->Type == TYPE_JOB) ?
GetKeyValue(jsp, objpath) : NULL;
} else {
if (bp || *objpath == '[') { // Old style
if (objpath[strlen(objpath) - 1] != ']') {
snprintf(g->Message, sizeof(g->Message), "Invalid Table path %s", Tp->Objname);
return NULL;
} else if (!bp)
objpath++;
} // endif bp
val = (jsp->Type == TYPE_JAR) ?
GetArrayValue(jsp, atoi(objpath) - Tp->B) : NULL;
} // endif objpath
// jsp = (val) ? val->GetJson() : NULL;
jsp = val;
} // endfor objpath
if (jsp && jsp->Type != TYPE_JOB) {
if (jsp->Type == TYPE_JAR) {
jsp = GetArrayValue(jsp, Tp->B);
if (jsp->Type != TYPE_JOB)
jsp = NULL;
} else
jsp = NULL;
} // endif Type
return jsp;
} // end of FindRow
/***********************************************************************/
/* Parse the read line. */
/***********************************************************************/
PBVAL BTUTIL::ParseLine(PGLOBAL g, int prty, bool cma)
{
pretty = prty;
comma = cma;
return ParseJson(g, Tp->To_Line, strlen(Tp->To_Line));
} // end of ParseLine
/***********************************************************************/
/* Make the top tree from the object path. */
/***********************************************************************/
PBVAL BTUTIL::MakeTopTree(PGLOBAL g, int type)
{
PBVAL top = NULL, val = NULL;
if (Tp->Objname) {
if (!Tp->Row) {
// Parse and allocate Objpath item(s)
char *p, *objpath = PlugDup(g, Tp->Objname);
char *sep = (char*)(Tp->Sep == ':' ? ":[" : ".[");
int i;
bool bp = false, b = false;
PBVAL objp = NULL;
PBVAL arp = NULL;
for (; 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
objp = NewVal(TYPE_JOB);
if (!top)
top = objp;
if (val)
SetValueObj(val, objp);
val = NewVal();
SetKeyValue(objp, MOF(val), objpath);
} else {
if (bp || *objpath == '[') {
// Old style
if (objpath[strlen(objpath) - 1] != ']') {
snprintf(g->Message, sizeof(g->Message), "Invalid Table path %s", Tp->Objname);
return NULL;
} else if (!bp)
objpath++;
} // endif bp
if (!top)
top = NewVal(TYPE_JAR);
if (val)
SetValueArr(val, arp);
val = NewVal();
i = atoi(objpath) - Tp->B;
SetArrayValue(arp, val, i);
} // endif objpath
} // endfor p
} // endif Val
Tp->Row = val;
if (Tp->Row) Tp->Row->Type = type;
} else
top = Tp->Row = NewVal(type);
return top;
} // end of MakeTopTree
PSZ BTUTIL::SerialVal(PGLOBAL g, PBVAL vlp, int pretty)
{
return Serialize(g, vlp, NULL, pretty);
} // en of SerialTop
/* -------------------------- Class BCUTIL --------------------------- */
/***********************************************************************/
/* SetValue: Set a value from a BVALUE contains. */
/***********************************************************************/
void BCUTIL::SetJsonValue(PGLOBAL g, PVAL vp, PBVAL jvp)
{
if (jvp) {
vp->SetNull(false);
if (Jb) {
vp->SetValue_psz(Serialize(g, jvp, NULL, 0));
Jb = false;
} else switch (jvp->Type) {
case TYPE_STRG:
case TYPE_INTG:
case TYPE_BINT:
case TYPE_DBL:
case TYPE_DTM:
case TYPE_FLOAT:
switch (vp->GetType()) {
case TYPE_STRING:
case TYPE_DECIM:
vp->SetValue_psz(GetString(jvp));
break;
case TYPE_INT:
case TYPE_SHORT:
case TYPE_TINY:
vp->SetValue(GetInteger(jvp));
break;
case TYPE_BIGINT:
vp->SetValue(GetBigint(jvp));
break;
case TYPE_DOUBLE:
vp->SetValue(GetDouble(jvp));
if (jvp->Type == TYPE_DBL || jvp->Type == TYPE_FLOAT)
vp->SetPrec(jvp->Nd);
break;
case TYPE_DATE:
if (jvp->Type == TYPE_STRG) {
PSZ dat = GetString(jvp);
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(GetInteger(jvp));
break;
default:
snprintf(G->Message, sizeof(G->Message), "Unsupported column type %d", vp->GetType());
throw 888;
} // endswitch Type
break;
case TYPE_BOOL:
if (vp->IsTypeNum())
vp->SetValue(GetInteger(jvp) ? 1 : 0);
else
vp->SetValue_psz((PSZ)(GetInteger(jvp) ? "true" : "false"));
break;
case TYPE_JAR:
case TYPE_JOB:
// SetJsonValue(g, vp, val->GetArray()->GetValue(0));
vp->SetValue_psz(GetValueText(g, jvp, NULL));
break;
default:
vp->Reset();
vp->SetNull(true);
} // endswitch Type
} else {
vp->Reset();
vp->SetNull(true);
} // endif val
} // end of SetJsonValue
/***********************************************************************/
/* MakeJson: Serialize the json item and set value to it. */
/***********************************************************************/
PBVAL BCUTIL::MakeBson(PGLOBAL g, PBVAL jsp, int n)
{
PBVAL vlp, jvp = jsp;
if (n < Cp->Nod - 1) {
if (jsp->Type == TYPE_JAR) {
int ars = GetArraySize(jsp);
PJNODE jnp = &Cp->Nodes[n];
jvp = NewVal(TYPE_JAR);
jnp->Op = OP_EQ;
for (int i = 0; i < ars; i++) {
jnp->Rank = i;
vlp = GetRowValue(g, jsp, n);
AddArrayValue(jvp,DupVal(vlp));
} // endfor i
jnp->Op = OP_XX;
jnp->Rank = 0;
} else if (jsp->Type == TYPE_JOB) {
jvp = NewVal(TYPE_JOB);
for (PBPR prp = GetObject(jsp); prp; prp = GetNext(prp)) {
vlp = GetRowValue(g, GetVlp(prp), n + 1);
SetKeyValue(jvp, vlp, MZP(prp->Key));
} // endfor prp
} // endif Type
} // endif's
Jb = true;
return jvp;
} // end of MakeBson
/***********************************************************************/
/* GetRowValue: */
/***********************************************************************/
PBVAL BCUTIL::GetRowValue(PGLOBAL g, PBVAL row, int i)
{
int nod = Cp->Nod;
JNODE *nodes = Cp->Nodes;
PBVAL arp;
PBVAL bvp = NULL;
for (; i < nod && row; i++) {
if (nodes[i].Op == OP_NUM) {
bvp = NewVal(TYPE_INT);
bvp->N = (row->Type == TYPE_JAR) ? GetSize(row) : 1;
return(bvp);
} else if (nodes[i].Op == OP_XX) {
return MakeBson(g, row, i);
} else switch (row->Type) {
case TYPE_JOB:
if (!nodes[i].Key) {
// Expected Array was not there, wrap the value
if (i < nod - 1)
continue;
else
bvp = row;
} else
bvp = GetKeyValue(row, nodes[i].Key);
break;
case TYPE_JAR:
arp = row;
if (!nodes[i].Key) {
if (nodes[i].Op == OP_EQ)
bvp = GetArrayValue(arp, nodes[i].Rank);
else if (nodes[i].Op == OP_EXP)
return NewVal(ExpandArray(g, arp, i));
else
return NewVal(CalculateArray(g, arp, i));
} else {
// Unexpected array, unwrap it as [0]
bvp = GetArrayValue(arp, 0);
i--;
} // endif's
break;
case TYPE_JVAL:
bvp = row;
break;
default:
snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->Type);
bvp = NULL;
} // endswitch Type
if (i < nod - 1)
row = bvp;
} // endfor i
return bvp;
} // end of GetRowValue
/***********************************************************************/
/* GetColumnValue: */
/***********************************************************************/
PVAL BCUTIL::GetColumnValue(PGLOBAL g, PBVAL row, int i)
{
PVAL value = Cp->Value;
PBVAL bvp = GetRowValue(g, row, i);
SetJsonValue(g, value, bvp);
return value;
} // end of GetColumnValue
/***********************************************************************/
/* ExpandArray: */
/***********************************************************************/
PVAL BCUTIL::ExpandArray(PGLOBAL g, PBVAL arp, int n)
{
int nod = Cp->Nod, ars = MY_MIN(Tp->Limit, GetArraySize(arp));
JNODE *nodes = Cp->Nodes;
PVAL value = Cp->Value;
PBVAL bvp;
BVAL bval;
if (!ars) {
value->Reset();
value->SetNull(true);
Tp->NextSame = 0;
return value;
} // endif ars
if (!(bvp = GetArrayValue(arp, (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 && IsJson(bvp)) {
SetValue(&bval, GetColumnValue(g, bvp, n + 1));
bvp = &bval;
} // endif n
if (n >= Tp->NextSame) {
if (++nodes[n].Nx == ars) {
nodes[n].Nx = 0;
Cp->Xnod = 0;
} else
Cp->Xnod = n;
Tp->NextSame = Cp->Xnod;
} // endif NextSame
SetJsonValue(g, value, bvp);
return value;
} // end of ExpandArray
/***********************************************************************/
/* CalculateArray: */
/***********************************************************************/
PVAL BCUTIL::CalculateArray(PGLOBAL g, PBVAL arp, int n)
{
int i, ars, nv = 0, nextsame = Tp->NextSame;
bool err;
int nod = Cp->Nod;
JNODE *nodes = Cp->Nodes;
OPVAL op = nodes[n].Op;
PVAL val[2], vp = nodes[n].Valp, mulval = Cp->MulVal;
PBVAL jvrp, jvp;
BVAL jval;
vp->Reset();
ars = MY_MIN(Tp->Limit, GetArraySize(arp));
xtrc(1,"CalculateArray: size=%d op=%d nextsame=%d\n", ars, op, nextsame);
for (i = 0; i < ars; i++) {
jvrp = GetArrayValue(arp, i);
xtrc(1, "i=%d nv=%d\n", i, nv);
if (!IsValueNull(jvrp) || (op == OP_CNC && GetJsonNull())) do {
if (IsValueNull(jvrp)) {
SetString(jvrp, PlugDup(G, GetJsonNull()));
jvp = jvrp;
} else if (n < nod - 1 && IsJson(jvrp)) {
Tp->NextSame = nextsame;
SetValue(&jval, GetColumnValue(g, jvrp, n + 1));
jvp = &jval;
} else
jvp = jvrp;
xtrc(1, "jvp=%s null=%d\n", GetString(jvp), IsValueNull(jvp) ? 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 (Tp->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
Tp->NextSame = nextsame;
return vp;
} // end of CalculateArray
/***********************************************************************/
/* GetRow: Get the object containing this column. */
/***********************************************************************/
PBVAL BCUTIL::GetRow(PGLOBAL g)
{
int nod = Cp->Nod;
JNODE *nodes = Cp->Nodes;
PBVAL val = NULL;
PBVAL arp;
PBVAL nwr, row = Tp->Row;
for (int i = 0; i < nod && row; i++) {
if (i < nod-1 && nodes[i+1].Op == OP_XX)
break;
else switch (row->Type) {
case TYPE_JOB:
if (!nodes[i].Key)
// Expected Array was not there, wrap the value
continue;
val = GetKeyValue(row, nodes[i].Key);
break;
case TYPE_JAR:
arp = row;
if (!nodes[i].Key) {
if (nodes[i].Op == OP_EQ)
val = GetArrayValue(arp, nodes[i].Rank);
else
val = GetArrayValue(arp, nodes[i].Rx);
} else {
// Unexpected array, unwrap it as [0]
val = GetArrayValue(arp, 0);
i--;
} // endif Nodes
break;
case TYPE_JVAL:
val = row;
break;
default:
snprintf(g->Message, sizeof(g->Message), "Invalid row JSON type %d", row->Type);
val = NULL;
} // endswitch Type
if (val) {
row = val;
} else {
// Construct missing objects
for (i++; row && i < nod; i++) {
int type;
if (nodes[i].Op == OP_XX)
break;
else if (!nodes[i].Key)
// Construct intermediate array
type = TYPE_JAR;
else
type = TYPE_JOB;
if (row->Type == TYPE_JOB) {
nwr = AddPair(row, nodes[i - 1].Key, type);
} else if (row->Type == TYPE_JAR) {
AddArrayValue(row, (nwr = NewVal(type)));
} 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
/* -------------------------- Class BSONDEF -------------------------- */
BSONDEF::BSONDEF(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 BSONDEF constructor
/***********************************************************************/
/* DefineAM: define specific AM block values. */
/***********************************************************************/
bool BSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
G = g;
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 BSONDEF::GetTable(PGLOBAL g, MODE m)
{
if (trace(1))
htrc("BSON GetTable Pretty=%d Uri=%s\n", Pretty, SVP(Uri));
if (Catfunc == FNC_COL)
return new(g)TDBBCL(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 (Lrecl) {
// Allocate the parse work memory
G = PlugInit(NULL, (size_t)Lrecl * (Pretty < 0 ? 3 : 5));
} else {
safe_strcpy(g->Message, sizeof(g->Message), "LRECL is not defined");
return NULL;
} // endif Lrecl
if (Pretty < 0) { // BJsonfile
txfp = new(g) BINFAM(this);
} else 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
txfp = new(g) DOSFAM(this);
// Txfp must be set for TDBBSN
tdbp = new(g) TDBBSN(g, this, txfp);
} 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) TDBBSON(g, this, txfp);
} // endif Pretty
if (Multiple)
tdbp = new(g) TDBMUL(tdbp);
return tdbp;
} // end of GetTable
/* --------------------------- Class TDBBSN -------------------------- */
/***********************************************************************/
/* Implementation of the TDBBSN class (Pretty < 2) */
/***********************************************************************/
TDBBSN::TDBBSN(PGLOBAL g, PBDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
{
Bp = new(g) BTUTIL(tdp->G, this);
Top = NULL;
Row = 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;
Bp->SetPretty(Pretty);
} // end of TDBBSN standard constructor
TDBBSN::TDBBSN(TDBBSN* tdbp) : TDBDOS(NULL, tdbp)
{
Bp = tdbp->Bp;
Top = tdbp->Top;
Row = tdbp->Row;
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 TDBBSN copy constructor
// Used for update
PTDB TDBBSN::Clone(PTABS t)
{
PTDB tp;
PBSCOL cp1, cp2;
PGLOBAL g = t->G;
tp = new(g) TDBBSN(this);
for (cp1 = (PBSCOL)Columns; cp1; cp1 = (PBSCOL)cp1->GetNext()) {
cp2 = new(g) BSONCOL(cp1, tp); // Make a copy
NewPointer(t, cp1, cp2);
} // endfor cp1
return tp;
} // end of Clone
/***********************************************************************/
/* Allocate JSN column description block. */
/***********************************************************************/
PCOL TDBBSN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
{
PBSCOL colp = new(g) BSONCOL(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 TDBBSN::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
/***********************************************************************/
/* JSON Cardinality: returns table size in number of rows. */
/***********************************************************************/
int TDBBSN::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 TDBBSN::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0)
MaxSize = TDBDOS::GetMaxSize(g) * ((Xcol) ? Limit : 1);
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* JSON EstimatedLength. Returns an estimated minimum line length. */
/***********************************************************************/
int TDBBSN::EstimatedLength(void)
{
if (AvgLen <= 0)
return (Lrecl ? Lrecl : 1024) / 8; // TODO: make it better
else
return AvgLen;
} // end of Estimated Length
/***********************************************************************/
/* OpenDB: Data Base open routine for BSN access method. */
/***********************************************************************/
bool TDBBSN::OpenDB(PGLOBAL g)
{
TUSE use = Use;
if (Use == USE_OPEN) {
/*******************************************************************/
/* Table already open replace it at its beginning. ??? */
/*******************************************************************/
Fpos = -1;
NextSame = 0;
SameRow = 0;
} // endif Use
/*********************************************************************/
/* Open according to logical input/output mode required. */
/*********************************************************************/
if (TDBDOS::OpenDB(g))
return true;
if (use == USE_OPEN)
return false;
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);
// Lrecl is Ok
size_t linelen = Lrecl;
MODE mode = Mode;
// Buffer must be allocated in G->Sarea
Mode = MODE_ANY;
Txfp->AllocateBuffer(Bp->G);
Mode = mode;
if (Mode == MODE_INSERT)
Bp->SubSet(true);
else
Bp->MemSave();
To_Line = Txfp->GetBuf();
memset(To_Line, 0, linelen);
xtrc(1, "OpenJSN: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
} // endif Pretty
/***********************************************************************/
/* First opening. */
/***********************************************************************/
if (Mode == MODE_INSERT) {
int type;
switch (Jmode) {
case MODE_OBJECT: type = TYPE_JOB; break;
case MODE_ARRAY: type = TYPE_JAR; break;
case MODE_VALUE: type = TYPE_JVAL; break;
default:
snprintf(g->Message, sizeof(g->Message), "Invalid Jmode %d", Jmode);
return true;
} // endswitch Jmode
Top = Bp->MakeTopTree(g, type);
Bp->MemSave();
} // endif Mode
if (Xcol)
To_Filter = NULL; // Not compatible
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 TDBBSN::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 TDBBSN::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
Bp->SubSet();
if ((Row = Bp->ParseLine(g, Pretty, Comma))) {
Top = Row;
Row = Bp->FindRow(g);
SameRow = 0;
Fpos++;
M = 1;
rc = RC_OK;
} else if (Pretty != 1 || strcmp(To_Line, "]")) {
Bp->GetMsg(g);
rc = RC_FX;
} else
rc = RC_EF;
} else { // Here we get a movable Json binary tree
Bp->MemSet(((BINFAM*)Txfp)->Recsize); // Useful when updating
Row = Top = (PBVAL)To_Line;
Row = Bp->FindRow(g);
SameRow = 0;
Fpos++;
M = 1;
rc = RC_OK;
} // endif Pretty
} // endif ReadDB
return rc;
} // end of ReadDB
/***********************************************************************/
/* PrepareWriting: Prepare the line for WriteDB. */
/***********************************************************************/
bool TDBBSN::PrepareWriting(PGLOBAL g)
{
if (Pretty >= 0) {
PSZ s;
// if (!(Top = Bp->MakeTopTree(g, Row->Type)))
// return true;
if ((s = Bp->SerialVal(g, Top, 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;
} else
((BINFAM*)Txfp)->Recsize = ((size_t)PlugSubAlloc(Bp->G, NULL, 0)
- (size_t)To_Line);
return false;
} // end of PrepareWriting
/***********************************************************************/
/* WriteDB: Data Base write routine for JSON access method. */
/***********************************************************************/
int TDBBSN::WriteDB(PGLOBAL g) {
int rc = TDBDOS::WriteDB(g);
Bp->SubSet();
Bp->Clear(Row);
return rc;
} // end of WriteDB
/***********************************************************************/
/* Data Base close routine for JSON access method. */
/***********************************************************************/
void TDBBSN::CloseDB(PGLOBAL g)
{
TDBDOS::CloseDB(g);
Bp->G = PlugExit(Bp->G);
} // end of CloseDB
/* ---------------------------- BSONCOL ------------------------------ */
/***********************************************************************/
/* BSONCOL public constructor. */
/***********************************************************************/
BSONCOL::BSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
: DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
{
Tbp = (TDBBSN*)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
Cp = new(g) BCUTIL(((PBDEF)Tbp->To_Def)->G, this, Tbp);
Jpath = cdp->GetFmt();
MulVal = NULL;
Nodes = NULL;
Nod = 0;
Sep = Tbp->Sep;
Xnod = -1;
Xpd = false;
Parsed = false;
Warned = false;
Sgfy = false;
} // end of BSONCOL constructor
/***********************************************************************/
/* BSONCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
BSONCOL::BSONCOL(BSONCOL* col1, PTDB tdbp) : DOSCOL(col1, tdbp)
{
Tbp = col1->Tbp;
Cp = col1->Cp;
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 BSONCOL copy constructor
/***********************************************************************/
/* SetBuffer: prepare a column block for write operation. */
/***********************************************************************/
bool BSONCOL::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;
Tbp = (TDBBSN*)To_Tdb;
return false;
} // end of SetBuffer
/***********************************************************************/
/* Check whether this object is expanded. */
/***********************************************************************/
bool BSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
{
if ((Tbp->Xcol && nm && !strcmp(nm, Tbp->Xcol) &&
(Tbp->Xval < 0 || Tbp->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 BSONCOL::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 = Tbp->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) - Tbp->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 (!Tbp->Xcol && nm) {
Xpd = true;
jnp->Op = OP_EXP;
Tbp->Xval = i;
Tbp->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 BSONCOL::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 (PBSCOL colp = (PBSCOL)Tbp->GetColumns(); colp;
colp = (PBSCOL)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 && Tbp->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 BSONCOL::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
default:
*p2++ = *p1;
break;
} // endswitch p1;
if (*(p2 - 1) == '.')
p2--;
*p2 = 0;
return mgopath;
} else
return NULL;
} // end of GetJpath
/***********************************************************************/
/* ReadColumn: */
/***********************************************************************/
void BSONCOL::ReadColumn(PGLOBAL g)
{
if (!Tbp->SameRow || Xnod >= Tbp->SameRow)
Value->SetValue_pval(Cp->GetColumnValue(g, Tbp->Row, 0));
#if defined(DEVELOPMENT)
if (Xpd && Value->IsNull() && !((PBDEF)Tbp->To_Def)->Accept)
htrc("Null expandable JSON value for column %s\n", Name);
#endif // DEVELOPMENT
// Set null when applicable
if (!Nullable)
Value->SetNull(false);
} // end of ReadColumn
/***********************************************************************/
/* WriteColumn: */
/***********************************************************************/
void BSONCOL::WriteColumn(PGLOBAL g)
{
if (Xpd && Tbp->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() && Tbp->Mode == MODE_INSERT)
return;
PBVAL jsp, row = Cp->GetRow(g);
if (row) switch (Buf_Type) {
case TYPE_STRING:
case TYPE_DATE:
case TYPE_INT:
case TYPE_TINY:
case TYPE_SHORT:
case TYPE_BIGINT:
case TYPE_DOUBLE:
if (Buf_Type == TYPE_STRING && Nodes[Nod - 1].Op == OP_XX) {
char *s = Value->GetCharValue();
if (!(jsp = Cp->ParseJson(g, s, strlen(s)))) {
safe_strcpy(g->Message, sizeof(g->Message), s);
throw 666;
} // endif jsp
switch (row->Type) {
case TYPE_JAR:
if (Nod > 1 && Nodes[Nod - 2].Op == OP_EQ)
Cp->SetArrayValue(row, jsp, Nodes[Nod - 2].Rank);
else
Cp->AddArrayValue(row, jsp);
break;
case TYPE_JOB:
if (Nod > 1 && Nodes[Nod - 2].Key)
Cp->SetKeyValue(row, jsp, Nodes[Nod - 2].Key);
break;
case TYPE_JVAL:
default:
Cp->SetValueVal(row, jsp);
} // endswitch Type
break;
} else
jsp = Cp->NewVal(Value);
switch (row->Type) {
case TYPE_JAR:
if (Nodes[Nod - 1].Op == OP_EQ)
Cp->SetArrayValue(row, jsp, Nodes[Nod - 1].Rank);
else
Cp->AddArrayValue(row, jsp);
break;
case TYPE_JOB:
if (Nodes[Nod - 1].Key)
Cp->SetKeyValue(row, jsp, Nodes[Nod - 1].Key);
break;
case TYPE_JVAL:
default:
Cp->SetValueVal(row, jsp);
} // endswitch Type
break;
default: // ??????????
snprintf(g->Message, sizeof(g->Message), "Invalid column type %d", Buf_Type);
} // endswitch Type
} // end of WriteColumn
/* -------------------------- Class TDBBSON -------------------------- */
/***********************************************************************/
/* Implementation of the TDBBSON class. */
/***********************************************************************/
TDBBSON::TDBBSON(PGLOBAL g, PBDEF tdp, PTXF txfp) : TDBBSN(g, tdp, txfp)
{
Docp = NULL;
Docrow = NULL;
Multiple = tdp->Multiple;
Docsize = 0;
Done = Changed = false;
Bp->SetPretty(2);
} // end of TDBBSON standard constructor
TDBBSON::TDBBSON(PBTDB tdbp) : TDBBSN(tdbp)
{
Docp = tdbp->Docp;
Docrow = tdbp->Docrow;
Multiple = tdbp->Multiple;
Docsize = tdbp->Docsize;
Done = tdbp->Done;
Changed = tdbp->Changed;
} // end of TDBBSON copy constructor
// Used for update
PTDB TDBBSON::Clone(PTABS t)
{
PTDB tp;
PBSCOL cp1, cp2;
PGLOBAL g = t->G;
tp = new(g) TDBBSON(this);
for (cp1 = (PBSCOL)Columns; cp1; cp1 = (PBSCOL)cp1->GetNext()) {
cp2 = new(g) BSONCOL(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 TDBBSON::MakeNewDoc(PGLOBAL g)
{
// Create a void table that will be populated
Docp = Bp->NewVal(TYPE_JAR);
if (!(Top = Bp->MakeTopTree(g, TYPE_JAR)))
return RC_FX;
Docp = Row;
Done = true;
return RC_OK;
} // end of MakeNewDoc
/***********************************************************************/
/* Make the document tree from a file. */
/***********************************************************************/
int TDBBSON::MakeDocument(PGLOBAL g)
{
char *p, *p1, *p2, *memory, *objpath, *key = NULL;
int i = 0;
size_t len;
my_bool a;
MODE mode = Mode;
PBVAL jsp;
PBVAL objp = NULL;
PBVAL arp = NULL;
PBVAL 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 = Bp->ParseJson(g, memory, len);
Txfp->CloseTableFile(g, false);
Mode = mode; // Restore saved Mode
if (!jsp && g->Message[0])
return RC_FX;
if ((objpath = PlugDup(g, Objname))) {
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->Type != 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;
arp = NULL;
val = Bp->GetKeyValue(objp, key);
if (!val || !(jsp = Bp->GetBson(val))) {
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->Type != TYPE_JAR) {
safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
return RC_FX;
} // endif Type
arp = jsp;
objp = NULL;
i = atoi(p) - B;
val = Bp->GetArrayValue(arp, i);
if (!val) {
snprintf(g->Message, sizeof(g->Message), "Cannot find array value %d", i);
return RC_FX;
} // endif val
} // endif
jsp = val;
} // endfor p
} // endif objpath
if (jsp && jsp->Type == TYPE_JAR)
Docp = jsp;
else {
// The table is void or is just one object or one value
if (objp) {
Docp = Bp->GetKeyValue(objp, key);
Docp->To_Val = Bp->MOF(Bp->DupVal(Docp));
Docp->Type = TYPE_JAR;
} else if (arp) {
Docp = Bp->NewVal(TYPE_JAR);
Bp->AddArrayValue(Docp, jsp);
Bp->SetArrayValue(arp, Docp, i);
} else {
Top = Docp = Bp->NewVal(TYPE_JAR);
Bp->AddArrayValue(Docp, jsp);
} // endif's
} // endif jsp
Docsize = Bp->GetSize(Docp);
Done = true;
return RC_OK;
} // end of MakeDocument
/***********************************************************************/
/* JSON Cardinality: returns table size in number of rows. */
/***********************************************************************/
int TDBBSON::Cardinality(PGLOBAL g)
{
if (!g)
return (Xcol || Multiple) ? 0 : 1;
else if (Cardinal < 0) {
if (!Multiple) {
if (MakeDocument(g) == RC_OK)
Cardinal = Docsize;
} else
return 10;
} // endif Cardinal
return Cardinal;
} // end of Cardinality
/***********************************************************************/
/* JSON GetMaxSize: returns table size estimate in number of rows. */
/***********************************************************************/
int TDBBSON::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 TDBBSON::ResetSize(void)
{
MaxSize = Cardinal = -1;
Fpos = -1;
N = 0;
Docrow = NULL;
Done = false;
} // end of ResetSize
/***********************************************************************/
/* TDBBSON is not indexable. */
/***********************************************************************/
int TDBBSON::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 TDBBSON::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 TDBBSON::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;
Docrow = NULL;
return false;
} // end of SetRecpos
/***********************************************************************/
/* JSON Access Method opening routine. */
/***********************************************************************/
bool TDBBSON::OpenDB(PGLOBAL g)
{
if (Use == USE_OPEN) {
/*******************************************************************/
/* Table already open replace it at its beginning. */
/*******************************************************************/
Fpos = -1;
NextSame = false;
SameRow = 0;
Docrow = NULL;
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 = Bp->NewVal(TYPE_JOB); break;
case MODE_ARRAY: Row = Bp->NewVal(TYPE_JAR); break;
case MODE_VALUE: Row = Bp->NewVal(TYPE_JVAL); 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 TDBBSON::ReadDB(PGLOBAL)
{
int rc;
N++;
if (NextSame) {
SameRow = NextSame;
NextSame = false;
M++;
rc = RC_OK;
} else if (++Fpos < Docsize) {
Docrow = (Docrow) ? Bp->GetNext(Docrow) : Bp->GetArrayValue(Docp, Fpos);
Row = (Docrow->Type == TYPE_JVAL) ? Bp->GetBson(Docrow) : Docrow;
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 TDBBSON::WriteDB(PGLOBAL g)
{
if (Mode == MODE_INSERT) {
Bp->AddArrayValue(Docp, Row);
switch(Jmode) {
case MODE_OBJECT: Row = Bp->NewVal(TYPE_JOB); break;
case MODE_ARRAY: Row = Bp->NewVal(TYPE_JAR); break;
default: Row = Bp->NewVal(); break;
} // endswitch Jmode
} else
Bp->SetArrayValue(Docp, Row, Fpos);
Changed = true;
return RC_OK;
} // end of WriteDB
/***********************************************************************/
/* Data Base delete line routine for JSON access method. */
/***********************************************************************/
int TDBBSON::DeleteDB(PGLOBAL g, int irc)
{
if (irc == RC_OK)
// Deleted current row
Bp->DeleteValue(Docp, Fpos);
else if (irc == RC_FX)
// Delete all
Docp->To_Val = 0;
Changed = true;
return RC_OK;
} // end of DeleteDB
/***********************************************************************/
/* Data Base close routine for JSON access methods. */
/***********************************************************************/
void TDBBSON::CloseDB(PGLOBAL g)
{
if (!Changed)
return;
// Save the modified document
char filename[_MAX_PATH];
//Docp->InitArray(g);
// We used the file name relative to recorded datapath
PlugSetPath(filename, ((PBDEF)To_Def)->Fn, GetPath());
// Serialize the modified table
if (!Bp->Serialize(g, Top, filename, Pretty))
puts(g->Message);
} // end of CloseDB
/* ---------------------------TDBBCL class --------------------------- */
/***********************************************************************/
/* TDBBCL class constructor. */
/***********************************************************************/
TDBBCL::TDBBCL(PBDEF tdp) : TDBCAT(tdp) {
Topt = tdp->GetTopt();
Db = tdp->Schema;
Dsn = tdp->Uri;
} // end of TDBBCL constructor
/***********************************************************************/
/* GetResult: Get the list the JSON file columns. */
/***********************************************************************/
PQRYRES TDBBCL::GetResult(PGLOBAL g) {
return BSONColumns(g, Db, Dsn, Topt, false);
} // end of GetResult
/* --------------------------- End of json --------------------------- */