mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
fc6e8a3d32
Similar to 567b6812
continue to replace use of strcat() and
strcpy() with safer options strncat() and strncpy().
All new code of the whole pull request, including one or several files
that are either new files or modified ones, are contributed under the BSD-new
license. I am contributing on behalf of my employer Amazon Web Services
2631 lines
74 KiB
C++
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 --------------------------- */
|