Commit 27323311 authored by Olivier Bertrand's avatar Olivier Bertrand

- Fix a bug causing CONNECT to loop when expanding a JSON column

  when the expanded column value is null or void array.
- Adding the FullArray option to JSON tables.
- Skipping expanded JSON lines when the expanded column value is null.
  modified:   storage/connect/connect.cc
  modified:   storage/connect/tabdos.h
  modified:   storage/connect/tabjson.cpp
  modified:   storage/connect/tabjson.h
parent efe80675
...@@ -404,6 +404,7 @@ RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr) ...@@ -404,6 +404,7 @@ RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr)
rc = RC_FX; rc = RC_FX;
} catch (const char *msg) { } catch (const char *msg) {
strcpy(g->Message, msg); strcpy(g->Message, msg);
rc = RC_NF;
} // end catch } // end catch
return rc; return rc;
......
...@@ -29,6 +29,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ ...@@ -29,6 +29,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
friend class TXTFAM; friend class TXTFAM;
friend class DBFBASE; friend class DBFBASE;
friend class UNZIPUTL; friend class UNZIPUTL;
friend class JSONCOL;
public: public:
// Constructor // Constructor
DOSDEF(void); DOSDEF(void);
......
...@@ -54,16 +54,16 @@ ...@@ -54,16 +54,16 @@
USETEMP UseTemp(void); USETEMP UseTemp(void);
char *GetJsonNull(void); char *GetJsonNull(void);
typedef struct _jncol { //typedef struct _jncol {
struct _jncol *Next; // struct _jncol *Next;
char *Name; // char *Name;
char *Fmt; // char *Fmt;
int Type; // int Type;
int Len; // int Len;
int Scale; // int Scale;
bool Cbn; // bool Cbn;
bool Found; // bool Found;
} JCOL, *PJCL; //} JCOL, *PJCL;
/***********************************************************************/ /***********************************************************************/
/* JSONColumns: construct the result blocks containing the description */ /* JSONColumns: construct the result blocks containing the description */
...@@ -76,26 +76,13 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -76,26 +76,13 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT}; FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0}; static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
char *p, colname[65], fmt[129]; int i, n = 0;
int i, j, lvl, n = 0;
int ncol = sizeof(buftyp) / sizeof(int); int ncol = sizeof(buftyp) / sizeof(int);
bool mgo = (GetTypeID(topt->type) == TAB_MONGO); PJCL jcp;
PCSZ sep, level; JSONDISC *pjdc = NULL;
PVAL valp;
JCOL jcol;
PJCL jcp, fjcp = NULL, pjcp = NULL;
PJPR *jrp, jpp;
PJSON jsp;
PJVAL jvp;
PJOB row;
PJDEF tdp;
TDBJSN *tjnp = NULL;
PJTDB tjsp = NULL;
PQRYRES qrp; PQRYRES qrp;
PCOLRES crp; PCOLRES crp;
jcol.Name = jcol.Fmt = NULL;
if (info) { if (info) {
length[0] = 128; length[0] = 128;
length[7] = 256; length[7] = 256;
...@@ -107,20 +94,100 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -107,20 +94,100 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
return NULL; return NULL;
} // endif Multiple } // endif Multiple
/*********************************************************************/ pjdc = new(g) JSONDISC(g, (int*)length);
/* Open the input file. */
if (!(n = pjdc->GetColumns(g, db, dsn, topt)))
return NULL;
skipit:
if (trace(1))
htrc("JSONColumns: n=%d len=%d\n", n, length[0]);
/*********************************************************************/
/* Allocate the structures used to refer to the result set. */
/*********************************************************************/
qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
buftyp, fldtyp, length, false, false);
crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
crp->Name = "Nullable";
crp->Next->Name = "Jpath";
if (info || !qrp)
return qrp;
qrp->Nblin = n;
/*********************************************************************/
/* Now get the results into blocks. */
/*********************************************************************/ /*********************************************************************/
level = GetStringTableOption(g, topt, "Level", NULL); for (i = 0, jcp = pjdc->fjcp; jcp; i++, jcp = jcp->Next) {
if (jcp->Type == TYPE_UNKNOWN)
jcp->Type = TYPE_STRING; // Void column
crp = qrp->Colresp; // Column Name
crp->Kdata->SetValue(jcp->Name, i);
crp = crp->Next; // Data Type
crp->Kdata->SetValue(jcp->Type, i);
crp = crp->Next; // Type Name
crp->Kdata->SetValue(GetTypeName(jcp->Type), i);
crp = crp->Next; // Precision
crp->Kdata->SetValue(jcp->Len, i);
crp = crp->Next; // Length
crp->Kdata->SetValue(jcp->Len, i);
crp = crp->Next; // Scale (precision)
crp->Kdata->SetValue(jcp->Scale, i);
crp = crp->Next; // Nullable
crp->Kdata->SetValue(jcp->Cbn ? 1 : 0, i);
crp = crp->Next; // Field format
if (crp->Kdata)
crp->Kdata->SetValue(jcp->Fmt, i);
} // endfor i
/*********************************************************************/
/* Return the result pointer. */
/*********************************************************************/
return qrp;
} // end of JSONColumns
/* -------------------------- Class JSONDISC ------------------------- */
/***********************************************************************/
/* Class used to get the columns of a JSON table. */
/***********************************************************************/
JSONDISC::JSONDISC(PGLOBAL g, int *lg)
{
length = lg;
jcp = fjcp = pjcp = NULL;
tjnp = NULL;
jpp = NULL;
tjsp = NULL;
jsp = NULL;
row = NULL;
sep = NULL;
i = n = bf = ncol = lvl = 0;
all = false;
} // end of JSONDISC constructor
int JSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
{
bool mgo = (GetTypeID(topt->type) == TAB_MONGO);
PCSZ level = GetStringTableOption(g, topt, "Level", NULL);
if (level) { if (level) {
lvl = atoi(level); lvl = atoi(level);
lvl = (lvl > 16) ? 16 : lvl; lvl = (lvl > 16) ? 16 : lvl;
} else } else
lvl = 0; lvl = 0;
sep = GetStringTableOption(g, topt, "Separator", "."); sep = GetStringTableOption(g, topt, "Separator", ".");
tdp = new(g) JSONDEF; /*********************************************************************/
/* Open the input file. */
/*********************************************************************/
tdp = new(g) JSONDEF;
#if defined(ZIP_SUPPORT) #if defined(ZIP_SUPPORT)
tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL); tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false); tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
...@@ -130,21 +197,21 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -130,21 +197,21 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
if (!(tdp->Database = SetPath(g, db))) if (!(tdp->Database = SetPath(g, db)))
return NULL; return NULL;
tdp->Objname = GetStringTableOption(g, topt, "Object", NULL); tdp->Objname = GetStringTableOption(g, topt, "Object", NULL);
tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0; tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0;
tdp->Pretty = GetIntegerTableOption(g, topt, "Pretty", 2); tdp->Pretty = GetIntegerTableOption(g, topt, "Pretty", 2);
tdp->Xcol = GetStringTableOption(g, topt, "Expand", NULL); tdp->Xcol = GetStringTableOption(g, topt, "Expand", NULL);
tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false); tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false);
tdp->Uri = (dsn && *dsn ? dsn : NULL); tdp->Uri = (dsn && *dsn ? dsn : NULL);
if (!tdp->Fn && !tdp->Uri) { if (!tdp->Fn && !tdp->Uri) {
strcpy(g->Message, MSG(MISSING_FNAME)); strcpy(g->Message, MSG(MISSING_FNAME));
return NULL; return 0;
} // endif Fn } // endif Fn
if (trace(1)) if (trace(1))
htrc("File %s objname=%s pretty=%d lvl=%d\n", htrc("File %s objname=%s pretty=%d lvl=%d\n",
tdp->Fn, tdp->Objname, tdp->Pretty, lvl); tdp->Fn, tdp->Objname, tdp->Pretty, lvl);
if (tdp->Uri) { if (tdp->Uri) {
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT) #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
...@@ -160,34 +227,34 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -160,34 +227,34 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
tdp->Pretty = 0; tdp->Pretty = 0;
#else // !MONGO_SUPPORT #else // !MONGO_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO"); sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "MONGO");
return NULL; return 0;
#endif // !MONGO_SUPPORT #endif // !MONGO_SUPPORT
} // endif Uri } // endif Uri
if (tdp->Pretty == 2) { if (tdp->Pretty == 2) {
if (tdp->Zipped) { if (tdp->Zipped) {
#if defined(ZIP_SUPPORT) #if defined(ZIP_SUPPORT)
tjsp = new(g) TDBJSON(tdp, new(g) UNZFAM(tdp)); tjsp = new(g) TDBJSON(tdp, new(g) UNZFAM(tdp));
#else // !ZIP_SUPPORT #else // !ZIP_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
return NULL; return 0;
#endif // !ZIP_SUPPORT #endif // !ZIP_SUPPORT
} else } else
tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp)); tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp));
if (tjsp->MakeDocument(g)) if (tjsp->MakeDocument(g))
return NULL; return NULL;
jsp = (tjsp->GetDoc()) ? tjsp->GetDoc()->GetValue(0) : NULL; jsp = (tjsp->GetDoc()) ? tjsp->GetDoc()->GetValue(0) : NULL;
} else { } else {
if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0))) if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))
if (!mgo) { if (!mgo) {
sprintf(g->Message, "LRECL must be specified for pretty=%d", tdp->Pretty); sprintf(g->Message, "LRECL must be specified for pretty=%d", tdp->Pretty);
return NULL; return NULL;
} else } else
tdp->Lrecl = 8192; // Should be enough tdp->Lrecl = 8192; // Should be enough
tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF); tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF);
if (tdp->Zipped) { if (tdp->Zipped) {
#if defined(ZIP_SUPPORT) #if defined(ZIP_SUPPORT)
...@@ -196,7 +263,7 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -196,7 +263,7 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
return NULL; return NULL;
#endif // !ZIP_SUPPORT #endif // !ZIP_SUPPORT
} else if (tdp->Uri) { } else if (tdp->Uri) {
if (tdp->Driver && toupper(*tdp->Driver) == 'C') { if (tdp->Driver && toupper(*tdp->Driver) == 'C') {
#if defined(CMGO_SUPPORT) #if defined(CMGO_SUPPORT)
tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp)); tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp));
...@@ -204,14 +271,14 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -204,14 +271,14 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
sprintf(g->Message, "Mongo %s Driver not available", "C"); sprintf(g->Message, "Mongo %s Driver not available", "C");
return NULL; return NULL;
#endif #endif
} else if (tdp->Driver && toupper(*tdp->Driver) == 'J') { } else if (tdp->Driver && toupper(*tdp->Driver) == 'J') {
#if defined(JAVA_SUPPORT) #if defined(JAVA_SUPPORT)
tjnp = new(g) TDBJSN(tdp, new(g) JMGFAM(tdp)); tjnp = new(g) TDBJSN(tdp, new(g) JMGFAM(tdp));
#else #else
sprintf(g->Message, "Mongo %s Driver not available", "Java"); sprintf(g->Message, "Mongo %s Driver not available", "Java");
return NULL; return NULL;
#endif #endif
} else { // Driver not specified } else { // Driver not specified
#if defined(CMGO_SUPPORT) #if defined(CMGO_SUPPORT)
tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp)); tjnp = new(g) TDBJSN(tdp, new(g) CMGFAM(tdp));
#elif defined(JAVA_SUPPORT) #elif defined(JAVA_SUPPORT)
...@@ -222,10 +289,10 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -222,10 +289,10 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
#endif #endif
} // endif Driver } // endif Driver
} else } else
tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp)); tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp));
tjnp->SetMode(MODE_READ); tjnp->SetMode(MODE_READ);
// Allocate the parse work memory // Allocate the parse work memory
PGLOBAL G = (PGLOBAL)PlugSubAlloc(g, NULL, sizeof(GLOBAL)); PGLOBAL G = (PGLOBAL)PlugSubAlloc(g, NULL, sizeof(GLOBAL));
...@@ -237,248 +304,227 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info) ...@@ -237,248 +304,227 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
tjnp->SetG(G); tjnp->SetG(G);
if (tjnp->OpenDB(g)) if (tjnp->OpenDB(g))
return NULL; return NULL;
switch (tjnp->ReadDB(g)) { switch (tjnp->ReadDB(g)) {
case RC_EF: case RC_EF:
strcpy(g->Message, "Void json table"); strcpy(g->Message, "Void json table");
case RC_FX: case RC_FX:
goto err; goto err;
default: default:
jsp = tjnp->GetRow(); jsp = tjnp->GetRow();
} // endswitch ReadDB } // endswitch ReadDB
} // endif pretty } // endif pretty
if (!(row = (jsp) ? jsp->GetObject() : NULL)) { if (!(row = (jsp) ? jsp->GetObject() : NULL)) {
strcpy(g->Message, "Can only retrieve columns from object rows"); strcpy(g->Message, "Can only retrieve columns from object rows");
goto err; goto err;
} // endif row } // endif row
jcol.Next = NULL; all = GetBooleanTableOption(g, topt, "Fullarray", false);
jcol.Found = true; jcol.Name = jcol.Fmt = NULL;
colname[64] = 0; jcol.Next = NULL;
fmt[128] = 0; jcol.Found = true;
colname[0] = 0;
if (!tdp->Uri) { if (!tdp->Uri) {
*fmt = '$'; fmt[0] = '$';
fmt[1] = '.'; fmt[1] = '.';
p = fmt + 2; bf = 2;
} else } // endif Uri
p = fmt;
jrp = (PJPR*)PlugSubAlloc(g, NULL, sizeof(PJPR) * MY_MAX(lvl, 0));
/*********************************************************************/
/* Analyse the JSON tree and define columns. */
/*********************************************************************/
for (i = 1; ; i++) {
for (jpp = row->GetFirst(); jpp; jpp = jpp->GetNext()) {
for (j = 0; j < lvl; j++)
jrp[j] = NULL;
more:
strncpy(colname, jpp->GetKey(), 64);
*p = 0;
j = 0;
jvp = jpp->GetVal();
retry:
if ((valp = jvp ? jvp->GetValue() : NULL)) {
jcol.Type = valp->GetType();
jcol.Len = valp->GetValLen();
jcol.Scale = valp->GetValPrec();
jcol.Cbn = valp->IsNull();
} else if (!jvp || jvp->IsNull()) {
jcol.Type = TYPE_UNKNOWN;
jcol.Len = jcol.Scale = 0;
jcol.Cbn = true;
} else if (j < lvl) {
if (!*p)
strcat(fmt, colname);
jsp = jvp->GetJson();
switch (jsp->GetType()) {
case TYPE_JOB:
if (!jrp[j])
jrp[j] = jsp->GetFirst();
if (*jrp[j]->GetKey() != '$') {
strncat(strncat(fmt, sep, 128), jrp[j]->GetKey(), 128);
strncat(strncat(colname, "_", 64), jrp[j]->GetKey(), 64);
} // endif Key
jvp = jrp[j]->GetVal();
j++;
break;
case TYPE_JAR:
if (!tdp->Xcol || stricmp(tdp->Xcol, colname)) {
if (tdp->Uri)
strncat(strncat(fmt, sep, 128), "0", 128);
else
strncat(fmt, "[0]", 128);
} else
strncat(fmt, (tdp->Uri ? sep : "[]"), 128);
jvp = jsp->GetValue(0);
break;
default:
sprintf(g->Message, "Logical error after %s", fmt);
goto err;
} // endswitch jsp
goto retry;
} else if (lvl >= 0) {
jcol.Type = TYPE_STRING;
jcol.Len = 256;
jcol.Scale = 0;
jcol.Cbn = true;
} else
continue;
// Check whether this column was already found /*********************************************************************/
for (jcp = fjcp; jcp; jcp = jcp->Next) /* Analyse the JSON tree and define columns. */
if (!strcmp(colname, jcp->Name)) /*********************************************************************/
break; for (i = 1; ; i++) {
for (jpp = row->GetFirst(); jpp; jpp = jpp->GetNext()) {
if (jcp) { strncpy(colname, jpp->GetKey(), 64);
if (jcp->Type != jcol.Type) { fmt[bf] = 0;
if (jcp->Type == TYPE_UNKNOWN)
jcp->Type = jcol.Type; if (Find(g, jpp->GetVal(), MY_MIN(lvl, 0)))
else if (jcol.Type != TYPE_UNKNOWN) goto err;
jcp->Type = TYPE_STRING;
} // endfor jpp
// Missing column can be null
for (jcp = fjcp; jcp; jcp = jcp->Next) {
jcp->Cbn |= !jcp->Found;
jcp->Found = false;
} // endfor jcp
if (tdp->Pretty != 2) {
// Read next record
switch (tjnp->ReadDB(g)) {
case RC_EF:
jsp = NULL;
break;
case RC_FX:
goto err;
default:
jsp = tjnp->GetRow();
} // endswitch ReadDB
} // endif Type } else
jsp = tjsp->GetDoc()->GetValue(i);
if (*p && (!jcp->Fmt || strlen(jcp->Fmt) < strlen(fmt))) { if (!(row = (jsp) ? jsp->GetObject() : NULL))
jcp->Fmt = PlugDup(g, fmt); break;
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 (*p) {
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++; } // endfor i
} // endif jcp
pjcp = jcp; if (tdp->Pretty != 2)
tjnp->CloseDB(g);
for (j = lvl - 1; j >= 0; j--) return n;
if (jrp[j] && (jrp[j] = jrp[j]->GetNext()))
goto more;
} // endfor jpp err:
if (tdp->Pretty != 2)
tjnp->CloseDB(g);
// Missing column can be null return 0;
for (jcp = fjcp; jcp; jcp = jcp->Next) { } // end of GetColumns
jcp->Cbn |= !jcp->Found;
jcp->Found = false;
} // endfor jcp
if (tdp->Pretty != 2) { bool JSONDISC::Find(PGLOBAL g, PJVAL jvp, int j)
// Read next record {
switch (tjnp->ReadDB(g)) { char *p, *pc = colname + strlen(colname);
case RC_EF: int ars;
jsp = NULL; PJOB job;
break; PJAR jar;
case RC_FX:
goto err; if ((valp = jvp ? jvp->GetValue() : NULL)) {
default: jcol.Type = valp->GetType();
jsp = tjnp->GetRow(); jcol.Len = valp->GetValLen();
} // endswitch ReadDB jcol.Scale = valp->GetValPrec();
jcol.Cbn = valp->IsNull();
} else if (!jvp || jvp->IsNull()) {
jcol.Type = TYPE_UNKNOWN;
jcol.Len = jcol.Scale = 0;
jcol.Cbn = true;
} else if (j < lvl) {
if (!fmt[bf])
strcat(fmt, colname);
p = fmt + strlen(fmt);
jsp = jvp->GetJson();
switch (jsp->GetType()) {
case TYPE_JOB:
job = (PJOB)jsp;
for (PJPR jrp = job->GetFirst(); jrp; jrp = jrp->GetNext()) {
if (*jrp->GetKey() != '$') {
strncat(strncat(fmt, sep, 128), jrp->GetKey(), 128);
strncat(strncat(colname, "_", 64), jrp->GetKey(), 64);
} // endif Key
if (Find(g, jrp->GetVal(), j + 1))
return true;
*p = *pc = 0;
} // endfor jrp
return false;
case TYPE_JAR:
jar = (PJAR)jsp;
if (all || (tdp->Xcol && !stricmp(tdp->Xcol, colname)))
ars = jar->GetSize(false);
else
ars = MY_MIN(jar->GetSize(false), 1);
} else for (int k = 0; k < ars; k++) {
jsp = tjsp->GetDoc()->GetValue(i); if (!tdp->Xcol || stricmp(tdp->Xcol, colname)) {
sprintf(buf, "%d", k);
if (!(row = (jsp) ? jsp->GetObject() : NULL)) if (tdp->Uri)
break; strncat(strncat(fmt, sep, 128), buf, 128);
else
strncat(strncat(strncat(fmt, "[", 128), buf, 128), "]", 128);
} // endor i if (all)
strncat(strncat(colname, "_", 64), buf, 64);
if (tdp->Pretty != 2) } else
tjnp->CloseDB(g); strncat(fmt, (tdp->Uri ? sep : "[*]"), 128);
skipit: if (Find(g, jar->GetValue(k), j))
if (trace(1)) return true;
htrc("JSONColumns: n=%d len=%d\n", n, length[0]);
/*********************************************************************/ *p = *pc = 0;
/* Allocate the structures used to refer to the result set. */ } // endfor k
/*********************************************************************/
qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
buftyp, fldtyp, length, false, false);
crp = qrp->Colresp->Next->Next->Next->Next->Next->Next; return false;
crp->Name = "Nullable"; default:
crp->Next->Name = "Jpath"; sprintf(g->Message, "Logical error after %s", fmt);
return true;
} // endswitch Type
if (info || !qrp) } else if (lvl >= 0) {
return qrp; jcol.Type = TYPE_STRING;
jcol.Len = 256;
jcol.Scale = 0;
jcol.Cbn = true;
} else
return false;
qrp->Nblin = n; AddColumn(g);
return false;
} // end of Find
/*********************************************************************/ void JSONDISC::AddColumn(PGLOBAL g)
/* Now get the results into blocks. */ {
/*********************************************************************/ bool b = fmt[bf] != 0; // True if formatted
for (i = 0, jcp = fjcp; jcp; i++, jcp = jcp->Next) {
if (jcp->Type == TYPE_UNKNOWN)
jcp->Type = TYPE_STRING; // Void column
crp = qrp->Colresp; // Column Name // Check whether this column was already found
crp->Kdata->SetValue(jcp->Name, i); for (jcp = fjcp; jcp; jcp = jcp->Next)
crp = crp->Next; // Data Type if (!strcmp(colname, jcp->Name))
crp->Kdata->SetValue(jcp->Type, i); break;
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) if (jcp) {
crp->Kdata->SetValue(jcp->Fmt, i); if (jcp->Type != jcol.Type) {
if (jcp->Type == TYPE_UNKNOWN)
jcp->Type = jcol.Type;
else if (jcol.Type != TYPE_UNKNOWN)
jcp->Type = TYPE_STRING;
} // endif Type
if (b && (!jcp->Fmt || strlen(jcp->Fmt) < strlen(fmt))) {
jcp->Fmt = PlugDup(g, fmt);
length[7] = MY_MAX(length[7], strlen(fmt));
} // endif fmt
jcp->Len = MY_MAX(jcp->Len, jcol.Len);
jcp->Scale = MY_MAX(jcp->Scale, jcol.Scale);
jcp->Cbn |= jcol.Cbn;
jcp->Found = true;
} else if (jcol.Type != TYPE_UNKNOWN || tdp->Accept) {
// New column
jcp = (PJCL)PlugSubAlloc(g, NULL, sizeof(JCOL));
*jcp = jcol;
jcp->Cbn |= (i > 1);
jcp->Name = PlugDup(g, colname);
length[0] = MY_MAX(length[0], strlen(colname));
if (b) {
jcp->Fmt = PlugDup(g, fmt);
length[7] = MY_MAX(length[7], strlen(fmt));
} else
jcp->Fmt = NULL;
} // endfor i if (pjcp) {
jcp->Next = pjcp->Next;
pjcp->Next = jcp;
} else
fjcp = jcp;
/*********************************************************************/ n++;
/* Return the result pointer. */ } // endif jcp
/*********************************************************************/
return qrp;
err: pjcp = jcp;
if (tdp->Pretty != 2) } // end of AddColumn
tjnp->CloseDB(g);
return NULL;
} // end of JSONColumns
/* -------------------------- Class JSONDEF -------------------------- */ /* -------------------------- Class JSONDEF -------------------------- */
...@@ -513,6 +559,7 @@ bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR, int poff) ...@@ -513,6 +559,7 @@ bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR, int poff)
Limit = GetIntCatInfo("Limit", 10); Limit = GetIntCatInfo("Limit", 10);
Base = GetIntCatInfo("Base", 0) ? 1 : 0; Base = GetIntCatInfo("Base", 0) ? 1 : 0;
Sep = *GetStringCatInfo(g, "Separator", "."); Sep = *GetStringCatInfo(g, "Separator", ".");
Accept = GetBoolCatInfo("Accept", false);
if (Uri = GetStringCatInfo(g, "Connect", NULL)) { if (Uri = GetStringCatInfo(g, "Connect", NULL)) {
#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT) #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
...@@ -1471,6 +1518,9 @@ void JSONCOL::ReadColumn(PGLOBAL g) ...@@ -1471,6 +1518,9 @@ void JSONCOL::ReadColumn(PGLOBAL g)
if (!Tjp->SameRow || Xnod >= Tjp->SameRow) if (!Tjp->SameRow || Xnod >= Tjp->SameRow)
Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0)); Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0));
if (Xpd && Value->IsNull() && !((PJDEF)Tjp->To_Def)->Accept)
throw("Null expandable JSON value");
// Set null when applicable // Set null when applicable
if (!Nullable) if (!Nullable)
Value->SetNull(false); Value->SetNull(false);
...@@ -1546,11 +1596,16 @@ PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i) ...@@ -1546,11 +1596,16 @@ PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i)
/***********************************************************************/ /***********************************************************************/
PVAL JSONCOL::ExpandArray(PGLOBAL g, PJAR arp, int n) PVAL JSONCOL::ExpandArray(PGLOBAL g, PJAR arp, int n)
{ {
int ars; int ars = MY_MIN(Tjp->Limit, arp->size());
PJVAL jvp; PJVAL jvp;
JVALUE jval; JVALUE jval;
ars = MY_MIN(Tjp->Limit, arp->size()); if (!ars) {
Value->Reset();
Value->SetNull(true);
Tjp->NextSame = 0;
return Value;
} // endif ars
if (!(jvp = arp->GetValue((Nodes[n].Rx = Nodes[n].Nx)))) { if (!(jvp = arp->GetValue((Nodes[n].Rx = Nodes[n].Nx)))) {
strcpy(g->Message, "Logical error expanding array"); strcpy(g->Message, "Logical error expanding array");
......
...@@ -15,6 +15,7 @@ enum JMODE {MODE_OBJECT, MODE_ARRAY, MODE_VALUE}; ...@@ -15,6 +15,7 @@ enum JMODE {MODE_OBJECT, MODE_ARRAY, MODE_VALUE};
typedef class JSONDEF *PJDEF; typedef class JSONDEF *PJDEF;
typedef class TDBJSON *PJTDB; typedef class TDBJSON *PJTDB;
typedef class JSONCOL *PJCOL; typedef class JSONCOL *PJCOL;
class TDBJSN;
/***********************************************************************/ /***********************************************************************/
/* The JSON tree node. Can be an Object or an Array. */ /* The JSON tree node. Can be an Object or an Array. */
...@@ -29,6 +30,47 @@ typedef struct _jnode { ...@@ -29,6 +30,47 @@ typedef struct _jnode {
int Nx; // Next to read row number int Nx; // Next to read row number
} JNODE, *PJNODE; } JNODE, *PJNODE;
typedef struct _jncol {
struct _jncol *Next;
char *Name;
char *Fmt;
int Type;
int Len;
int Scale;
bool Cbn;
bool Found;
} JCOL, *PJCL;
/***********************************************************************/
/* Class used to get the columns of a mongo collection. */
/***********************************************************************/
class JSONDISC : public BLOCK {
public:
// Constructor
JSONDISC(PGLOBAL g, int *lg);
// Functions
int GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt);
bool Find(PGLOBAL g, PJVAL jvp, int j);
void AddColumn(PGLOBAL g);
// Members
JCOL jcol;
PJCL jcp, fjcp, pjcp;
PVAL valp;
PJDEF tdp;
TDBJSN *tjnp;
PJTDB tjsp;
PJPR jpp;
PJSON jsp;
PJOB row;
PCSZ sep;
char colname[65], fmt[129], buf[16];
int *length;
int i, n, bf, ncol, lvl;
bool all;
}; // end of JSONDISC
/***********************************************************************/ /***********************************************************************/
/* JSON table. */ /* JSON table. */
/***********************************************************************/ /***********************************************************************/
...@@ -36,13 +78,13 @@ class DllExport JSONDEF : public DOSDEF { /* Table description */ ...@@ -36,13 +78,13 @@ class DllExport JSONDEF : public DOSDEF { /* Table description */
friend class TDBJSON; friend class TDBJSON;
friend class TDBJSN; friend class TDBJSN;
friend class TDBJCL; friend class TDBJCL;
friend class JSONDISC;
#if defined(CMGO_SUPPORT) #if defined(CMGO_SUPPORT)
friend class CMGFAM; friend class CMGFAM;
#endif // CMGO_SUPPORT #endif // CMGO_SUPPORT
#if defined(JAVA_SUPPORT) #if defined(JAVA_SUPPORT)
friend class JMGFAM; friend class JMGFAM;
#endif // JAVA_SUPPORT #endif // JAVA_SUPPORT
friend PQRYRES JSONColumns(PGLOBAL, PCSZ, PCSZ, PTOS, bool);
public: public:
// Constructor // Constructor
JSONDEF(void); JSONDEF(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment